Updating RCCL based on NCCL 2.3.7
- Contains modifications to support AMD hardware
- Adds unit tests
[ROCm/rccl commit: 55a4b22ad7]
Этот коммит содержится в:
@@ -0,0 +1,63 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
|
||||
message("Going to build unit tests (Installed in /test/UnitTests)")
|
||||
|
||||
# OpenMP is used to drive GPUs (one per thread)
|
||||
find_package(OpenMP REQUIRED)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
|
||||
|
||||
# Download and unpack googletest at configure time
|
||||
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
|
||||
if(result)
|
||||
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} --build .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
|
||||
if(result)
|
||||
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
||||
endif()
|
||||
|
||||
# Add googletest directly to our build. This adds the following targets:
|
||||
# gtest, gtest_main, gmock and gmock_main
|
||||
add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src"
|
||||
"${CMAKE_BINARY_DIR}/googletest-build")
|
||||
|
||||
# Add googletest directly to our build. This defines the gtest and gtest_main
|
||||
# targets. add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
|
||||
# ${CMAKE_CURRENT_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL)
|
||||
|
||||
# Collect source files for tests
|
||||
set(TEST_SOURCES
|
||||
test_AllGather.cpp
|
||||
test_AllReduce.cpp
|
||||
test_Broadcast.cpp
|
||||
test_Reduce.cpp
|
||||
test_ReduceScatter.cpp
|
||||
test_GroupCalls.cpp
|
||||
)
|
||||
|
||||
add_executable(UnitTests ${TEST_SOURCES})
|
||||
target_include_directories(UnitTests PRIVATE /opt/rocm)
|
||||
target_link_libraries(UnitTests PRIVATE gtest_main PRIVATE rccl)
|
||||
install(TARGETS UnitTests RUNTIME DESTINATION test)
|
||||
|
||||
# HCC adds /opt/rocm/lib as RPATH, even though the install process is supposed to
|
||||
# remove RPATH. As a work-around, set the correct RPATH for the unit test executable
|
||||
# as a post-install step
|
||||
install(
|
||||
CODE
|
||||
"execute_process(COMMAND chrpath -r ${CMAKE_INSTALL_PREFIX}/lib:/opt/rocm/lib ${CMAKE_INSTALL_PREFIX}/test/UnitTests)"
|
||||
)
|
||||
else()
|
||||
message("Not building unit tests")
|
||||
endif()
|
||||
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 2.8.2)
|
||||
|
||||
project(googletest-download NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG master
|
||||
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
|
||||
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
@@ -0,0 +1,311 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#ifndef CORRECTNESSTEST_HPP
|
||||
#define CORRECTNESSTEST_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
#include "rccl.h"
|
||||
|
||||
#define HIP_CALL(x) ASSERT_EQ(x, hipSuccess)
|
||||
#define NCCL_CALL(x) ASSERT_EQ(x, ncclSuccess)
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
// Performs the various basic reduction operations
|
||||
template <typename T>
|
||||
T ReduceOp(ncclRedOp_t const op, T const A, T const B)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case ncclSum: return A + B;
|
||||
case ncclProd: return A * B;
|
||||
case ncclMax: return std::max(A, B);
|
||||
case ncclMin: return std::min(A, B);
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported reduction operator (%d)\n", op);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of bytes per element for each supported datatype
|
||||
static int DataTypeToBytes(ncclDataType_t const dataType)
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
case ncclInt8: return 1;
|
||||
case ncclUint8: return 1;
|
||||
case ncclInt32: return 4;
|
||||
case ncclUint32: return 4;
|
||||
case ncclInt64: return 8;
|
||||
case ncclUint64: return 8;
|
||||
case ncclFloat16: return 2;
|
||||
case ncclFloat32: return 4;
|
||||
case ncclFloat64: return 8;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported datatype (%d)\n", dataType);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Encapsulates all the memory used per devices for collectives, as well as reference results
|
||||
struct Dataset
|
||||
{
|
||||
int numDevices; // Number of devices participating
|
||||
size_t numElements; // Number of elements per array
|
||||
ncclDataType_t dataType; // Data type of each input/output pointer
|
||||
bool inPlace; // Whether or not output pointers are same as input pointers
|
||||
std::vector<void *> inputs; // Input pointers (1 per device)
|
||||
std::vector<void *> outputs; // Output pointers (1 per device)
|
||||
// May be identical to input pointers for in-place tests
|
||||
std::vector<void *> expected; // Expected output (1 per device)
|
||||
|
||||
size_t NumBytes() const
|
||||
{
|
||||
return numElements * DataTypeToBytes(dataType);
|
||||
}
|
||||
|
||||
void Initialize(int const numDevices_,
|
||||
size_t const numElements_,
|
||||
ncclDataType_t const dataType_,
|
||||
bool const inPlace_)
|
||||
{
|
||||
numDevices = numDevices_;
|
||||
numElements = numElements_;
|
||||
dataType = dataType_;
|
||||
inPlace = inPlace_;
|
||||
|
||||
inputs.resize(numDevices);
|
||||
outputs.resize(numDevices);
|
||||
expected.resize(numDevices);
|
||||
|
||||
// Allocate per-device memory
|
||||
size_t const numBytes = NumBytes();
|
||||
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
HIP_CALL(hipSetDevice(i));
|
||||
HIP_CALL(hipMalloc((void **)&inputs[i], numBytes));
|
||||
if (inPlace)
|
||||
outputs[i] = inputs[i];
|
||||
else
|
||||
HIP_CALL(hipMalloc((void **)&outputs[i], numBytes));
|
||||
|
||||
expected[i] = malloc(numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
~Dataset()
|
||||
{
|
||||
for (int i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
if (!inPlace) hipFree(outputs[i]);
|
||||
hipFree(inputs[i]);
|
||||
free(expected[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::tuple<ncclRedOp_t /* op */,
|
||||
ncclDataType_t /* dataType */,
|
||||
size_t /* numElements */,
|
||||
int /* numDevices */,
|
||||
bool /* inPlace */> TestTuple;
|
||||
|
||||
// Base class for each collective test
|
||||
// - Each test is instantiated with a different TestTuple
|
||||
class CorrectnessTest : public testing::TestWithParam<TestTuple>
|
||||
{
|
||||
protected:
|
||||
|
||||
// This code is called per test-tuple
|
||||
void SetUp() override
|
||||
{
|
||||
// Check for fine-grained env variable (otherwise will hang)
|
||||
if (!getenv("HSA_FORCE_FINE_GRAIN_PCIE"))
|
||||
{
|
||||
printf("Must set HSA_FORCE_FINE_GRAIN_PCIE=1 prior to execution\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Make the test tuple parameters accessible
|
||||
std::tie(op, dataType, numElements, numDevices, inPlace) = GetParam();
|
||||
|
||||
// Collect the number of available GPUs
|
||||
HIP_CALL(hipGetDeviceCount(&numDevicesAvailable));
|
||||
|
||||
// Only proceed with testing if there are enough GPUs
|
||||
if (numDevices > numDevicesAvailable)
|
||||
{
|
||||
fprintf(stdout, "Skipping test requring %d devices (only %d available)\n",
|
||||
numDevices, numDevicesAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize communicators
|
||||
comms.resize(numDevices);
|
||||
NCCL_CALL(ncclCommInitAll(comms.data(), numDevices, NULL));
|
||||
|
||||
// Create streams
|
||||
streams.resize(numDevices);
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
HIP_CALL(hipStreamCreate(&streams[i]));
|
||||
}
|
||||
|
||||
// Clean up per TestTuple
|
||||
void TearDown() override
|
||||
{
|
||||
// Release communicators and streams
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
NCCL_CALL(ncclCommDestroy(comms[i]));
|
||||
HIP_CALL(hipStreamDestroy(streams[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void FillDatasetWithPattern(Dataset& dataset)
|
||||
{
|
||||
int8_t* arrayI1 = (int8_t *)malloc(dataset.NumBytes());
|
||||
uint8_t* arrayU1 = (uint8_t *)arrayI1;
|
||||
int32_t* arrayI4 = (int32_t *)arrayI1;
|
||||
uint32_t* arrayU4 = (uint32_t *)arrayI1;
|
||||
int64_t* arrayI8 = (int64_t *)arrayI1;
|
||||
uint64_t* arrayU8 = (uint64_t *)arrayI1;
|
||||
float* arrayF4 = (float *)arrayI1;
|
||||
double* arrayF8 = (double *)arrayI1;
|
||||
|
||||
// NOTE: Currently half-precision float tests are unsupported due to half being supported
|
||||
// on GPU only and not host
|
||||
|
||||
// Fills input data[i][j] with (i + j) % 6
|
||||
// - Keeping range small to reduce likelihood of overflow
|
||||
// - Sticking with floating points values that are perfectly representable
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
{
|
||||
for (int j = 0; j < dataset.numElements; j++)
|
||||
{
|
||||
int valueI = (i + j) % 6;
|
||||
float valueF = (float)valueI;
|
||||
|
||||
switch (dataset.dataType)
|
||||
{
|
||||
case ncclInt8: arrayI1[j] = valueI; break;
|
||||
case ncclUint8: arrayU1[j] = valueI; break;
|
||||
case ncclInt32: arrayI4[j] = valueI; break;
|
||||
case ncclUint32: arrayU4[j] = valueI; break;
|
||||
case ncclInt64: arrayI8[j] = valueI; break;
|
||||
case ncclUint64: arrayU8[j] = valueI; break;
|
||||
case ncclFloat32: arrayF4[j] = valueF; break;
|
||||
case ncclFloat64: arrayF8[j] = valueF; break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported datatype\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
HIP_CALL(hipSetDevice(i));
|
||||
HIP_CALL(hipMemcpy(dataset.inputs[i], arrayI1, dataset.NumBytes(), hipMemcpyHostToDevice));
|
||||
|
||||
// Fills output data[i][j] with 0 (if not inplace)
|
||||
if (!dataset.inPlace)
|
||||
HIP_CALL(hipMemset(dataset.outputs[i], 0, dataset.NumBytes()));
|
||||
}
|
||||
|
||||
free(arrayI1);
|
||||
}
|
||||
|
||||
void ValidateResults(Dataset const& dataset) const
|
||||
{
|
||||
int8_t* outputI1 = (int8_t *)malloc(dataset.NumBytes());
|
||||
uint8_t* outputU1 = (uint8_t *)outputI1;
|
||||
int32_t* outputI4 = (int32_t *)outputI1;
|
||||
uint32_t* outputU4 = (uint32_t *)outputI1;
|
||||
int64_t* outputI8 = (int64_t *)outputI1;
|
||||
uint64_t* outputU8 = (uint64_t *)outputI1;
|
||||
float* outputF4 = (float *)outputI1;
|
||||
double* outputF8 = (double *)outputI1;
|
||||
|
||||
bool isMatch = true;
|
||||
|
||||
// Loop over each device's output and compare it to the expected output
|
||||
// (Each collective operation computes its own expected results)
|
||||
for (int i = 0; i < dataset.numDevices && isMatch; i++)
|
||||
{
|
||||
HIP_CALL(hipMemcpy(outputI1, dataset.outputs[i], dataset.NumBytes(), hipMemcpyDeviceToHost));
|
||||
|
||||
int8_t* expectedI1 = (int8_t *)dataset.expected[i];
|
||||
uint8_t* expectedU1 = (uint8_t *)expectedI1;
|
||||
int32_t* expectedI4 = (int32_t *)expectedI1;
|
||||
uint32_t* expectedU4 = (uint32_t *)expectedI1;
|
||||
int64_t* expectedI8 = (int64_t *)expectedI1;
|
||||
uint64_t* expectedU8 = (uint64_t *)expectedI1;
|
||||
float* expectedF4 = (float *)expectedI1;
|
||||
double* expectedF8 = (double *)expectedI1;
|
||||
|
||||
for (int j = 0; j < dataset.numElements && isMatch; j++)
|
||||
{
|
||||
switch (dataset.dataType)
|
||||
{
|
||||
case ncclInt8: isMatch &= (outputI1[j] == expectedI1[j]); break;
|
||||
case ncclUint8: isMatch &= (outputU1[j] == expectedU1[j]); break;
|
||||
case ncclInt32: isMatch &= (outputI4[j] == expectedI4[j]); break;
|
||||
case ncclUint32: isMatch &= (outputU4[j] == expectedU4[j]); break;
|
||||
case ncclInt64: isMatch &= (outputI8[j] == expectedI8[j]); break;
|
||||
case ncclUint64: isMatch &= (outputU8[j] == expectedU8[j]); break;
|
||||
case ncclFloat32: isMatch &= (outputF4[j] == expectedF4[j]); break;
|
||||
case ncclFloat64: isMatch &= (outputF8[j] == expectedF8[j]); break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported datatype\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!isMatch)
|
||||
{
|
||||
switch (dataset.dataType)
|
||||
{
|
||||
case ncclInt8:
|
||||
printf("Expected %d. Output %d on device %d[%d]\n", outputI1[j], expectedI1[j], i, j); break;
|
||||
case ncclUint8:
|
||||
printf("Expected %u. Output %u on device %d[%d]\n", outputU1[j], expectedU1[j], i, j); break;
|
||||
case ncclInt32:
|
||||
printf("Expected %d. Output %d on device %d[%d]\n", outputI4[j], expectedI4[j], i, j); break;
|
||||
case ncclUint32:
|
||||
printf("Expected %u. Output %u on device %d[%d]\n", outputU4[j], expectedU4[j], i, j); break;
|
||||
case ncclInt64:
|
||||
printf("Expected %ld. Output %ld on device %d[%d]\n", outputI8[j], expectedI8[j], i, j); break;
|
||||
case ncclUint64:
|
||||
printf("Expected %lu. Output %lu on device %d[%d]\n", outputU8[j], expectedU8[j], i, j); break;
|
||||
case ncclFloat32:
|
||||
printf("Expected %f. Output %f on device %d[%d]\n", outputF4[j], expectedF4[j], i, j); break;
|
||||
case ncclFloat64:
|
||||
printf("Expected %lf. Output %lf on device %d[%d]\n", outputF8[j], expectedF8[j], i, j); break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported datatype\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(isMatch, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Passed in parameters from TestTuple
|
||||
ncclRedOp_t op;
|
||||
ncclDataType_t dataType;
|
||||
size_t numElements;
|
||||
int numDevices;
|
||||
bool inPlace;
|
||||
|
||||
int numDevicesAvailable;
|
||||
std::vector<ncclComm_t> comms;
|
||||
std::vector<hipStream_t> streams;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#include "test_AllGather.hpp"
|
||||
#include <omp.h>
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
TEST_P(AllGatherCorrectnessTest, Correctness)
|
||||
{
|
||||
if (numDevices > numDevicesAvailable) return;
|
||||
if (numElements % numDevices != 0) return;
|
||||
|
||||
// Prepare input / output / expected results
|
||||
Dataset dataset;
|
||||
dataset.Initialize(numDevices, numElements, dataType, inPlace);
|
||||
FillDatasetWithPattern(dataset);
|
||||
ComputeExpectedResults(dataset);
|
||||
|
||||
size_t const byteCount = dataset.NumBytes() / dataset.numDevices;
|
||||
size_t const sendCount = dataset.numElements / dataset.numDevices;
|
||||
|
||||
// Launch the reduction (1 thread per GPU)
|
||||
#pragma omp parallel for num_threads(numDevices)
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
ncclAllGather((int8_t *)dataset.inputs[i] + (i * byteCount),
|
||||
dataset.outputs[i], sendCount,
|
||||
dataType, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// Wait for reduction to complete
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
hipStreamSynchronize(streams[i]);
|
||||
|
||||
// Check results
|
||||
ValidateResults(dataset);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AllGatherCorrectnessSweep,
|
||||
AllGatherCorrectnessTest,
|
||||
testing::Combine(
|
||||
// Reduction operator (not used)
|
||||
testing::Values(ncclSum),
|
||||
// Data types
|
||||
testing::Values(ncclInt8,
|
||||
ncclUint8,
|
||||
ncclInt32,
|
||||
ncclUint32,
|
||||
ncclInt64,
|
||||
ncclUint64,
|
||||
//ncclFloat16,
|
||||
ncclFloat32,
|
||||
ncclFloat64),
|
||||
// Number of elements
|
||||
testing::Values(3072, 3145728),
|
||||
// Number of devices
|
||||
testing::Values(2,3,4),
|
||||
// In-place or not
|
||||
testing::Values(false, true)));
|
||||
} // namespace
|
||||
@@ -0,0 +1,32 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#ifndef TEST_ALLGATHER_HPP
|
||||
#define TEST_ALLGATHER_HPP
|
||||
|
||||
#include "CorrectnessTest.hpp"
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
class AllGatherCorrectnessTest : public CorrectnessTest
|
||||
{
|
||||
public:
|
||||
static void ComputeExpectedResults(Dataset& dataset)
|
||||
{
|
||||
size_t const byteCount = dataset.NumBytes() / dataset.numDevices;
|
||||
|
||||
int8_t* result = (int8_t *)malloc(dataset.NumBytes());
|
||||
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
HIP_CALL(hipMemcpy(result + i * byteCount, (int8_t *)dataset.inputs[i] + (i * byteCount),
|
||||
byteCount, hipMemcpyDeviceToHost));
|
||||
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
memcpy(dataset.expected[i], result, dataset.NumBytes());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include "test_AllReduce.hpp"
|
||||
#include <omp.h>
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
TEST_P(AllReduceCorrectnessTest, Correctness)
|
||||
{
|
||||
if (numDevices > numDevicesAvailable) return;
|
||||
|
||||
// Prepare input / output / expected results
|
||||
Dataset dataset;
|
||||
dataset.Initialize(numDevices, numElements, dataType, inPlace);
|
||||
FillDatasetWithPattern(dataset);
|
||||
ComputeExpectedResults(dataset, op);
|
||||
|
||||
// Launch the reduction (1 thread per GPU)
|
||||
#pragma omp parallel for num_threads(numDevices)
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
ncclAllReduce(dataset.inputs[i], dataset.outputs[i],
|
||||
numElements, dataType, op, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// Wait for reduction to complete
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
hipStreamSynchronize(streams[i]);
|
||||
|
||||
// Check results
|
||||
ValidateResults(dataset);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AllReduceCorrectnessSweep,
|
||||
AllReduceCorrectnessTest,
|
||||
testing::Combine(
|
||||
// Reduction operator
|
||||
testing::Values(ncclSum, ncclProd, ncclMax, ncclMin),
|
||||
// Data types
|
||||
testing::Values(ncclInt8,
|
||||
ncclUint8,
|
||||
ncclInt32,
|
||||
ncclUint32,
|
||||
ncclInt64,
|
||||
ncclUint64,
|
||||
//ncclFloat16,
|
||||
ncclFloat32,
|
||||
ncclFloat64),
|
||||
// Number of elements
|
||||
testing::Values(1024, 1048576),
|
||||
// Number of devices
|
||||
testing::Values(2,3,4),
|
||||
// In-place or not
|
||||
testing::Values(false, true)));
|
||||
} // namespace
|
||||
@@ -0,0 +1,76 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#ifndef TEST_ALLREDUCE_HPP
|
||||
#define TEST_ALLREDUCE_HPP
|
||||
|
||||
#include "CorrectnessTest.hpp"
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
class AllReduceCorrectnessTest : public CorrectnessTest
|
||||
{
|
||||
public:
|
||||
static void ComputeExpectedResults(Dataset& dataset, ncclRedOp_t const op)
|
||||
{
|
||||
// Copy all inputs to expected arrays temporarily to perform reduction on host
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
HIP_CALL(hipMemcpy(dataset.expected[i], dataset.inputs[i],
|
||||
dataset.NumBytes(), hipMemcpyDeviceToHost));
|
||||
|
||||
// Allocate temporary host array to accumulate results
|
||||
int8_t* resultI1 = (int8_t *)malloc(dataset.NumBytes());
|
||||
uint8_t* resultU1 = (uint8_t *)resultI1;
|
||||
int32_t* resultI4 = (int32_t *)resultI1;
|
||||
uint32_t* resultU4 = (uint32_t *)resultI1;
|
||||
int64_t* resultI8 = (int64_t *)resultI1;
|
||||
uint64_t* resultU8 = (uint64_t *)resultI1;
|
||||
float* resultF4 = (float *)resultI1;
|
||||
double* resultF8 = (double *)resultI1;
|
||||
|
||||
// Initialize the result with the first device's array
|
||||
memcpy(resultI1, dataset.expected[0], dataset.NumBytes());
|
||||
|
||||
// Perform reduction on the other device arrays
|
||||
for (int i = 1; i < dataset.numDevices; i++)
|
||||
{
|
||||
int8_t* arrayI1 = (int8_t *)dataset.expected[i];
|
||||
uint8_t* arrayU1 = (uint8_t *)arrayI1;
|
||||
int32_t* arrayI4 = (int32_t *)arrayI1;
|
||||
uint32_t* arrayU4 = (uint32_t *)arrayI1;
|
||||
int64_t* arrayI8 = (int64_t *)arrayI1;
|
||||
uint64_t* arrayU8 = (uint64_t *)arrayI1;
|
||||
float* arrayF4 = (float *)arrayI1;
|
||||
double* arrayF8 = (double *)arrayI1;
|
||||
|
||||
for (int j = 0; j < dataset.numElements; j++)
|
||||
{
|
||||
switch (dataset.dataType)
|
||||
{
|
||||
case ncclInt8: resultI1[j] = ReduceOp(op, resultI1[j], arrayI1[j]); break;
|
||||
case ncclUint8: resultU1[j] = ReduceOp(op, resultU1[j], arrayU1[j]); break;
|
||||
case ncclInt32: resultI4[j] = ReduceOp(op, resultI4[j], arrayI4[j]); break;
|
||||
case ncclUint32: resultU4[j] = ReduceOp(op, resultU4[j], arrayU4[j]); break;
|
||||
case ncclInt64: resultI8[j] = ReduceOp(op, resultI8[j], arrayI8[j]); break;
|
||||
case ncclUint64: resultU8[j] = ReduceOp(op, resultU8[j], arrayU8[j]); break;
|
||||
case ncclFloat32: resultF4[j] = ReduceOp(op, resultF4[j], arrayF4[j]); break;
|
||||
case ncclFloat64: resultF8[j] = ReduceOp(op, resultF8[j], arrayF8[j]); break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported datatype\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy results into expected arrays
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
memcpy(dataset.expected[i], resultI1, dataset.NumBytes());
|
||||
|
||||
free(resultI1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include "test_Broadcast.hpp"
|
||||
#include <omp.h>
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
TEST_P(BroadcastCorrectnessTest, Correctness)
|
||||
{
|
||||
if (numDevices > numDevicesAvailable) return;
|
||||
|
||||
// Allocate data
|
||||
Dataset dataset;
|
||||
dataset.Initialize(numDevices, numElements, dataType, inPlace);
|
||||
|
||||
// Test each possible root
|
||||
for (int root = 0; root < numDevices; root++)
|
||||
{
|
||||
// Prepare input / output / expected results
|
||||
FillDatasetWithPattern(dataset);
|
||||
ComputeExpectedResults(dataset, root);
|
||||
|
||||
// Launch the reduction (1 thread per GPU)
|
||||
#pragma omp parallel for num_threads(numDevices)
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
ncclBroadcast(dataset.inputs[i],
|
||||
dataset.outputs[i],
|
||||
numElements, dataType,
|
||||
root, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// Wait for reduction to complete
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
hipStreamSynchronize(streams[i]);
|
||||
|
||||
// Check results
|
||||
ValidateResults(dataset);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(BroadcastCorrectnessSweep,
|
||||
BroadcastCorrectnessTest,
|
||||
testing::Combine(
|
||||
// Reduction operator is not used
|
||||
testing::Values(ncclSum),
|
||||
// Data types
|
||||
testing::Values(ncclInt8,
|
||||
ncclUint8,
|
||||
ncclInt32,
|
||||
ncclUint32,
|
||||
ncclInt64,
|
||||
ncclUint64,
|
||||
//ncclFloat16,
|
||||
ncclFloat32,
|
||||
ncclFloat64),
|
||||
// Number of elements
|
||||
testing::Values(1024, 1048576),
|
||||
// Number of devices
|
||||
testing::Values(2,3,4),
|
||||
// In-place or not
|
||||
testing::Values(false, true)));
|
||||
} // namespace
|
||||
@@ -0,0 +1,26 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#ifndef TEST_BROADCAST_HPP
|
||||
#define TEST_BROADCAST_HPP
|
||||
|
||||
#include "CorrectnessTest.hpp"
|
||||
#include <omp.h>
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
class BroadcastCorrectnessTest : public CorrectnessTest
|
||||
{
|
||||
public:
|
||||
static void ComputeExpectedResults(Dataset& dataset, int const root)
|
||||
{
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
HIP_CALL(hipMemcpy(dataset.expected[i], dataset.inputs[root],
|
||||
dataset.NumBytes(), hipMemcpyDeviceToHost));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#include "test_GroupCalls.hpp"
|
||||
|
||||
#include "test_AllGather.hpp"
|
||||
#include "test_AllReduce.hpp"
|
||||
#include "test_Broadcast.hpp"
|
||||
#include "test_Reduce.hpp"
|
||||
#include "test_ReduceScatter.hpp"
|
||||
|
||||
#include <omp.h>
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
TEST_P(GroupCallsCorrectnessTest, Correctness)
|
||||
{
|
||||
if (numDevices > numDevicesAvailable) return;
|
||||
|
||||
// Create multiple datasets for group operation
|
||||
std::vector<Dataset> datasets(5);
|
||||
for (int i = 0; i < datasets.size(); i++)
|
||||
{
|
||||
datasets[i].Initialize(numDevices, numElements, dataType, inPlace);
|
||||
FillDatasetWithPattern(datasets[i]);
|
||||
}
|
||||
|
||||
// Compute expected results for each dataset in group
|
||||
int const root = 0;
|
||||
AllGatherCorrectnessTest::ComputeExpectedResults(datasets[0]);
|
||||
AllReduceCorrectnessTest::ComputeExpectedResults(datasets[1], op);
|
||||
BroadcastCorrectnessTest::ComputeExpectedResults(datasets[2], root);
|
||||
ReduceCorrectnessTest::ComputeExpectedResults(datasets[3], op, root);
|
||||
ReduceScatterCorrectnessTest::ComputeExpectedResults(datasets[4], op);
|
||||
|
||||
// Start a group call
|
||||
ncclGroupStart();
|
||||
|
||||
// AllGather
|
||||
size_t const byteCount = datasets[0].NumBytes() / numDevices;
|
||||
size_t const elemCount = numElements / numDevices;
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
HIP_CALL(hipSetDevice(i));
|
||||
ncclAllGather((int8_t *)datasets[0].inputs[i] + (i * byteCount),
|
||||
datasets[0].outputs[i], elemCount,
|
||||
dataType, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// AllReduce
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
HIP_CALL(hipSetDevice(i));
|
||||
ncclAllReduce(datasets[1].inputs[i], datasets[1].outputs[i],
|
||||
numElements, dataType, op, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// Broadcast
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
HIP_CALL(hipSetDevice(i));
|
||||
ncclBroadcast(datasets[2].inputs[i],
|
||||
datasets[2].outputs[i],
|
||||
numElements, dataType,
|
||||
root, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// Reduce
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
HIP_CALL(hipSetDevice(i));
|
||||
ncclReduce(datasets[3].inputs[i],
|
||||
datasets[3].outputs[i],
|
||||
numElements, dataType, op,
|
||||
root, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// ReduceScatter
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
ncclReduceScatter(datasets[4].inputs[i],
|
||||
(int8_t *)datasets[4].outputs[i] + (i * byteCount),
|
||||
elemCount, dataType, op,
|
||||
comms[i], streams[i]);
|
||||
HIP_CALL(hipSetDevice(i));
|
||||
}
|
||||
|
||||
// Signal end of group call
|
||||
ncclGroupEnd();
|
||||
|
||||
// Wait for reduction to complete
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
hipStreamSynchronize(streams[i]);
|
||||
|
||||
// Check results for each collective in the group
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
ValidateResults(datasets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(GroupCallsCorrectnessSweep,
|
||||
GroupCallsCorrectnessTest,
|
||||
testing::Combine(
|
||||
// Reduction operator (not used)
|
||||
testing::Values(ncclSum),
|
||||
// Data types
|
||||
testing::Values(ncclInt8,
|
||||
ncclUint8,
|
||||
ncclInt32,
|
||||
ncclUint32,
|
||||
ncclInt64,
|
||||
ncclUint64,
|
||||
//ncclFloat16,
|
||||
ncclFloat32,
|
||||
ncclFloat64),
|
||||
// Number of elements
|
||||
testing::Values(3072, 3145728),
|
||||
// Number of devices
|
||||
testing::Values(2,3,4),
|
||||
// In-place or not
|
||||
testing::Values(false, true)));
|
||||
} // namespace
|
||||
@@ -0,0 +1,17 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#ifndef TEST_GROUPCALLS_HPP
|
||||
#define TEST_GROUPCALLS_HPP
|
||||
|
||||
#include "CorrectnessTest.hpp"
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
class GroupCallsCorrectnessTest : public CorrectnessTest {};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include "test_Reduce.hpp"
|
||||
#include <omp.h>
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
TEST_P(ReduceCorrectnessTest, Correctness)
|
||||
{
|
||||
if (numDevices > numDevicesAvailable) return;
|
||||
|
||||
// Allocate data
|
||||
Dataset dataset;
|
||||
dataset.Initialize(numDevices, numElements, dataType, inPlace);
|
||||
|
||||
// Test each possible root
|
||||
for (int root = 0; root < numDevices; root++)
|
||||
{
|
||||
// Prepare input / output / expected results
|
||||
FillDatasetWithPattern(dataset);
|
||||
ComputeExpectedResults(dataset, op, root);
|
||||
|
||||
// Launch the reduction (1 thread per GPU)
|
||||
#pragma omp parallel for num_threads(numDevices)
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
ncclReduce(dataset.inputs[i],
|
||||
dataset.outputs[i],
|
||||
numElements, dataType, op,
|
||||
root, comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// Wait for reduction to complete
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
hipStreamSynchronize(streams[i]);
|
||||
|
||||
// Check results
|
||||
ValidateResults(dataset);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ReduceCorrectnessSweep,
|
||||
ReduceCorrectnessTest,
|
||||
testing::Combine(
|
||||
// Reduction operator
|
||||
testing::Values(ncclSum, ncclProd, ncclMax, ncclMin),
|
||||
// Data types
|
||||
testing::Values(ncclInt8,
|
||||
ncclUint8,
|
||||
ncclInt32,
|
||||
ncclUint32,
|
||||
ncclInt64,
|
||||
ncclUint64,
|
||||
//ncclFloat16,
|
||||
ncclFloat32,
|
||||
ncclFloat64),
|
||||
// Number of elements
|
||||
testing::Values(1024, 1048576),
|
||||
// Number of devices
|
||||
testing::Values(2,3,4),
|
||||
// In-place or not
|
||||
testing::Values(false, true)));
|
||||
} // namespace
|
||||
@@ -0,0 +1,80 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#ifndef TEST_REDUCE_HPP
|
||||
#define TEST_REDUCE_HPP
|
||||
|
||||
#include "CorrectnessTest.hpp"
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
class ReduceCorrectnessTest : public CorrectnessTest
|
||||
{
|
||||
public:
|
||||
static void ComputeExpectedResults(Dataset& dataset, ncclRedOp_t const op, int const root)
|
||||
{
|
||||
// Copy all inputs to expected arrays temporarily to perform reduction on host
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
HIP_CALL(hipMemcpy(dataset.expected[i], dataset.inputs[i],
|
||||
dataset.NumBytes(), hipMemcpyDeviceToHost));
|
||||
|
||||
// Allocate temporary host array to accumulate results
|
||||
int8_t* resultI1 = (int8_t *)malloc(dataset.NumBytes());
|
||||
uint8_t* resultU1 = (uint8_t *)resultI1;
|
||||
int32_t* resultI4 = (int32_t *)resultI1;
|
||||
uint32_t* resultU4 = (uint32_t *)resultI1;
|
||||
int64_t* resultI8 = (int64_t *)resultI1;
|
||||
uint64_t* resultU8 = (uint64_t *)resultI1;
|
||||
float* resultF4 = (float *)resultI1;
|
||||
double* resultF8 = (double *)resultI1;
|
||||
|
||||
// Initialize the result with the first device's array
|
||||
memcpy(resultI1, dataset.expected[0], dataset.NumBytes());
|
||||
|
||||
// Perform reduction on the other device arrays
|
||||
for (int i = 1; i < dataset.numDevices; i++)
|
||||
{
|
||||
int8_t* arrayI1 = (int8_t *)dataset.expected[i];
|
||||
uint8_t* arrayU1 = (uint8_t *)arrayI1;
|
||||
int32_t* arrayI4 = (int32_t *)arrayI1;
|
||||
uint32_t* arrayU4 = (uint32_t *)arrayI1;
|
||||
int64_t* arrayI8 = (int64_t *)arrayI1;
|
||||
uint64_t* arrayU8 = (uint64_t *)arrayI1;
|
||||
float* arrayF4 = (float *)arrayI1;
|
||||
double* arrayF8 = (double *)arrayI1;
|
||||
|
||||
for (int j = 0; j < dataset.numElements; j++)
|
||||
{
|
||||
switch (dataset.dataType)
|
||||
{
|
||||
case ncclInt8: resultI1[j] = ReduceOp(op, resultI1[j], arrayI1[j]); break;
|
||||
case ncclUint8: resultU1[j] = ReduceOp(op, resultU1[j], arrayU1[j]); break;
|
||||
case ncclInt32: resultI4[j] = ReduceOp(op, resultI4[j], arrayI4[j]); break;
|
||||
case ncclUint32: resultU4[j] = ReduceOp(op, resultU4[j], arrayU4[j]); break;
|
||||
case ncclInt64: resultI8[j] = ReduceOp(op, resultI8[j], arrayI8[j]); break;
|
||||
case ncclUint64: resultU8[j] = ReduceOp(op, resultU8[j], arrayU8[j]); break;
|
||||
case ncclFloat32: resultF4[j] = ReduceOp(op, resultF4[j], arrayF4[j]); break;
|
||||
case ncclFloat64: resultF8[j] = ReduceOp(op, resultF8[j], arrayF8[j]); break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported datatype\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy results into expected arrays
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
{
|
||||
if (i == root)
|
||||
memcpy(dataset.expected[root], resultI1, dataset.NumBytes());
|
||||
else
|
||||
HIP_CALL(hipMemcpy(dataset.expected[i], dataset.outputs[i], dataset.NumBytes(), hipMemcpyDeviceToHost));
|
||||
}
|
||||
free(resultI1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include "test_ReduceScatter.hpp"
|
||||
#include <omp.h>
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
TEST_P(ReduceScatterCorrectnessTest, Correctness)
|
||||
{
|
||||
if (numDevices > numDevicesAvailable) return;
|
||||
if (numElements % numDevices != 0) return;
|
||||
|
||||
// Prepare input / output / expected results
|
||||
Dataset dataset;
|
||||
dataset.Initialize(numDevices, numElements, dataType, inPlace);
|
||||
FillDatasetWithPattern(dataset);
|
||||
ComputeExpectedResults(dataset, op);
|
||||
|
||||
size_t const byteCount = dataset.NumBytes() / dataset.numDevices;
|
||||
size_t const recvCount = dataset.numElements / dataset.numDevices;
|
||||
|
||||
// Launch the reduction (1 thread per GPU)
|
||||
#pragma omp parallel for num_threads(numDevices)
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
ncclReduceScatter(dataset.inputs[i],
|
||||
(int8_t *)dataset.outputs[i] + (i * byteCount),
|
||||
recvCount, dataType, op,
|
||||
comms[i], streams[i]);
|
||||
}
|
||||
|
||||
// Wait for reduction to complete
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
hipStreamSynchronize(streams[i]);
|
||||
|
||||
// Check results
|
||||
ValidateResults(dataset);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ReduceScatterCorrectnessSweep,
|
||||
ReduceScatterCorrectnessTest,
|
||||
testing::Combine(
|
||||
// Reduction operator
|
||||
testing::Values(ncclSum, ncclProd, ncclMax, ncclMin),
|
||||
// Data types
|
||||
testing::Values(ncclInt8,
|
||||
ncclUint8,
|
||||
ncclInt32,
|
||||
ncclUint32,
|
||||
ncclInt64,
|
||||
ncclUint64,
|
||||
//ncclFloat16,
|
||||
ncclFloat32,
|
||||
ncclFloat64),
|
||||
// Number of elements
|
||||
testing::Values(3072, 3145728),
|
||||
// Number of devices
|
||||
testing::Values(2,3,4),
|
||||
// In-place or not
|
||||
testing::Values(false, true)));
|
||||
} // namespace
|
||||
@@ -0,0 +1,83 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*
|
||||
* See LICENSE.txt for license information
|
||||
************************************************************************/
|
||||
#ifndef TEST_REDUCE_SCATTER_HPP
|
||||
#define TEST_REDUCE_SCATTER_HPP
|
||||
|
||||
#include "CorrectnessTest.hpp"
|
||||
|
||||
namespace CorrectnessTests
|
||||
{
|
||||
class ReduceScatterCorrectnessTest : public CorrectnessTest
|
||||
{
|
||||
public:
|
||||
static void ComputeExpectedResults(Dataset& dataset, ncclRedOp_t const op)
|
||||
{
|
||||
// Copy all inputs to expected arrays temporarily to perform reduction on host
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
HIP_CALL(hipMemcpy(dataset.expected[i], dataset.inputs[i],
|
||||
dataset.NumBytes(), hipMemcpyDeviceToHost));
|
||||
|
||||
// Allocate temporary host array to accumulate results
|
||||
int8_t* resultI1 = (int8_t *)malloc(dataset.NumBytes());
|
||||
uint8_t* resultU1 = (uint8_t *)resultI1;
|
||||
int32_t* resultI4 = (int32_t *)resultI1;
|
||||
uint32_t* resultU4 = (uint32_t *)resultI1;
|
||||
int64_t* resultI8 = (int64_t *)resultI1;
|
||||
uint64_t* resultU8 = (uint64_t *)resultI1;
|
||||
float* resultF4 = (float *)resultI1;
|
||||
double* resultF8 = (double *)resultI1;
|
||||
|
||||
// Initialize the result with the first device's array
|
||||
memcpy(resultI1, dataset.expected[0], dataset.NumBytes());
|
||||
|
||||
// Perform reduction on the other device arrays
|
||||
for (int i = 1; i < dataset.numDevices; i++)
|
||||
{
|
||||
int8_t* arrayI1 = (int8_t *)dataset.expected[i];
|
||||
uint8_t* arrayU1 = (uint8_t *)arrayI1;
|
||||
int32_t* arrayI4 = (int32_t *)arrayI1;
|
||||
uint32_t* arrayU4 = (uint32_t *)arrayI1;
|
||||
int64_t* arrayI8 = (int64_t *)arrayI1;
|
||||
uint64_t* arrayU8 = (uint64_t *)arrayI1;
|
||||
float* arrayF4 = (float *)arrayI1;
|
||||
double* arrayF8 = (double *)arrayI1;
|
||||
|
||||
for (int j = 0; j < dataset.numElements; j++)
|
||||
{
|
||||
switch (dataset.dataType)
|
||||
{
|
||||
case ncclInt8: resultI1[j] = ReduceOp(op, resultI1[j], arrayI1[j]); break;
|
||||
case ncclUint8: resultU1[j] = ReduceOp(op, resultU1[j], arrayU1[j]); break;
|
||||
case ncclInt32: resultI4[j] = ReduceOp(op, resultI4[j], arrayI4[j]); break;
|
||||
case ncclUint32: resultU4[j] = ReduceOp(op, resultU4[j], arrayU4[j]); break;
|
||||
case ncclInt64: resultI8[j] = ReduceOp(op, resultI8[j], arrayI8[j]); break;
|
||||
case ncclUint64: resultU8[j] = ReduceOp(op, resultU8[j], arrayU8[j]); break;
|
||||
case ncclFloat32: resultF4[j] = ReduceOp(op, resultF4[j], arrayF4[j]); break;
|
||||
case ncclFloat64: resultF8[j] = ReduceOp(op, resultF8[j], arrayF8[j]); break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] Unsupported datatype\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy results into expected arrays
|
||||
size_t const byteCount = dataset.NumBytes() / dataset.numDevices;
|
||||
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
HIP_CALL(hipMemcpy(dataset.expected[i], dataset.outputs[i],
|
||||
dataset.NumBytes(), hipMemcpyDeviceToHost));
|
||||
|
||||
for (int i = 0; i < dataset.numDevices; i++)
|
||||
memcpy((int8_t *)dataset.expected[i] + (i * byteCount),
|
||||
resultI1 + (i * byteCount), byteCount);
|
||||
|
||||
free(resultI1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
Ссылка в новой задаче
Block a user