diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index d54eb6b9e0..8c580cc95f 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -14,9 +14,7 @@ "Unit_hipDeviceReset_Positive_Basic", "Unit_hipDeviceReset_Positive_Threaded", "Unit_hipGraphMemcpyNodeSetParamsToSymbol_Positive_Basic", - "Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Positive_Basic", "Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Positive_Basic", - "Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Positive_Basic", "Unit_hipKernelNameRef_Negative_Parameters", "Unit_hipMemAdvise_AccessedBy_All_Devices", "Unit_hipMemAdvise_No_Flag_Interference", @@ -31,7 +29,6 @@ "Unit_hipStreamAttachMemAsync_Positive_AttachGlobal", "Unit_hipStreamAttachMemAsync_Negative_Parameters", "Unit_hipMemGetAddressRange_Positive", - "Unit_hipGraphAddMemcpyNode1D_Negative_Basic", "Unit_hipStreamGetCaptureInfo_Nullstream_CaptureInfo", "intermittent issue: corrupted double-linked list", "Unit_hipGraphRetainUserObject_Functional_2", @@ -44,10 +41,6 @@ "Unit_hipStreamCreateWithFlags_DefaultStreamInteraction", "Unit_hipStreamWaitEvent_UninitializedStream_Negative", "Unit_hipDeviceSetSharedMemConfig_Negative_Parameters", - "Disabling tests tracked with SWDEV-394083", - "Unit_hipDeviceSynchronize_Positive_Nullstream", - "Disabling tests tracked with SWDEV-393637", - "Unit_hipDeviceSynchronize_Functional", "Unit_hipMemset3DSync", "Unit_hipStreamAddCallback_StrmSyncTiming", "Disabling test tracked SWDEV-394199", @@ -71,10 +64,22 @@ "Unit_hipMemcpyPeer_Positive_Synchronization_Behavior", "SWDEV-398981 fails in stress test", "Unit_hipStreamCreateWithPriority_MulthreadDefaultflag", - "Disabling below 4 tests temporarily due to change in API behavior", - "Unit_hipDeviceSetCacheConfig_Positive_Basic", - "Unit_hipDeviceSetCacheConfig_Negative_Parameters", - "Unit_hipDeviceSetSharedMemConfig_Positive_Basic", - "Unit_hipDeviceSetSharedMemConfig_Negative_Parameters" + "=== Below tests fail in stress test on 23/06/23 ===", + "Unit_hipIpcMemAccess_ParameterValidation", + "Unit_hipMemcpy2DFromArrayAsync_Positive_Synchronization_Behavior", + "Unit_hipMalloc3D_SmallandBigChunks", + "Unit_hipMalloc3D_MultiThread", + "Unit_hipArrayCreate_DiffSizes", + "Unit_hipArrayCreate_MultiThread", + "hipMemGetInfo_DifferentMallocSmall", + "Unit_hipMemGetInfo_ParaSmall", + "Unit_hipMemGetInfo_ParaNonDiv", + "Unit_hipMemGetInfo_ParaMultiSmall", + "Unit_hipMemGetInfo_Negative", + "Unit_hipMemsetDSync - int8_t", + "Unit_hipGraphClone_Test_hipGraphExecMemcpyNodeSetParams", + "Unit_hipGraphClone_Test_hipGraphMemcpyNodeSetParams1D_and_exec", + "Unit_hipStreamValue_Wait64_Blocking_NoMask_And", + "Unit_hipStreamValue_Wait64_Blocking_NoMask_Nor" ] } diff --git a/catch/hipTestMain/config/config_amd_windows_common.json b/catch/hipTestMain/config/config_amd_windows_common.json index 05a22159d9..c6dcda0c92 100644 --- a/catch/hipTestMain/config/config_amd_windows_common.json +++ b/catch/hipTestMain/config/config_amd_windows_common.json @@ -166,11 +166,6 @@ "SWDEV-402082 - PAL Backend fails to reserve address on GPU except first one", "Unit_hipGraphInstantiateWithFlags_FlagAutoFreeOnLaunch_check", "SWDEV-398981 fails in stress test", - "Unit_hipStreamCreateWithPriority_MulthreadDefaultflag", - "Disabling below 4 tests temporarily due to change in API behavior", - "Unit_hipDeviceSetCacheConfig_Positive_Basic", - "Unit_hipDeviceSetCacheConfig_Negative_Parameters", - "Unit_hipDeviceSetSharedMemConfig_Positive_Basic", - "Unit_hipDeviceSetSharedMemConfig_Negative_Parameters" + "Unit_hipStreamCreateWithPriority_MulthreadDefaultflag" ] } diff --git a/catch/include/hip_array_common.hh b/catch/include/hip_array_common.hh index fd6f094f8d..a0b5d96622 100644 --- a/catch/include/hip_array_common.hh +++ b/catch/include/hip_array_common.hh @@ -81,4 +81,136 @@ struct vector_info : type_and_size_and_format {}; template <> struct vector_info - : type_and_size_and_format {}; \ No newline at end of file + : type_and_size_and_format {}; + +template < + typename T, + typename std::enable_if::value == false>::type* = nullptr> +static inline __host__ __device__ constexpr int rank() { + return sizeof(T) / sizeof(decltype(T::x)); +} + +template< + typename T, + typename std::enable_if() == 1>::type* = nullptr> +static inline bool isEqual(const T &val0, const T &val1) { + return val0.x == val1.x; +} + +template< + typename T, + typename std::enable_if() == 2>::type* = nullptr> +static inline bool isEqual(const T &val0, const T &val1) { + return val0.x == val1.x && + val0.y == val1.y; +} + +template< + typename T, + typename std::enable_if() == 4>::type* = nullptr> +static inline bool isEqual(const T &val0, const T &val1) { + return val0.x == val1.x && + val0.y == val1.y && + val0.z == val1.z && + val0.w == val1.w; +} + +template< + typename T, + typename std::enable_if::value>::type* = nullptr> +static inline bool isEqual(const T &val0, const T &val1) { + return val0 == val1; +} + +template< + typename T, + typename std::enable_if() == 1>::type* = nullptr> +const std::string getString(const T& t) +{ + std::ostringstream os; + os<< "(" << t.x << ")"; + return os.str(); +} + +template< + typename T, + typename std::enable_if() == 2>::type* = nullptr> +const std::string getString(const T& t) +{ + std::ostringstream os; + os<< "(" << t.x << ", " << t.y << ")"; + return os.str(); +} + +template< + typename T, + typename std::enable_if() == 3>::type* = nullptr> +const std::string getString(const T& t) +{ + std::ostringstream os; + os<< "(" << t.x << ", " << t.y << ", " << t.z << ")"; + return os.str(); +} + +template< + typename T, + typename std::enable_if() == 4>::type* = nullptr> +const std::string getString(const T& t) +{ + std::ostringstream os; + os<< "(" << t.x << ", " << t.y << ", " << t.z << ", " << t.w << ")"; + return os.str(); +} + +template< + typename T, + typename std::enable_if::value>::type* = nullptr> +std::string getString(const T& t) +{ + std::ostringstream os; + os << t; + return os.str(); +} + +template +static inline T getRandom() { + double r = 0; + if (std::is_signed::value) { + r = (std::rand() - RAND_MAX / 2.0) / (RAND_MAX / 2.0 + 1.); + } else { + r = std::rand() / (RAND_MAX + 1.); + } + return static_cast(std::numeric_limits < T > ::max() * r); +} + +template< + typename T, + typename std::enable_if() == 1>::type* = nullptr> +static inline void initVal(T &val) { + val.x = getRandom(); +} + +template< + typename T, + typename std::enable_if() == 2>::type* = nullptr> +static inline void initVal(T &val) { + val.x = getRandom(); + val.y = getRandom(); +} + +template< + typename T, + typename std::enable_if() == 4>::type* = nullptr> +static inline void initVal(T &val) { + val.x = getRandom(); + val.y = getRandom(); + val.z = getRandom(); + val.w = getRandom(); +} + +template< + typename T, + typename std::enable_if::value>::type* = nullptr> +static inline void initVal(T &val) { + val = getRandom(); +} diff --git a/catch/include/hip_test_common.hh b/catch/include/hip_test_common.hh index 2ba1dddba3..7f9e859cd0 100644 --- a/catch/include/hip_test_common.hh +++ b/catch/include/hip_test_common.hh @@ -22,6 +22,7 @@ THE SOFTWARE. #pragma once #include "hip_test_context.hh" + #include #include #include @@ -30,6 +31,7 @@ THE SOFTWARE. #include #include #include +#include #define HIP_PRINT_STATUS(status) INFO(hipGetErrorName(status) << " at line: " << __LINE__); @@ -429,6 +431,52 @@ static inline void runKernelForDuration(std::chrono::milliseconds duration, hipLaunchKernelGGL(waitKernel_used, dim3(1), dim3(1), 0, stream, ticksPerSecond * millis / 1000); } +class BlockingContext { + std::atomic_bool blocked{true}; + hipStream_t stream; + + public: + BlockingContext(hipStream_t s) : stream(s), blocked(true) {} + + BlockingContext(const BlockingContext& in) { + blocked = in.blocked_val(); + stream = in.stream_val(); + } + + BlockingContext(const BlockingContext&& in) { + blocked = in.blocked_val(); + stream = in.stream_val(); + } + + void reset() { blocked = true; } + + BlockingContext& operator=(const BlockingContext& in) { + blocked = in.blocked_val(); + stream = in.stream_val(); + return *this; + } + + void block_stream() { + blocked = true; + auto blocking_callback = [](hipStream_t, hipError_t, void* data) { + auto blocked = reinterpret_cast(data); + while (blocked->load()) { + // Yield this thread till we are waiting + std::this_thread::yield(); + } + }; + HIP_CHECK(hipStreamAddCallback(stream, blocking_callback, (void*)&blocked, 0)); + } + + void unblock_stream() { + blocked = false; + } + + bool is_blocked() const { return hipStreamQuery(stream) == hipErrorNotReady; } + + bool blocked_val() const { return blocked.load(); } + hipStream_t stream_val() const { return stream; } +}; } // namespace HipTest // This must be called in the beginning of image test app's main() to indicate whether image diff --git a/catch/include/hip_test_defgroups.hh b/catch/include/hip_test_defgroups.hh index 77e389b5da..4fa9facedb 100644 --- a/catch/include/hip_test_defgroups.hh +++ b/catch/include/hip_test_defgroups.hh @@ -42,3 +42,10 @@ THE SOFTWARE. * This section describes the warp shuffle types & functions of HIP runtime API. * @} */ + +/** + * @defgroup StreamTest Stream Management + * @{ + * This section describes the stream management types & functions of HIP runtime API. + * @} + */ diff --git a/catch/include/hip_texture_helper.hh b/catch/include/hip_texture_helper.hh index d0c9db0dc9..461966abfa 100644 --- a/catch/include/hip_texture_helper.hh +++ b/catch/include/hip_texture_helper.hh @@ -8,6 +8,8 @@ #define HIP_SAMPLING_VERIFY_ABSOLUTE_THRESHOLD 0.1 #if HT_NVIDIA +typedef unsigned char uchar; + template typename std::enable_if::type inline __host__ __device__ operator+(const T &a, const T &b) @@ -47,7 +49,6 @@ inline __host__ __device__ operator*=(T &a, const decltype(T::x) &b) } #endif // HT_NVIDIA -// See https://en.wikipedia.org/wiki/SRGB#Transformation // From CIE 1931 color space to sRGB inline float hipSRGBMap(float fc) { double c = static_cast(fc); diff --git a/catch/include/utils.hh b/catch/include/utils.hh index fec888bd97..98f6676d6d 100644 --- a/catch/include/utils.hh +++ b/catch/include/utils.hh @@ -134,7 +134,6 @@ inline void LaunchDelayKernel(const std::chrono::milliseconds interval, const hi HIPCHECK(hipDeviceGetAttribute(&ticks_per_ms, hipDeviceAttributeClockRate, 0)); } Delay<<<1, 1, 0, stream>>>(interval.count(), ticks_per_ms); - HIP_CHECK(hipGetLastError()); } template diff --git a/catch/unit/CMakeLists.txt b/catch/unit/CMakeLists.txt index 31bd37da9b..c26af21b02 100644 --- a/catch/unit/CMakeLists.txt +++ b/catch/unit/CMakeLists.txt @@ -28,6 +28,7 @@ add_subdirectory(occupancy) add_subdirectory(device) add_subdirectory(printf) add_subdirectory(texture) +add_subdirectory(surface) add_subdirectory(streamperthread) add_subdirectory(kernel) add_subdirectory(multiThread) diff --git a/catch/unit/cooperativeGrps/CMakeLists.txt b/catch/unit/cooperativeGrps/CMakeLists.txt index 61c567aee4..ce7632fb8b 100644 --- a/catch/unit/cooperativeGrps/CMakeLists.txt +++ b/catch/unit/cooperativeGrps/CMakeLists.txt @@ -6,6 +6,9 @@ set(TEST_SRC hipCGMultiGridGroupType.cc hipCGMultiGridGroupTypeViaBaseType.cc hipCGMultiGridGroupTypeViaPublicApi.cc + coalesced_groups_shfl_down.cc + coalesced_groups_shfl_up.cc + simple_coalesced_groups.cc ) if(HIP_PLATFORM STREQUAL "nvidia") set_source_files_properties(hipCGMultiGridGroupType.cc PROPERTIES COMPILE_FLAGS "-rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80") diff --git a/catch/unit/cooperativeGrps/coalesced_groups_shfl_down.cc b/catch/unit/cooperativeGrps/coalesced_groups_shfl_down.cc index 63d569c0e5..de13e8eda1 100644 --- a/catch/unit/cooperativeGrps/coalesced_groups_shfl_down.cc +++ b/catch/unit/cooperativeGrps/coalesced_groups_shfl_down.cc @@ -66,7 +66,7 @@ __global__ void kernel_shfl_down (int * dPtr, int *dResults, int lane_delta, int } } -__global__ void kernel_cg_group_partition(int* result, unsigned int tileSz, int cg_sizes) { +__global__ void kernel_cg_group_partition_shfl_down(int* result, unsigned int tileSz, int cg_sizes) { int id = threadIdx.x + blockIdx.x * blockDim.x; if (id % cg_sizes == 0) { @@ -82,7 +82,6 @@ __global__ void kernel_cg_group_partition(int* result, unsigned int tileSz, int threadBlockCGTy.sync(); coalesced_group tiledPartition = tiled_partition(threadBlockCGTy, tileSz); - int threadRank = tiledPartition.thread_rank(); input = tiledPartition.thread_rank(); @@ -110,7 +109,7 @@ void verifyResults(int* ptr, int expectedResult, int numTiles) { } } -void compareResults(int* cpu, int* gpu, int size) { +void compareResultsCoalescedGroupsShflDown(int* cpu, int* gpu, int size) { for (unsigned int i = 0; i < size / sizeof(int); i++) { if (cpu[i] != gpu[i]) { INFO(" results do not match."); @@ -118,7 +117,7 @@ void compareResults(int* cpu, int* gpu, int size) { } } -void printResults(int* ptr, int size) { +void printResultsCoalescedGroupsShflDown(int* ptr, int size) { for (int i = 0; i < size; i++) { std::cout << ptr[i] << " "; } @@ -148,14 +147,14 @@ static void test_group_partition(unsigned int tileSz) { int* dResult = NULL; int* hResult = NULL; - hipHostMalloc(&hResult, numTiles * sizeof(int), hipHostMallocDefault); + HIPCHECK(hipHostMalloc(&hResult, numTiles * sizeof(int), hipHostMallocDefault)); memset(hResult, 0, numTiles * sizeof(int)); - hipMalloc(&dResult, numTiles * sizeof(int)); + HIPCHECK(hipMalloc(&dResult, numTiles * sizeof(int))); // Launch Kernel - hipLaunchKernelGGL(kernel_cg_group_partition, blockSize, threadsPerBlock, + hipLaunchKernelGGL(kernel_cg_group_partition_shfl_down, blockSize, threadsPerBlock, threadsPerBlock * sizeof(int), 0, dResult, tileSz, i); HIP_CHECK(hipGetLastError()); err = hipDeviceSynchronize(); @@ -164,13 +163,13 @@ static void test_group_partition(unsigned int tileSz) { } - hipMemcpy(hResult, dResult, sizeof(int) * numTiles, hipMemcpyDeviceToHost); + HIPCHECK(hipMemcpy(hResult, dResult, sizeof(int) * numTiles, hipMemcpyDeviceToHost)); verifyResults(hResult, expectedSum, numTiles); // Free all allocated memory on host and device - hipFree(dResult); - hipFree(hResult); + HIPCHECK(hipFree(dResult)); + HIPCHECK(hipHostFree(hResult)); delete[] expectedResult; printf("\n...PASSED.\n\n"); @@ -199,7 +198,7 @@ static void test_shfl_down() { int arrSize = blockSize * threadsPerBlock * sizeof(int); - hipHostMalloc(&hPtr, arrSize); + HIPCHECK(hipHostMalloc(&hPtr, arrSize)); // Fill up the array for (int i = 0; i < WAVE_SIZE; i++) { hPtr[i] = rand() % 1000; @@ -210,30 +209,30 @@ static void test_shfl_down() { cpuResultsArr[i] = (i + lane_delta >= group_size) ? hPtr[i] : hPtr[i + lane_delta]; } //printf("Array passed to GPU for computation\n"); - //printResults(hPtr, WAVE_SIZE); - hipMalloc(&dPtr, group_size_in_bytes); - hipMalloc(&dResults, group_size_in_bytes); + //printResultsCoalescedGroupsShflDown(hPtr, WAVE_SIZE); + HIPCHECK(hipMalloc(&dPtr, group_size_in_bytes)); + HIPCHECK(hipMalloc(&dResults, group_size_in_bytes)); - hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice); + HIPCHECK(hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice)); // Launch Kernel hipLaunchKernelGGL(kernel_shfl_down, blockSize, threadsPerBlock, threadsPerBlock * sizeof(int), 0, dPtr, dResults, lane_delta, i); HIP_CHECK(hipGetLastError()); - hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost); + HIPCHECK(hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost)); err = hipDeviceSynchronize(); if (err != hipSuccess) { fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err)); } //printf("GPU results: \n"); - //printResults(hPtr, WAVE_SIZE); + //printResultsCoalescedGroupsShflDown(hPtr, WAVE_SIZE); //printf("Printing cpu to be verified array\n"); - //printResults(cpuResultsArr, WAVE_SIZE); + //printResultsCoalescedGroupsShflDown(cpuResultsArr, WAVE_SIZE); - compareResults(hPtr, cpuResultsArr, group_size_in_bytes); + compareResultsCoalescedGroupsShflDown(hPtr, cpuResultsArr, group_size_in_bytes); std::cout << "Results verified!\n"; - hipFree(hPtr); - hipFree(dPtr); + HIPCHECK(hipHostFree(hPtr)); + HIPCHECK(hipFree(dPtr)); free(cpuResultsArr); } } @@ -246,7 +245,6 @@ TEST_CASE("Unit_coalesced_groups_shfl_down") { ASSERT_EQUAL(hipGetDevice(&deviceId), hipSuccess); hipDeviceProp_t deviceProperties; ASSERT_EQUAL(hipGetDeviceProperties(&deviceProperties, deviceId), hipSuccess); - int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock; // Test shfl_down with random group sizes for (int i = 0; i < 100; i++) { diff --git a/catch/unit/cooperativeGrps/coalesced_groups_shfl_up.cc b/catch/unit/cooperativeGrps/coalesced_groups_shfl_up.cc index e3c9d3d9f4..75ca40a6d7 100644 --- a/catch/unit/cooperativeGrps/coalesced_groups_shfl_up.cc +++ b/catch/unit/cooperativeGrps/coalesced_groups_shfl_up.cc @@ -40,7 +40,7 @@ __device__ int prefix_sum_kernel(coalesced_group const& g, int val) { for (int i = 1; i < sz; i <<= 1) { int temp = g.shfl_up(val, i); - if (g.thread_rank() >= i) { + if ((int)g.thread_rank() >= i) { val += temp; } } @@ -60,7 +60,7 @@ __global__ void kernel_shfl_up (int * dPtr, int *dResults, int lane_delta, int c } -__global__ void kernel_cg_group_partition(int* dPtr, unsigned int tileSz, int cg_sizes) { +__global__ void kernel_cg_group_partition_shfl_up(int* dPtr, unsigned int tileSz, int cg_sizes) { int id = threadIdx.x + blockIdx.x * blockDim.x; if (id % cg_sizes == 0) { @@ -110,7 +110,7 @@ void printResults(int* ptr, int size) { std::cout << '\n'; } -void verifyResults(int* cpu, int* gpu, int size) { +void verifyResultsCoalescedGroupsShflUp(int* cpu, int* gpu, int size) { for (unsigned int i = 0; i < size / sizeof(int); i++) { if (cpu[i] != gpu[i]) { INFO(" Results do not match."); @@ -132,14 +132,14 @@ static void test_group_partition(unsigned tileSz) { int arrSize = blockSize * threadsPerBlock * sizeof(int); - hipHostMalloc(&hPtr, arrSize); - hipMalloc(&dPtr, arrSize); + HIPCHECK(hipHostMalloc(&hPtr, arrSize)); + HIPCHECK(hipMalloc(&dPtr, arrSize)); // Launch Kernel - hipLaunchKernelGGL(kernel_cg_group_partition, blockSize, threadsPerBlock, + hipLaunchKernelGGL(kernel_cg_group_partition_shfl_up, blockSize, threadsPerBlock, threadsPerBlock * sizeof(int), 0, dPtr, tileSz, i); HIP_CHECK(hipGetLastError()); - hipMemcpy(hPtr, dPtr, arrSize, hipMemcpyDeviceToHost); + HIPCHECK(hipMemcpy(hPtr, dPtr, arrSize, hipMemcpyDeviceToHost)); err = hipDeviceSynchronize(); if (err != hipSuccess) { fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err)); @@ -153,12 +153,12 @@ static void test_group_partition(unsigned tileSz) { //std::cout << "\nPrefix sum results on GPU\n"; //printResults(hPtr, tileSz); std::cout << "\n"; - verifyResults(hPtr, cpuPrefixSum, tileSz); + verifyResultsCoalescedGroupsShflUp(hPtr, cpuPrefixSum, tileSz); std::cout << "Results verified!\n"; delete[] cpuPrefixSum; - hipFree(hPtr); - hipFree(dPtr); + HIPCHECK(hipHostFree(hPtr)); + HIPCHECK(hipFree(dPtr)); } } @@ -185,7 +185,7 @@ static void test_shfl_up() { int arrSize = blockSize * threadsPerBlock * sizeof(int); - hipHostMalloc(&hPtr, arrSize); + HIPCHECK(hipHostMalloc(&hPtr, arrSize)); // Fill up the array for (int i = 0; i < WAVE_SIZE; i++) { hPtr[i] = rand() % 1000; @@ -200,14 +200,14 @@ static void test_shfl_up() { //printf("Printing cpu results arr\n"); //printResults(cpuResultsArr, WAVE_SIZE); - hipMalloc(&dPtr, group_size_in_bytes); - hipMalloc(&dResults, group_size_in_bytes); + HIPCHECK(hipMalloc(&dPtr, group_size_in_bytes)); + HIPCHECK(hipMalloc(&dResults, group_size_in_bytes)); - hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice); + HIPCHECK(hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice)); // Launch Kernel hipLaunchKernelGGL(kernel_shfl_up, blockSize, threadsPerBlock, threadsPerBlock * sizeof(int), 0, dPtr, dResults, lane_delta, i); - hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost); + HIPCHECK(hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost)); HIP_CHECK(hipGetLastError()); err = hipDeviceSynchronize(); if (err != hipSuccess) { @@ -216,22 +216,21 @@ static void test_shfl_up() { //printf("GPU computation array :\n"); //printResults(hPtr, WAVE_SIZE); - verifyResults(hPtr, cpuResultsArr, group_size_in_bytes); + verifyResultsCoalescedGroupsShflUp(hPtr, cpuResultsArr, group_size_in_bytes); std::cout << "Results verified!\n"; - hipFree(hPtr); - hipFree(dPtr); + HIPCHECK(hipHostFree(hPtr)); + HIPCHECK(hipFree(dPtr)); free(cpuResultsArr); } } -TEST_CASE("Unit_coalesced_groups_shfl_down") { +TEST_CASE("Unit_coalesced_groups_shfl_up") { // Use default device for validating the test int deviceId; ASSERT_EQUAL(hipGetDevice(&deviceId), hipSuccess); hipDeviceProp_t deviceProperties; ASSERT_EQUAL(hipGetDeviceProperties(&deviceProperties, deviceId), hipSuccess); - int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock; for (int i = 0; i < 100; i++) { test_shfl_up(); diff --git a/catch/unit/cooperativeGrps/simple_coalesced_groups.cc b/catch/unit/cooperativeGrps/simple_coalesced_groups.cc index cef1d94e96..b07ccfe96f 100644 --- a/catch/unit/cooperativeGrps/simple_coalesced_groups.cc +++ b/catch/unit/cooperativeGrps/simple_coalesced_groups.cc @@ -51,7 +51,7 @@ using namespace cooperative_groups; __device__ int atomicAggInc(int *ptr) { coalesced_group g = coalesced_threads(); - int prev; + int prev = 0; // elect the first active thread to perform atomic add if (g.thread_rank() == 0) { prev = atomicAdd(ptr, g.size()); @@ -104,7 +104,6 @@ __global__ void filter_arr(int *dst, int *nres, const int *src, int n) { */ __device__ int reduction_kernel(coalesced_group g, int* x, int val) { int lane = g.thread_rank(); - int sz = g.size(); for (int i = g.size() / 2; i > 0; i /= 2) { // use lds to store the temporary result @@ -138,7 +137,6 @@ __global__ void kernel_cg_coalesced_group_partition(unsigned int tileSz, int* re int id = threadIdx.x + blockIdx.x * blockDim.x; if (id % cg_sizes == 0) { coalesced_group threadBlockCGTy = coalesced_threads(); - int threadBlockGroupSize = threadBlockCGTy.size(); int* workspace = NULL; @@ -150,13 +148,11 @@ __global__ void kernel_cg_coalesced_group_partition(unsigned int tileSz, int* re workspace = sharedMem; } - int input, outputSum, expectedOutput; + int input, outputSum; // input to reduction, for each thread, is its' rank in the group input = threadBlockCGTy.thread_rank(); - expectedOutput = (threadBlockGroupSize - 1) * threadBlockGroupSize / 2; - outputSum = reduction_kernel(threadBlockCGTy, workspace, input); if (threadBlockCGTy.thread_rank() == 0) { @@ -189,10 +185,8 @@ __global__ void kernel_cg_coalesced_group_partition(unsigned int tileSz, int* re __global__ void kernel_coalesced_active_groups() { thread_block threadBlockCGTy = this_thread_block(); - int threadBlockGroupSize = threadBlockCGTy.size(); // input to reduction, for each thread, is its' rank in the group - int input = threadBlockCGTy.thread_rank(); if (threadBlockCGTy.thread_rank() == 0) { printf(" Creating odd and even set of active thread groups based on branch divergence\n\n"); @@ -222,14 +216,14 @@ __global__ void kernel_coalesced_active_groups() { return; } -void printResults(int* ptr, int size) { +void printResultsSimpleCoalescedGroups(int* ptr, int size) { for (int i = 0; i < size; i++) { std::cout << ptr[i] << " "; } std::cout << '\n'; } -void compareResults(int* cpu, int* gpu, int size) { +void compareResultsSimpleCoalescedGroups(int* cpu, int* gpu, int size) { for (unsigned int i = 0; i < size / sizeof(int); i++) { if (cpu[i] != gpu[i]) { INFO(" results do not match."); @@ -254,7 +248,7 @@ static void test_active_threads_grouping() { } // Search if the sum exists in the expected results array -void verifyResults(int* hPtr, int* dPtr, int size) { +void verifyResultsSimpleCoalescedGroups(int* hPtr, int* dPtr, int size) { int i = 0, j = 0; for (i = 0; i < size; i++) { for (j = 0; j < size; j++) { @@ -294,15 +288,15 @@ static void test_group_partition(unsigned int tileSz, bool useGlobalMem) { } int* dResult = NULL; - hipMalloc(&dResult, sizeof(int) * numTiles); + HIPCHECK(hipMalloc(&dResult, sizeof(int) * numTiles)); int* globalMem = NULL; if (useGlobalMem) { - hipMalloc((void**)&globalMem, threadsPerBlock * sizeof(int)); + HIPCHECK(hipMalloc((void**)&globalMem, threadsPerBlock * sizeof(int))); } int* hResult = NULL; - hipHostMalloc(&hResult, numTiles * sizeof(int), hipHostMallocDefault); + HIPCHECK(hipHostMalloc(&hResult, numTiles * sizeof(int), hipHostMallocDefault)); memset(hResult, 0, numTiles * sizeof(int)); // Launch Kernel @@ -326,13 +320,13 @@ static void test_group_partition(unsigned int tileSz, bool useGlobalMem) { } } - hipMemcpy(hResult, dResult, numTiles * sizeof(int), hipMemcpyDeviceToHost); - verifyResults(expectedSum, hResult, numTiles); + HIPCHECK(hipMemcpy(hResult, dResult, numTiles * sizeof(int), hipMemcpyDeviceToHost)); + verifyResultsSimpleCoalescedGroups(expectedSum, hResult, numTiles); // Free all allocated memory on host and device - hipFree(dResult); - hipFree(hResult); + HIPCHECK(hipFree(dResult)); + HIPCHECK(hipHostFree(hResult)); if (useGlobalMem) { - hipFree(globalMem); + HIPCHECK(hipFree(globalMem)); } delete[] expectedSum; @@ -363,7 +357,7 @@ static void test_shfl_any_to_any() { int arrSize = blockSize * threadsPerBlock * sizeof(int); - hipHostMalloc(&hPtr, arrSize); + HIPCHECK(hipHostMalloc(&hPtr, arrSize)); // Fill up the array for (int i = 0; i < WAVE_SIZE; i++) { hPtr[i] = rand() % 1000; @@ -382,36 +376,36 @@ static void test_shfl_any_to_any() { } //printf("Array passed to GPU for computation\n"); - //printResults(hPtr, WAVE_SIZE); - hipMalloc(&dPtr, group_size_in_bytes); - hipMalloc(&dResults, group_size_in_bytes); + //printResultsSimpleCoalescedGroups(hPtr, WAVE_SIZE); + HIPCHECK(hipMalloc(&dPtr, group_size_in_bytes)); + HIPCHECK(hipMalloc(&dResults, group_size_in_bytes)); - hipMalloc(&dsrcArr, group_size_in_bytes); - hipMemcpy(dsrcArr, srcArr, group_size_in_bytes, hipMemcpyHostToDevice); + HIPCHECK(hipMalloc(&dsrcArr, group_size_in_bytes)); + HIPCHECK(hipMemcpy(dsrcArr, srcArr, group_size_in_bytes, hipMemcpyHostToDevice)); - hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice); + HIPCHECK(hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice)); // Launch Kernel hipLaunchKernelGGL(kernel_shfl_any_to_any, blockSize, threadsPerBlock, threadsPerBlock * sizeof(int), 0 , dPtr, dsrcArr, dResults, i); HIP_CHECK(hipGetLastError()); - hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost); + HIPCHECK(hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost)); err = hipDeviceSynchronize(); if (err != hipSuccess) { fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err)); } //printf("GPU results: \n"); - //printResults(hPtr, group_size); + //printResultsSimpleCoalescedGroups(hPtr, group_size); //printf("Printing cpu to be verified array\n"); - //printResults(cpuResultsArr, group_size); + //printResultsSimpleCoalescedGroups(cpuResultsArr, group_size); //printf("Printing srcLane array that was passed\n"); - //printResults(srcArr, group_size); + //printResultsSimpleCoalescedGroups(srcArr, group_size); //printf("Printing srcLane array on the CPU\n"); - //printResults(srcArrCpu, group_size); - compareResults(hPtr, cpuResultsArr, group_size_in_bytes); + //printResultsSimpleCoalescedGroups(srcArrCpu, group_size); + compareResultsSimpleCoalescedGroups(hPtr, cpuResultsArr, group_size_in_bytes); std::cout << "Results verified!\n"; - hipFree(hPtr); - hipFree(dPtr); + HIPCHECK(hipHostFree(hPtr)); + HIPCHECK(hipFree(dPtr)); free(srcArr); free(srcArrCpu); free(cpuResultsArr); @@ -440,7 +434,7 @@ static void test_shfl_broadcast() { int arrSize = blockSize * threadsPerBlock * sizeof(int); - hipHostMalloc(&hPtr, arrSize); + HIPCHECK(hipHostMalloc(&hPtr, arrSize)); // Fill up the array for (int i = 0; i < WAVE_SIZE; i++) { hPtr[i] = rand() % 1000; @@ -455,30 +449,30 @@ static void test_shfl_broadcast() { cpuResultsArr[i] = srcLaneCpu; } printf("Array passed to GPU for computation\n"); - printResults(hPtr, WAVE_SIZE); - hipMalloc(&dPtr, group_size_in_bytes); - hipMalloc(&dResults, group_size_in_bytes); + printResultsSimpleCoalescedGroups(hPtr, WAVE_SIZE); + HIPCHECK(hipMalloc(&dPtr, group_size_in_bytes)); + HIPCHECK(hipMalloc(&dResults, group_size_in_bytes)); - hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice); + HIPCHECK(hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice)); // Launch Kernel hipLaunchKernelGGL(kernel_shfl, blockSize, threadsPerBlock, threadsPerBlock * sizeof(int), 0, dPtr, dResults, srcLane, i); HIP_CHECK(hipGetLastError()); - hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost); + HIPCHECK(hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost)); err = hipDeviceSynchronize(); if (err != hipSuccess) { fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err)); } printf("GPU results: \n"); - printResults(hPtr, group_size); + printResultsSimpleCoalescedGroups(hPtr, group_size); printf("Printing cpu to be verified array\n"); - printResults(cpuResultsArr, group_size); + printResultsSimpleCoalescedGroups(cpuResultsArr, group_size); - compareResults(hPtr, cpuResultsArr, group_size_in_bytes); + compareResultsSimpleCoalescedGroups(hPtr, cpuResultsArr, group_size_in_bytes); std::cout << "Results verified!\n"; - hipFree(hPtr); - hipFree(dPtr); + HIPCHECK(hipHostFree(hPtr)); + HIPCHECK(hipFree(dPtr)); free(cpuResultsArr); } } @@ -489,7 +483,6 @@ TEST_CASE("Unit_coalesced_groups") { HIP_CHECK(hipGetDevice(&deviceId)); hipDeviceProp_t deviceProperties; HIP_CHECK(hipGetDeviceProperties(&deviceProperties, deviceId)); - int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock; std::cout << "Now testing coalesced_groups" << '\n' << std::endl; @@ -512,7 +505,7 @@ TEST_CASE("Unit_coalesced_groups") { HIP_CHECK(hipMemcpy(d_data_to_filter, data_to_filter, sizeof(int) * NUM_ELEMS, hipMemcpyHostToDevice)); - hipMemset(d_nres, 0, sizeof(int)); + HIPCHECK(hipMemset(d_nres, 0, sizeof(int))); dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1); dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK) + 1, 1, 1); diff --git a/catch/unit/device/hipDeviceSetGetCacheConfig.cc b/catch/unit/device/hipDeviceSetGetCacheConfig.cc index b093336d6a..dc82a85fc3 100644 --- a/catch/unit/device/hipDeviceSetGetCacheConfig.cc +++ b/catch/unit/device/hipDeviceSetGetCacheConfig.cc @@ -38,16 +38,12 @@ TEST_CASE("Unit_hipDeviceSetCacheConfig_Positive_Basic") { const auto cache_config = GENERATE(from_range(std::begin(kCacheConfigs), std::end(kCacheConfigs))); -#if HT_AMD - HIP_CHECK_ERROR(hipDeviceSetCacheConfig(cache_config), hipErrorNotSupported); -#elif HT_NVIDIA HIP_CHECK(hipDeviceSetCacheConfig(cache_config)); -#endif } TEST_CASE("Unit_hipDeviceSetCacheConfig_Negative_Parameters") { #if HT_AMD - HIP_CHECK_ERROR(hipDeviceSetCacheConfig(static_cast(-1)), hipErrorNotSupported); + HIP_CHECK_ERROR(hipDeviceSetCacheConfig(static_cast(-1)), hipSuccess); #elif HT_NVIDIA HIP_CHECK_ERROR(hipDeviceSetCacheConfig(static_cast(-1)), hipErrorInvalidValue); #endif @@ -105,4 +101,4 @@ TEST_CASE("Unit_hipDeviceGetCacheConfig_Positive_Threaded") { TEST_CASE("Unit_HipDeviceGetCacheConfig_Negative_Parameters") { HIP_CHECK_ERROR(hipDeviceGetCacheConfig(nullptr), hipErrorInvalidValue); -} \ No newline at end of file +} diff --git a/catch/unit/device/hipDeviceSetGetSharedMemConfig.cc b/catch/unit/device/hipDeviceSetGetSharedMemConfig.cc index 83251172dc..9a1230a7c8 100644 --- a/catch/unit/device/hipDeviceSetGetSharedMemConfig.cc +++ b/catch/unit/device/hipDeviceSetGetSharedMemConfig.cc @@ -35,11 +35,7 @@ TEST_CASE("Unit_hipDeviceSetSharedMemConfig_Positive_Basic") { HIP_CHECK(hipSetDevice(device)); INFO("Current device is " << device); -#if HT_AMD - HIP_CHECK_ERROR(hipDeviceSetSharedMemConfig(mem_config), hipErrorNotSupported); -#elif HT_NVIDIA HIP_CHECK(hipDeviceSetSharedMemConfig(mem_config)); -#endif } TEST_CASE("Unit_hipDeviceSetSharedMemConfig_Negative_Parameters") { @@ -113,4 +109,4 @@ TEST_CASE("Unit_hipDeviceGetSharedMemConfig_Positive_Threaded") { TEST_CASE("Unit_hipDeviceGetSharedMemConfig_Negative_Parameters") { HIP_CHECK_ERROR(hipDeviceGetSharedMemConfig(nullptr), hipErrorInvalidValue); -} \ No newline at end of file +} diff --git a/catch/unit/device/hipDeviceSynchronize.cc b/catch/unit/device/hipDeviceSynchronize.cc index bfb02ab784..4dd3ed0dff 100644 --- a/catch/unit/device/hipDeviceSynchronize.cc +++ b/catch/unit/device/hipDeviceSynchronize.cc @@ -33,15 +33,15 @@ THE SOFTWARE. #define NUM_ITERS 1 << 30 static __global__ void Iter(int* Ad, int num) { - int tx = threadIdx.x + blockIdx.x * blockDim.x; - // Kernel loop designed to execute very slowly. - // so we can test timing-related - // behavior below - if (tx == 0) { - for (int i = 0; i < num; i++) { - Ad[tx] += 1; - } + int tx = threadIdx.x + blockIdx.x * blockDim.x; + // Kernel loop designed to execute very slowly. + // so we can test timing-related + // behavior below + if (tx == 0) { + for (int i = 0; i < num; i++) { + Ad[tx] += 1; } + } } TEST_CASE("Unit_hipDeviceSynchronize_Positive_Empty_Streams") { @@ -61,42 +61,47 @@ TEST_CASE("Unit_hipDeviceSynchronize_Positive_Nullstream") { INFO("Current device: " << device); int *A_h = nullptr, *A_d = nullptr; + HipTest::BlockingContext b_context{nullptr}; HIP_CHECK(hipHostMalloc(reinterpret_cast(&A_h), _SIZE, hipHostMallocDefault)); A_h[0] = 1; HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), _SIZE)); HIP_CHECK(hipMemcpyAsync(A_d, A_h, _SIZE, hipMemcpyHostToDevice, NULL)); + b_context.block_stream(); + REQUIRE(b_context.is_blocked()); hipLaunchKernelGGL(HIP_KERNEL_NAME(Iter), dim3(1), dim3(1), 0, NULL, A_d, 1 << 30); HIP_CHECK(hipMemcpyAsync(A_h, A_d, _SIZE, hipMemcpyDeviceToHost, NULL)); - CHECK(1 << 30 != A_h[0] - 1); + REQUIRE(1 << 30 != A_h[0] - 1); + b_context.unblock_stream(); HIP_CHECK(hipDeviceSynchronize()); - CHECK(1 << 30 == A_h[0] - 1); + REQUIRE(1 << 30 == A_h[0] - 1); } TEST_CASE("Unit_hipDeviceSynchronize_Functional") { int* A[NUM_STREAMS]; int* Ad[NUM_STREAMS]; hipStream_t stream[NUM_STREAMS]; + std::vector b_context; + b_context.reserve(NUM_STREAMS); + for (int i = 0; i < NUM_STREAMS; i++) { - HIP_CHECK(hipHostMalloc(reinterpret_cast(&A[i]), _SIZE, - hipHostMallocDefault)); - A[i][0] = 1; - HIP_CHECK(hipMalloc(reinterpret_cast(&Ad[i]), _SIZE)); - HIP_CHECK(hipStreamCreate(&stream[i])); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&A[i]), _SIZE, hipHostMallocDefault)); + A[i][0] = 1; + HIP_CHECK(hipMalloc(reinterpret_cast(&Ad[i]), _SIZE)); + HIP_CHECK(hipStreamCreate(&stream[i])); + b_context.emplace_back(HipTest::BlockingContext(stream[i])); } for (int i = 0; i < NUM_STREAMS; i++) { - HIP_CHECK(hipMemcpyAsync(Ad[i], A[i], _SIZE, hipMemcpyHostToDevice, - stream[i])); + HIP_CHECK(hipMemcpyAsync(Ad[i], A[i], _SIZE, hipMemcpyHostToDevice, stream[i])); } for (int i = 0; i < NUM_STREAMS; i++) { - hipLaunchKernelGGL(HIP_KERNEL_NAME(Iter), dim3(1), dim3(1), 0, - stream[i], Ad[i], NUM_ITERS); - HIP_CHECK(hipGetLastError()); + b_context[i].block_stream(); + REQUIRE(b_context[i].is_blocked()); + hipLaunchKernelGGL(HIP_KERNEL_NAME(Iter), dim3(1), dim3(1), 0, stream[i], Ad[i], NUM_ITERS); } for (int i = 0; i < NUM_STREAMS; i++) { - HIP_CHECK(hipMemcpyAsync(A[i], Ad[i], _SIZE, hipMemcpyDeviceToHost, - stream[i])); + HIP_CHECK(hipMemcpyAsync(A[i], Ad[i], _SIZE, hipMemcpyDeviceToHost, stream[i])); } @@ -106,7 +111,10 @@ TEST_CASE("Unit_hipDeviceSynchronize_Functional") { // Conservative implementations which synchronize the hipMemcpyAsync will // fail, ie if HIP_LAUNCH_BLOCKING=true. - CHECK(NUM_ITERS != A[NUM_STREAMS - 1][0] - 1); + REQUIRE(NUM_ITERS != A[NUM_STREAMS - 1][0] - 1); + for (int i = 0; i < NUM_STREAMS; i++) { + b_context[i].unblock_stream(); + } HIP_CHECK(hipDeviceSynchronize()); - CHECK(NUM_ITERS == A[NUM_STREAMS - 1][0] - 1); + REQUIRE(NUM_ITERS == A[NUM_STREAMS - 1][0] - 1); } diff --git a/catch/unit/deviceLib/vectorTypesDevice.cc b/catch/unit/deviceLib/vectorTypesDevice.cc index 5da42bc6bc..cc1cbdcda1 100644 --- a/catch/unit/deviceLib/vectorTypesDevice.cc +++ b/catch/unit/deviceLib/vectorTypesDevice.cc @@ -267,3 +267,7 @@ TEST_CASE("Unit_vectorTypes_CompileTest") { HIP_CHECK(hipFree(ptr)); REQUIRE(passed == true); } +//Test to check __half2_raw can be initialized without designator +TEST_CASE("Unit_half2_raw_def") { + constexpr __half2_raw inf = {0x7C00,0x7C00}; +} \ No newline at end of file diff --git a/catch/unit/errorHandling/CMakeLists.txt b/catch/unit/errorHandling/CMakeLists.txt index 78933fd3f8..5df11b5d1c 100644 --- a/catch/unit/errorHandling/CMakeLists.txt +++ b/catch/unit/errorHandling/CMakeLists.txt @@ -4,6 +4,8 @@ set(TEST_SRC hipGetErrorString.cc hipGetLastError.cc hipPeekAtLastError.cc + hipDrvGetErrorName.cc + hipDrvGetErrorString.cc ) hip_add_exe_to_target(NAME ErrorHandlingTest diff --git a/catch/unit/errorHandling/errorEnumerators.h b/catch/unit/errorHandling/errorEnumerators.h index a379a47a06..e671938c41 100644 --- a/catch/unit/errorHandling/errorEnumerators.h +++ b/catch/unit/errorHandling/errorEnumerators.h @@ -33,6 +33,7 @@ constexpr hipError_t kErrorEnumerators[] = {hipSuccess, hipErrorProfilerNotInitialized, hipErrorProfilerAlreadyStarted, hipErrorProfilerAlreadyStopped, + #if HT_AMD hipErrorInvalidConfiguration, hipErrorInvalidPitchValue, hipErrorInvalidSymbol, @@ -42,6 +43,7 @@ constexpr hipError_t kErrorEnumerators[] = {hipSuccess, hipErrorMissingConfiguration, hipErrorPriorLaunchFailure, hipErrorInvalidDeviceFunction, + #endif hipErrorNoDevice, hipErrorInvalidDevice, hipErrorInvalidImage, @@ -95,5 +97,8 @@ constexpr hipError_t kErrorEnumerators[] = {hipSuccess, hipErrorStreamCaptureWrongThread, hipErrorGraphExecUpdateFailure, hipErrorUnknown, + #if HT_AMD hipErrorRuntimeMemory, - hipErrorRuntimeOther}; \ No newline at end of file + hipErrorRuntimeOther + #endif + }; diff --git a/catch/unit/errorHandling/hipDrvGetErrorName.cc b/catch/unit/errorHandling/hipDrvGetErrorName.cc new file mode 100644 index 0000000000..367d890be0 --- /dev/null +++ b/catch/unit/errorHandling/hipDrvGetErrorName.cc @@ -0,0 +1,363 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include "errorEnumerators.h" + +// Local Function to return the error code in string + +static const char *ErrorName(hipError_t enumerator) { + switch (enumerator) { + #if HT_AMD + case hipSuccess: + return "hipSuccess"; + case hipErrorInvalidValue: + return "hipErrorInvalidValue"; + case hipErrorOutOfMemory: + return "hipErrorOutOfMemory"; + case hipErrorNotInitialized: + return "hipErrorNotInitialized"; + case hipErrorDeinitialized: + return "hipErrorDeinitialized"; + case hipErrorProfilerDisabled: + return "hipErrorProfilerDisabled"; + case hipErrorProfilerNotInitialized: + return "hipErrorProfilerNotInitialized"; + case hipErrorProfilerAlreadyStarted: + return "hipErrorProfilerAlreadyStarted"; + case hipErrorProfilerAlreadyStopped: + return "hipErrorProfilerAlreadyStopped"; + case hipErrorInvalidConfiguration: + return "hipErrorInvalidConfiguration"; + case hipErrorInvalidSymbol: + return "hipErrorInvalidSymbol"; + case hipErrorInvalidDevicePointer: + return "hipErrorInvalidDevicePointer"; + case hipErrorInvalidMemcpyDirection: + return "hipErrorInvalidMemcpyDirection"; + case hipErrorInsufficientDriver: + return "hipErrorInsufficientDriver"; + case hipErrorMissingConfiguration: + return "hipErrorMissingConfiguration"; + case hipErrorPriorLaunchFailure: + return "hipErrorPriorLaunchFailure"; + case hipErrorInvalidDeviceFunction: + return "hipErrorInvalidDeviceFunction"; + case hipErrorNoDevice: + return "hipErrorNoDevice"; + case hipErrorInvalidDevice: + return "hipErrorInvalidDevice"; + case hipErrorInvalidPitchValue: + return "hipErrorInvalidPitchValue"; + case hipErrorInvalidImage: + return "hipErrorInvalidImage"; + case hipErrorInvalidContext: + return "hipErrorInvalidContext"; + case hipErrorContextAlreadyCurrent: + return "hipErrorContextAlreadyCurrent"; + case hipErrorMapFailed: + return "hipErrorMapFailed"; + case hipErrorUnmapFailed: + return "hipErrorUnmapFailed"; + case hipErrorArrayIsMapped: + return "hipErrorArrayIsMapped"; + case hipErrorAlreadyMapped: + return "hipErrorAlreadyMapped"; + case hipErrorNoBinaryForGpu: + return "hipErrorNoBinaryForGpu"; + case hipErrorAlreadyAcquired: + return "hipErrorAlreadyAcquired"; + case hipErrorNotMapped: + return "hipErrorNotMapped"; + case hipErrorNotMappedAsArray: + return "hipErrorNotMappedAsArray"; + case hipErrorNotMappedAsPointer: + return "hipErrorNotMappedAsPointer"; + case hipErrorECCNotCorrectable: + return "hipErrorECCNotCorrectable"; + case hipErrorUnsupportedLimit: + return "hipErrorUnsupportedLimit"; + case hipErrorContextAlreadyInUse: + return "hipErrorContextAlreadyInUse"; + case hipErrorPeerAccessUnsupported: + return "hipErrorPeerAccessUnsupported"; + case hipErrorInvalidKernelFile: + return "hipErrorInvalidKernelFile"; + case hipErrorInvalidGraphicsContext: + return "hipErrorInvalidGraphicsContext"; + case hipErrorInvalidSource: + return "hipErrorInvalidSource"; + case hipErrorFileNotFound: + return "hipErrorFileNotFound"; + case hipErrorSharedObjectSymbolNotFound: + return "hipErrorSharedObjectSymbolNotFound"; + case hipErrorSharedObjectInitFailed: + return "hipErrorSharedObjectInitFailed"; + case hipErrorOperatingSystem: + return "hipErrorOperatingSystem"; + case hipErrorInvalidHandle: + return "hipErrorInvalidHandle"; + case hipErrorIllegalState: + return "hipErrorIllegalState"; + case hipErrorNotFound: + return "hipErrorNotFound"; + case hipErrorNotReady: + return "hipErrorNotReady"; + case hipErrorIllegalAddress: + return "hipErrorIllegalAddress"; + case hipErrorLaunchOutOfResources: + return "hipErrorLaunchOutOfResources"; + case hipErrorLaunchTimeOut: + return "hipErrorLaunchTimeOut"; + case hipErrorPeerAccessAlreadyEnabled: + return "hipErrorPeerAccessAlreadyEnabled"; + case hipErrorPeerAccessNotEnabled: + return "hipErrorPeerAccessNotEnabled"; + case hipErrorSetOnActiveProcess: + return "hipErrorSetOnActiveProcess"; + case hipErrorContextIsDestroyed: + return "hipErrorContextIsDestroyed"; + case hipErrorAssert: + return "hipErrorAssert"; + case hipErrorHostMemoryAlreadyRegistered: + return "hipErrorHostMemoryAlreadyRegistered"; + case hipErrorHostMemoryNotRegistered: + return "hipErrorHostMemoryNotRegistered"; + case hipErrorLaunchFailure: + return "hipErrorLaunchFailure"; + case hipErrorNotSupported: + return "hipErrorNotSupported"; + case hipErrorUnknown: + return "hipErrorUnknown"; + case hipErrorRuntimeMemory: + return "hipErrorRuntimeMemory"; + case hipErrorRuntimeOther: + return "hipErrorRuntimeOther"; + case hipErrorCooperativeLaunchTooLarge: + return "hipErrorCooperativeLaunchTooLarge"; + case hipErrorStreamCaptureUnsupported: + return "hipErrorStreamCaptureUnsupported"; + case hipErrorStreamCaptureInvalidated: + return "hipErrorStreamCaptureInvalidated"; + case hipErrorStreamCaptureMerge: + return "hipErrorStreamCaptureMerge"; + case hipErrorStreamCaptureUnmatched: + return "hipErrorStreamCaptureUnmatched"; + case hipErrorStreamCaptureUnjoined: + return "hipErrorStreamCaptureUnjoined"; + case hipErrorStreamCaptureIsolation: + return "hipErrorStreamCaptureIsolation"; + case hipErrorStreamCaptureImplicit: + return "hipErrorStreamCaptureImplicit"; + case hipErrorCapturedEvent: + return "hipErrorCapturedEvent"; + case hipErrorStreamCaptureWrongThread: + return "hipErrorStreamCaptureWrongThread"; + case hipErrorGraphExecUpdateFailure: + return "hipErrorGraphExecUpdateFailure"; + case hipErrorTbd: + return "hipErrorTbd"; + default: + return "hipErrorUnknown"; + #endif + #if HT_NVIDIA + case hipSuccess: + return "CUDA_SUCCESS"; + case hipErrorInvalidValue: + return "CUDA_ERROR_INVALID_VALUE"; + case hipErrorOutOfMemory: + return "CUDA_ERROR_OUT_OF_MEMORY"; + case hipErrorNotInitialized: + return "CUDA_ERROR_NOT_INITIALIZED"; + case hipErrorDeinitialized: + return "CUDA_ERROR_DEINITIALIZED"; + case hipErrorProfilerDisabled: + return "CUDA_ERROR_PROFILER_DISABLED"; + case hipErrorProfilerNotInitialized: + return "CUDA_ERROR_PROFILER_NOT_INITIALIZED"; + case hipErrorProfilerAlreadyStarted: + return "CUDA_ERROR_PROFILER_ALREADY_STARTED"; + case hipErrorProfilerAlreadyStopped: + return "CUDA_ERROR_PROFILER_ALREADY_STOPPED"; + case hipErrorInvalidConfiguration: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorInvalidSymbol: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorInvalidDevicePointer: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorInvalidMemcpyDirection: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorInsufficientDriver: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorMissingConfiguration: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorPriorLaunchFailure: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorInvalidDeviceFunction: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorNoDevice: + return "CUDA_ERROR_NO_DEVICE"; + case hipErrorInvalidDevice: + return "CUDA_ERROR_INVALID_DEVICE"; + case hipErrorInvalidPitchValue: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorInvalidImage: + return "CUDA_ERROR_INVALID_IMAGE"; + case hipErrorInvalidContext: + return "CUDA_ERROR_INVALID_CONTEXT"; + case hipErrorContextAlreadyCurrent: + return "CUDA_ERROR_CONTEXT_ALREADY_CURRENT"; + case hipErrorMapFailed: + return "CUDA_ERROR_MAP_FAILED"; + case hipErrorUnmapFailed: + return "CUDA_ERROR_UNMAP_FAILED"; + case hipErrorArrayIsMapped: + return "CUDA_ERROR_ARRAY_IS_MAPPED"; + case hipErrorAlreadyMapped: + return "CUDA_ERROR_ALREADY_MAPPED"; + case hipErrorNoBinaryForGpu: + return "CUDA_ERROR_NO_BINARY_FOR_GPU"; + case hipErrorAlreadyAcquired: + return "CUDA_ERROR_ALREADY_ACQUIRED"; + case hipErrorNotMapped: + return "CUDA_ERROR_NOT_MAPPED"; + case hipErrorNotMappedAsArray: + return "CUDA_ERROR_NOT_MAPPED_AS_ARRAY"; + case hipErrorNotMappedAsPointer: + return "CUDA_ERROR_NOT_MAPPED_AS_POINTER"; + case hipErrorECCNotCorrectable: + return "CUDA_ERROR_ECC_UNCORRECTABLE"; + case hipErrorUnsupportedLimit: + return "CUDA_ERROR_UNSUPPORTED_LIMIT"; + case hipErrorContextAlreadyInUse: + return "CUDA_ERROR_CONTEXT_ALREADY_IN_USE"; + case hipErrorPeerAccessUnsupported: + return "CUDA_ERROR_PEER_ACCESS_UNSUPPORTED"; + case hipErrorInvalidKernelFile: + return "CUDA_ERROR_INVALID_PTX"; + case hipErrorInvalidGraphicsContext: + return "CUDA_ERROR_INVALID_GRAPHICS_CONTEXT"; + case hipErrorInvalidSource: + return "CUDA_ERROR_INVALID_SOURCE"; + case hipErrorFileNotFound: + return "CUDA_ERROR_FILE_NOT_FOUND"; + case hipErrorSharedObjectSymbolNotFound: + return "CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND"; + case hipErrorSharedObjectInitFailed: + return "CUDA_ERROR_SHARED_OBJECT_INIT_FAILED"; + case hipErrorOperatingSystem: + return "CUDA_ERROR_OPERATING_SYSTEM"; + case hipErrorInvalidHandle: + return "CUDA_ERROR_INVALID_HANDLE"; + case hipErrorIllegalState: + return "CUDA_ERROR_ILLEGAL_STATE"; + case hipErrorNotFound: + return "CUDA_ERROR_NOT_FOUND"; + case hipErrorNotReady: + return "CUDA_ERROR_NOT_READY"; + case hipErrorIllegalAddress: + return "CUDA_ERROR_ILLEGAL_ADDRESS"; + case hipErrorLaunchOutOfResources: + return "CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES"; + case hipErrorLaunchTimeOut: + return "CUDA_ERROR_LAUNCH_TIMEOUT"; + case hipErrorPeerAccessAlreadyEnabled: + return "CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED"; + case hipErrorPeerAccessNotEnabled: + return "CUDA_ERROR_PEER_ACCESS_NOT_ENABLED"; + case hipErrorSetOnActiveProcess: + return "CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE"; + case hipErrorContextIsDestroyed: + return "CUDA_ERROR_CONTEXT_IS_DESTROYED"; + case hipErrorAssert: + return "CUDA_ERROR_ASSERT"; + case hipErrorHostMemoryAlreadyRegistered: + return "CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED"; + case hipErrorHostMemoryNotRegistered: + return "CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED"; + case hipErrorLaunchFailure: + return "CUDA_ERROR_LAUNCH_FAILED"; + case hipErrorNotSupported: + return "CUDA_ERROR_NOT_SUPPORTED"; + case hipErrorUnknown: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorRuntimeMemory: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorRuntimeOther: + return "CUDA_ERROR_UNKNOWN"; + case hipErrorCooperativeLaunchTooLarge: + return "CUDA_ERROR_COOPERATIVE_LAUNCH_TOO_LARGE"; + case hipErrorStreamCaptureUnsupported: + return "CUDA_ERROR_STREAM_CAPTURE_UNSUPPORTED"; + case hipErrorStreamCaptureInvalidated: + return "CUDA_ERROR_STREAM_CAPTURE_INVALIDATED"; + case hipErrorStreamCaptureMerge: + return "CUDA_ERROR_STREAM_CAPTURE_MERGE"; + case hipErrorStreamCaptureUnmatched: + return "CUDA_ERROR_STREAM_CAPTURE_UNMATCHED"; + case hipErrorStreamCaptureUnjoined: + return "CUDA_ERROR_STREAM_CAPTURE_UNJOINED"; + case hipErrorStreamCaptureIsolation: + return "CUDA_ERROR_STREAM_CAPTURE_ISOLATION"; + case hipErrorStreamCaptureImplicit: + return "CUDA_ERROR_STREAM_CAPTURE_IMPLICIT"; + case hipErrorCapturedEvent: + return "CUDA_ERROR_CAPTURED_EVENT"; + case hipErrorStreamCaptureWrongThread: + return "CUDA_ERROR_STREAM_CAPTURE_WRONG_THREAD"; + case hipErrorGraphExecUpdateFailure: + return "CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE"; + default: + return "CUDA_ERROR_UNKNOWN"; + #endif + } +} + +// Functional test case +// Test case to verify the returned error name is same as generated error name. + +TEST_CASE("Unit_hipDrvGetErrorName_Functional") { + const char* error_string = nullptr; + hipError_t error_ret; + const auto enumerator = + GENERATE(from_range(std::begin(kErrorEnumerators), + std::end(kErrorEnumerators))); + error_ret = hipDrvGetErrorName(enumerator, &error_string); + REQUIRE(error_string != nullptr); + REQUIRE(strcmp(error_string, ErrorName(enumerator)) == 0); + REQUIRE(error_ret == hipSuccess); +} + +// Negative test cases. + +TEST_CASE("Unit_hipDrvGetErrorName_Negative") { + const char* error_string = nullptr; + SECTION("pass unknown value to hipError") { + REQUIRE((hipDrvGetErrorName(static_cast(-1), &error_string)) + == hipErrorInvalidValue); + } + #if HT_AMD + SECTION("pass nullptr to error string") { + REQUIRE((hipDrvGetErrorString(static_cast(0), nullptr)) + == hipErrorInvalidValue); + } + #endif +} diff --git a/catch/unit/errorHandling/hipDrvGetErrorString.cc b/catch/unit/errorHandling/hipDrvGetErrorString.cc new file mode 100644 index 0000000000..2b51a82422 --- /dev/null +++ b/catch/unit/errorHandling/hipDrvGetErrorString.cc @@ -0,0 +1,263 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include "errorEnumerators.h" + +// Local Function to return the error string. + +static const char *ErrorString(hipError_t enumerator) { + switch (enumerator) { + case hipSuccess: + return "no error"; + case hipErrorInvalidValue: + return "invalid argument"; + case hipErrorOutOfMemory: + return "out of memory"; + case hipErrorNotInitialized: + return "initialization error"; + case hipErrorDeinitialized: + return "driver shutting down"; + case hipErrorProfilerDisabled: + return "profiler disabled while using external profiling tool"; + case hipErrorProfilerNotInitialized: + #if HT_AMD + return "profiler is not initialized"; + #elif HT_NVIDIA + return "profiler not initialized: call cudaProfilerInitialize()"; + #endif + case hipErrorProfilerAlreadyStarted: + return "profiler already started"; + case hipErrorProfilerAlreadyStopped: + return "profiler already stopped"; + #if HT_AMD + case hipErrorInvalidConfiguration: + return "invalid configuration argument"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorInvalidPitchValue: + return "invalid pitch argument"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorInvalidSymbol: + return "invalid device symbol"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorInvalidDevicePointer: + return "invalid device pointer"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorInvalidMemcpyDirection: + return "invalid copy direction for memcpy"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorInsufficientDriver: + return "driver version is insufficient for runtime version"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorMissingConfiguration: + return "__global__ function call is not configured"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorPriorLaunchFailure: + return "unspecified launch failure in prior launch"; + #elif HT_NVIDIA + return "unknown error"; + #endif + #if HT_AMD + case hipErrorInvalidDeviceFunction: + return "invalid device function"; + #elif HT_NVIDIA + return "unknown error"; + #endif + case hipErrorNoDevice: + #if HT_AMD + return "no ROCm-capable device is detected"; + #elif HT_NVIDIA + return "no CUDA-capable device is detected"; + #endif + case hipErrorInvalidDevice: + return "invalid device ordinal"; + case hipErrorInvalidImage: + return "device kernel image is invalid"; + case hipErrorInvalidContext: + return "invalid device context"; + case hipErrorContextAlreadyCurrent: + #if HT_AMD + return "context is already current context"; + #elif HT_NVIDIA + return "context already current"; + #endif + case hipErrorMapFailed: + return "mapping of buffer object failed"; + case hipErrorUnmapFailed: + return "unmapping of buffer object failed"; + case hipErrorArrayIsMapped: + return "array is mapped"; + case hipErrorAlreadyMapped: + return "resource already mapped"; + case hipErrorNoBinaryForGpu: + return "no kernel image is available for execution on the device"; + case hipErrorAlreadyAcquired: + return "resource already acquired"; + case hipErrorNotMapped: + return "resource not mapped"; + case hipErrorNotMappedAsArray: + return "resource not mapped as array"; + case hipErrorNotMappedAsPointer: + return "resource not mapped as pointer"; + case hipErrorECCNotCorrectable: + return "uncorrectable ECC error encountered"; + case hipErrorUnsupportedLimit: + return "limit is not supported on this architecture"; + case hipErrorContextAlreadyInUse: + return "exclusive-thread device already in use by a different thread"; + case hipErrorPeerAccessUnsupported: + return "peer access is not supported between these two devices"; + case hipErrorInvalidKernelFile: + #if HT_AMD + return "invalid kernel file"; + #elif HT_NVIDIA + return "a PTX JIT compilation failed"; + #endif + case hipErrorInvalidGraphicsContext: + return "invalid OpenGL or DirectX context"; + case hipErrorInvalidSource: + return "device kernel image is invalid"; + case hipErrorFileNotFound: + return "file not found"; + case hipErrorSharedObjectSymbolNotFound: + return "shared object symbol not found"; + case hipErrorSharedObjectInitFailed: + return "shared object initialization failed"; + case hipErrorOperatingSystem: + return "OS call failed or operation not supported on this OS"; + case hipErrorInvalidHandle: + return "invalid resource handle"; + case hipErrorIllegalState: + return "the operation cannot be performed in the present state"; + case hipErrorNotFound: + return "named symbol not found"; + case hipErrorNotReady: + return "device not ready"; + case hipErrorIllegalAddress: + return "an illegal memory access was encountered"; + case hipErrorLaunchOutOfResources: + return "too many resources requested for launch"; + case hipErrorLaunchTimeOut: + return "the launch timed out and was terminated"; + case hipErrorPeerAccessAlreadyEnabled: + return "peer access is already enabled"; + case hipErrorPeerAccessNotEnabled: + return "peer access has not been enabled"; + case hipErrorSetOnActiveProcess: + return "cannot set while device is active in this process"; + case hipErrorContextIsDestroyed: + return "context is destroyed"; + case hipErrorAssert: + return "device-side assert triggered"; + case hipErrorHostMemoryAlreadyRegistered: + return "part or all of the requested memory range is already mapped"; + case hipErrorHostMemoryNotRegistered: + return "pointer does not correspond to a registered memory region"; + case hipErrorLaunchFailure: + return "unspecified launch failure"; + case hipErrorCooperativeLaunchTooLarge: + return "too many blocks in cooperative launch"; + case hipErrorNotSupported: + return "operation not supported"; + case hipErrorStreamCaptureUnsupported: + return "operation not permitted when stream is capturing"; + case hipErrorStreamCaptureInvalidated: + return "operation failed due to a previous error during capture"; + case hipErrorStreamCaptureMerge: + return "operation would result in a merge of separate capture sequences"; + case hipErrorStreamCaptureUnmatched: + return "capture was not ended in the same stream as it began"; + case hipErrorStreamCaptureUnjoined: + return "capturing stream has unjoined work"; + case hipErrorStreamCaptureIsolation: + return "dependency created on uncaptured work in another stream"; + case hipErrorStreamCaptureImplicit: + return "operation would make the legacy stream depend on a capturing blocking stream"; //NOLINT + case hipErrorCapturedEvent: + return "operation not permitted on an event last recorded in a capturing stream"; //NOLINT + case hipErrorStreamCaptureWrongThread: + return "attempt to terminate a thread-local capture sequence from another thread"; //NOLINT + case hipErrorGraphExecUpdateFailure: + return "the graph update was not performed because it included changes which violated constraints specific to instantiated graph update"; //NOLINT + case hipErrorRuntimeMemory: + return "runtime memory call returned error"; + case hipErrorRuntimeOther: + return "runtime call other than memory returned error"; + case hipErrorUnknown: + default: + #if HT_AMD + return "unknown error"; + #elif HT_NVIDIA + return "unknown error"; + #endif + } +} + +// Test case to verify the returned error string is +// same as generated error string. + +TEST_CASE("Unit_hipDrvGetErrorString_Functional") { + const char* error_string = nullptr; + const auto enumerator = + GENERATE(from_range(std::begin(kErrorEnumerators), + std::end(kErrorEnumerators))); + hipError_t error_ret = hipDrvGetErrorString(enumerator, &error_string); + REQUIRE(error_string != nullptr); + REQUIRE(strcmp(error_string, ErrorString(enumerator)) == 0); + REQUIRE(error_ret == hipSuccess); +} + +// Negative test cases. + +TEST_CASE("Unit_hipDrvGetErrorString_Negative") { + const char* error_string = nullptr; + SECTION("pass unknown value to hipError") { + REQUIRE((hipDrvGetErrorString(static_cast(-1), &error_string)) + == hipErrorInvalidValue); + } + #if HT_AMD + SECTION("pass nullptr to error string") { + REQUIRE((hipDrvGetErrorString(static_cast(0), nullptr)) + == hipErrorInvalidValue); + } + #endif +} diff --git a/catch/unit/graph/CMakeLists.txt b/catch/unit/graph/CMakeLists.txt index 5a93f0c83e..57c7ecb4eb 100644 --- a/catch/unit/graph/CMakeLists.txt +++ b/catch/unit/graph/CMakeLists.txt @@ -96,6 +96,10 @@ set(TEST_SRC hipLaunchHostFunc.cc hipStreamGetCaptureInfo_v2_old.cc hipUserObjectCreate.cc + hipUserObjectRelease.cc + hipUserObjectRetain.cc + hipGraphReleaseUserObject.cc + hipGraphRetainUserObject.cc hipGraphDebugDotPrint.cc hipGraphCloneComplx.cc hipGraphNodeSetEnabled.cc @@ -105,8 +109,21 @@ set(TEST_SRC hipGraphExecDestroy.cc hipGraphUpload.cc hipGraphKernelNodeCopyAttributes.cc + hipGraphKernelNodeGetAttribute.cc ) +if(HIP_PLATFORM MATCHES "amd") + set(AMD_SRC + hipStreamCaptureExtModuleLaunchKernel.cc + ) + set(TEST_SRC ${TEST_SRC} ${AMD_SRC}) +endif() + hip_add_exe_to_target(NAME GraphsTest TEST_SRC ${TEST_SRC} TEST_TARGET_NAME build_tests) + +if(HIP_PLATFORM MATCHES "amd") + add_custom_target(hipMatMul COMMAND ${CMAKE_CXX_COMPILER} --genco ${OFFLOAD_ARCH_STR} ${CMAKE_CURRENT_SOURCE_DIR}/hipMatMul.cc -o ${CMAKE_CURRENT_BINARY_DIR}/../../unit/graph/hipMatMul.code -I${CMAKE_CURRENT_SOURCE_DIR}/../../../../include/ -I${CMAKE_CURRENT_SOURCE_DIR}/../../include --rocm-path=${ROCM_PATH}) + add_dependencies(build_tests hipMatMul) +endif() diff --git a/catch/unit/graph/hipGraphAddEmptyNode.cc b/catch/unit/graph/hipGraphAddEmptyNode.cc index b21315dd2a..23d477f595 100644 --- a/catch/unit/graph/hipGraphAddEmptyNode.cc +++ b/catch/unit/graph/hipGraphAddEmptyNode.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -21,10 +21,18 @@ THE SOFTWARE. Testcase Scenarios : 1) Create and add empty node to graph and verify addition is successful. 2) Negative Scenarios + 3) Functional Test to use empty node as barrier to wait for multiple nodes. + In a graph add 3 independent memcpy_h2d nodes, add an empty node with + dependencies on the 3 memcpy_h2d nodes, add 3 independent kernel nodes, + add another empty node with dependencies on the 3 kernel nodes and + add 3 independent memcpy_d2h nodes with dependencies on previous empty + node. Execute the graph and validate the results. */ - +#include #include +#include +#define TEST_LOOP_SIZE 50 /** * Functional Test to add empty node with dependencies */ @@ -90,7 +98,7 @@ TEST_CASE("Unit_hipGraphAddEmptyNode_NegTest") { dependencies.data(), dependencies.size())); } // pDependencies is nullptr - SECTION("Null Graph") { + SECTION("Dependencies is null") { REQUIRE(hipErrorInvalidValue == hipGraphAddEmptyNode(&emptyNode, graph, nullptr, dependencies.size())); } @@ -98,3 +106,148 @@ TEST_CASE("Unit_hipGraphAddEmptyNode_NegTest") { HIP_CHECK(hipFree(pOutBuff_d)); HIP_CHECK(hipGraphDestroy(graph)); } + +// Function to fill input data +static void fillRandInpData(int *A1_h, int *A2_h, int *A3_h, size_t N) { + unsigned int seed = time(nullptr); + for (size_t i = 0; i < N; i++) { + A1_h[i] = (HipTest::RAND_R(&seed) & 0xFF); + A2_h[i] = (HipTest::RAND_R(&seed) & 0xFF); + A3_h[i] = (HipTest::RAND_R(&seed) & 0xFF); + } +} + +// Function to validate result +static void validateOutData(int *A1_h, int *A2_h, size_t N) { + for (size_t i = 0; i < N; i++) { + int result = (A1_h[i]*A1_h[i]); + REQUIRE(result == A2_h[i]); + } +} +/** + * Functional Test to use empty node as barrier to wait for multiple nodes. + */ +TEST_CASE("Unit_hipGraphAddEmptyNode_BarrierFunc") { + size_t size = 1024; + constexpr auto blocksPerCU = 6; + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, + threadsPerBlock, size); + hipGraph_t graph; + std::vector nodeDependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + int *inputVec_d1{nullptr}, *inputVec_h1{nullptr}, *outputVec_h1{nullptr}, + *outputVec_d1{nullptr}; + int *inputVec_d2{nullptr}, *inputVec_h2{nullptr}, *outputVec_h2{nullptr}, + *outputVec_d2{nullptr}; + int *inputVec_d3{nullptr}, *inputVec_h3{nullptr}, *outputVec_h3{nullptr}, + *outputVec_d3{nullptr}; + // host and device allocation + HipTest::initArrays(&inputVec_d1, &outputVec_d1, nullptr, + &inputVec_h1, &outputVec_h1, nullptr, size, false); + HipTest::initArrays(&inputVec_d2, &outputVec_d2, nullptr, + &inputVec_h2, &outputVec_h2, nullptr, size, false); + HipTest::initArrays(&inputVec_d3, &outputVec_d3, nullptr, + &inputVec_h3, &outputVec_h3, nullptr, size, false); + // add nodes to graph + hipGraphNode_t memcpyH2D_1, memcpyH2D_2, memcpyH2D_3; + hipGraphNode_t vecSqr1, vecSqr2, vecSqr3; + hipGraphNode_t memcpyD2H_1, memcpyD2H_2, memcpyD2H_3; + hipGraphNode_t emptyNode1, emptyNode2; + // Create memcpy h2d nodes + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_1, graph, nullptr, + 0, inputVec_d1, inputVec_h1, (sizeof(int)*size), hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_2, graph, nullptr, + 0, inputVec_d2, inputVec_h2, (sizeof(int)*size), hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_3, graph, nullptr, + 0, inputVec_d3, inputVec_h3, (sizeof(int)*size), hipMemcpyHostToDevice)); + // Create dependency list + nodeDependencies.push_back(memcpyH2D_1); + nodeDependencies.push_back(memcpyH2D_2); + nodeDependencies.push_back(memcpyH2D_3); + // Create emptyNode and add it to graph with dependency + HIP_CHECK(hipGraphAddEmptyNode(&emptyNode1, graph, nodeDependencies.data(), + nodeDependencies.size())); + nodeDependencies.clear(); + nodeDependencies.push_back(emptyNode1); + // Creating kernel nodes + hipKernelNodeParams kerNodeParams1{}, kerNodeParams2{}, kerNodeParams3{}; + void* kernelArgs1[] = {reinterpret_cast(&inputVec_d1), + reinterpret_cast(&outputVec_d1), + reinterpret_cast(&size)}; + kerNodeParams1.func = reinterpret_cast(HipTest::vector_square); + kerNodeParams1.gridDim = dim3(blocks); + kerNodeParams1.blockDim = dim3(threadsPerBlock); + kerNodeParams1.sharedMemBytes = 0; + kerNodeParams1.kernelParams = reinterpret_cast(kernelArgs1); + kerNodeParams1.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&vecSqr1, graph, nodeDependencies.data(), + nodeDependencies.size(), &kerNodeParams1)); + void* kernelArgs2[] = {reinterpret_cast(&inputVec_d2), + reinterpret_cast(&outputVec_d2), + reinterpret_cast(&size)}; + kerNodeParams2.func = reinterpret_cast(HipTest::vector_square); + kerNodeParams2.gridDim = dim3(blocks); + kerNodeParams2.blockDim = dim3(threadsPerBlock); + kerNodeParams2.sharedMemBytes = 0; + kerNodeParams2.kernelParams = reinterpret_cast(kernelArgs2); + kerNodeParams2.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&vecSqr2, graph, nodeDependencies.data(), + nodeDependencies.size(), &kerNodeParams2)); + void* kernelArgs3[] = {reinterpret_cast(&inputVec_d3), + reinterpret_cast(&outputVec_d3), + reinterpret_cast(&size)}; + kerNodeParams3.func = reinterpret_cast(HipTest::vector_square); + kerNodeParams3.gridDim = dim3(blocks); + kerNodeParams3.blockDim = dim3(threadsPerBlock); + kerNodeParams3.sharedMemBytes = 0; + kerNodeParams3.kernelParams = reinterpret_cast(kernelArgs3); + kerNodeParams3.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&vecSqr3, graph, nodeDependencies.data(), + nodeDependencies.size(), &kerNodeParams3)); + nodeDependencies.clear(); + nodeDependencies.push_back(vecSqr1); + nodeDependencies.push_back(vecSqr2); + nodeDependencies.push_back(vecSqr3); + // Create emptyNode and add it to graph with dependency + HIP_CHECK(hipGraphAddEmptyNode(&emptyNode2, graph, nodeDependencies.data(), + nodeDependencies.size())); + nodeDependencies.clear(); + nodeDependencies.push_back(emptyNode2); + // Create memcpy d2h nodes + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_1, graph, + nodeDependencies.data(), nodeDependencies.size(), outputVec_h1, + outputVec_d1, (sizeof(int)*size), hipMemcpyDeviceToHost)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_2, graph, + nodeDependencies.data(), nodeDependencies.size(), outputVec_h2, + outputVec_d2, (sizeof(int)*size), hipMemcpyDeviceToHost)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_3, graph, + nodeDependencies.data(), nodeDependencies.size(), outputVec_h3, + outputVec_d3, (sizeof(int)*size), hipMemcpyDeviceToHost)); + nodeDependencies.clear(); + // Create executable graph + hipStream_t streamForGraph; + hipGraphExec_t graphExec{nullptr}; + HIP_CHECK(hipStreamCreate(&streamForGraph)); + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, + nullptr, 0)); + // Execute graph + for (int iter = 0; iter < TEST_LOOP_SIZE; iter++) { + fillRandInpData(inputVec_h1, inputVec_h2, inputVec_h3, size); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + validateOutData(inputVec_h1, outputVec_h1, size); + validateOutData(inputVec_h2, outputVec_h2, size); + validateOutData(inputVec_h3, outputVec_h3, size); + } + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + // Free + HipTest::freeArrays(inputVec_d1, outputVec_d1, nullptr, + inputVec_h1, outputVec_h1, nullptr, false); + HipTest::freeArrays(inputVec_d2, outputVec_d2, nullptr, + inputVec_h2, outputVec_h2, nullptr, false); + HipTest::freeArrays(inputVec_d3, outputVec_d3, nullptr, + inputVec_h3, outputVec_h3, nullptr, false); + HIP_CHECK(hipGraphDestroy(graph)); +} diff --git a/catch/unit/graph/hipGraphAddMemcpyNode.cc b/catch/unit/graph/hipGraphAddMemcpyNode.cc index 5612f998ef..745b29d41a 100644 --- a/catch/unit/graph/hipGraphAddMemcpyNode.cc +++ b/catch/unit/graph/hipGraphAddMemcpyNode.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -48,10 +48,15 @@ Testcase Scenarios : Functional Memcpy nodes are added and assigned to Peer device. 4) Perform memcpy operation for 1D, 2D and 3D arrays on Peer device and verify the results. +5) Create two host pointers, copy the data between them by the api + hipGraphAddMemcpyNode with data transfer kind hipMemcpyHostToHost. + Validate the output. */ #include #include +#include +#include #define ZSIZE 32 #define YSIZE 32 @@ -517,4 +522,49 @@ TEST_CASE("Unit_hipGraphAddMemcpyNode_PeerAccessFunctional") { validateMemcpyNode1DArray(true); } } +/* +* Create two host pointers, copy the data between them by the api +* hipGraphAddMemcpyNode with data transfer kind hipMemcpyHostToHost. +* Validate the output. +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNode_HostToHost") { + constexpr size_t size = 1024; + size_t numW = size * sizeof(int); + // Host Vectors + std::vector A_h(numW); + std::vector B_h(numW); + // Initialization + std::iota(A_h.begin(), A_h.end(), 0); + std::fill_n(B_h.begin(), size, 0); + hipGraph_t graph; + hipStream_t streamForGraph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyH2H; + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipStreamCreate(&streamForGraph)); + + hipMemcpy3DParms myparms{}; + myparms.srcPos = make_hipPos(0, 0, 0); + myparms.dstPos = make_hipPos(0, 0, 0); + myparms.srcPtr = make_hipPitchedPtr(A_h.data(), numW, numW, 1); + myparms.dstPtr = make_hipPitchedPtr(B_h.data(), numW, numW, 1); + myparms.extent = make_hipExtent(numW, 1, 1); + myparms.kind = hipMemcpyHostToHost; + + // Host to Host + HIP_CHECK(hipGraphAddMemcpyNode(&memcpyH2H, graph, nullptr, + 0, &myparms)); + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + + // Validation + REQUIRE(memcmp(A_h.data(), B_h.data(), numW) == 0); +} diff --git a/catch/unit/graph/hipGraphAddMemcpyNode1D.cc b/catch/unit/graph/hipGraphAddMemcpyNode1D.cc index e68eac22cf..9ed4a9fbcd 100644 --- a/catch/unit/graph/hipGraphAddMemcpyNode1D.cc +++ b/catch/unit/graph/hipGraphAddMemcpyNode1D.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -24,6 +24,9 @@ Functional - Memcpy nodes are added and assigned to default device. 2) Allocate memory on default device(Dev 0), Perform memcpy operation for 1D arrays on Peer device(Dev 1) and verify the results. +3) Create two host pointers, copy the data between them by the api hipGraphAddMemcpyNode1D with data transfer + kind hipMemcpyHostToHost. Validate the output. + Negative - 1) Pass pGraphNode as nullptr and check if api returns error. 2) When graph is un-initialized argument(skipping graph creation), api should return error code. @@ -38,7 +41,8 @@ Negative - #include #include - +#include +#include static void validateMemcpyNode1DArray(bool peerAccess) { constexpr int SIZE{32}; @@ -197,46 +201,42 @@ TEST_CASE("Unit_hipGraphAddMemcpyNode1D_Negative") { HIP_CHECK(hipFree(A_h)); HIP_CHECK(hipGraphDestroy(graph)); } +/* + * Create two host pointers, copy the data between them by the api + * hipGraphAddMemcpyNode1D with data transfer kind hipMemcpyHostToHost. + * Validate the output. +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNode1D_HostToHost") { + constexpr size_t size = 1024; + size_t numBytes{size * sizeof(int)}; -TEST_CASE("Unit_hipGraphAddMemcpyNode1D_Negative_Basic") { - constexpr size_t N = 1024; - constexpr size_t Nbytes = N * sizeof(int); - int *A_d, *B_d, *C_d, *A_h, *B_h, *C_h; + // Host Vectors + std::vector A_h(size); + std::vector B_h(size); + // Initialization + std::iota(A_h.begin(), A_h.end(), 0); + std::fill_n(B_h.begin(), size, 0); - hipGraphNode_t memcpy_A, memcpy_B, memcpy_C; - hipError_t ret; hipGraph_t graph; - - HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); - + hipStream_t streamForGraph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyH2H; HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipStreamCreate(&streamForGraph)); - // Pass memcpy direction as hipMemcpyDeviceToHost and - // source pointer as host and destination pointer as device pointer - ret = hipGraphAddMemcpyNode1D(&memcpy_B, graph, nullptr, 0, B_d, B_h, - Nbytes, hipMemcpyDeviceToHost); - REQUIRE(hipErrorInvalidValue == ret); + // Host to Host + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2H, graph, nullptr, 0, + B_h.data(), A_h.data(), numBytes, hipMemcpyHostToHost)); - // Pass memcpy direction as hipMemcpyHostToDevice and - // source pointer as device and destination pointer as host pointer - ret = hipGraphAddMemcpyNode1D(&memcpy_C, graph, nullptr, 0, C_h, C_d, - Nbytes, hipMemcpyHostToDevice); - REQUIRE(hipErrorInvalidValue == ret); + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); - // Pass memcpy direction as hipMemcpyDeviceToDevice and - // pass source pointer as device and destination pointer as host pointer - ret = hipGraphAddMemcpyNode1D(&memcpy_C, graph, nullptr, 0, C_h, C_d, - Nbytes, hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - - // Pass memcpy direction as hipMemcpyDeviceToDevice and - // pass source pointer as host and destination pointer as device pointer - ret = hipGraphAddMemcpyNode1D(&memcpy_C, graph, nullptr, 0, C_d, C_h, - Nbytes, hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + + // Validation + REQUIRE(std::equal(A_h.begin(), A_h.end(), B_h.begin(), B_h.end())); } diff --git a/catch/unit/graph/hipGraphDebugDotPrint.cc b/catch/unit/graph/hipGraphDebugDotPrint.cc index d0320dd4e0..d3d559cbd2 100644 --- a/catch/unit/graph/hipGraphDebugDotPrint.cc +++ b/catch/unit/graph/hipGraphDebugDotPrint.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -17,6 +17,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/** +* @addtogroup hipGraphDebugDotPrint hipGraphDebugDotPrint +* @{ +* @ingroup GraphTest +* `hipGraphDebugDotPrint(hipGraph_t graph, const char* path, unsigned int flags)` - +* Write a DOT file describing graph structure. +*/ + #include #include #include @@ -30,6 +38,13 @@ THE SOFTWARE. #include #include +__device__ int globalIn[N]; + +static void callbackfunc(void *A_h) { + int *A = reinterpret_cast(A_h); + std::iota(A, A+N, 0); +} + static void deleteFile(const char* fName) { if ( remove(fName) != 0 ) { INFO("Error in deleting file -" << fName); @@ -42,8 +57,9 @@ static bool checkFileExists(const char* fName) { return (access(fName, F_OK) != -1); } -static int countSubstr(std::string input_str, std::string substr) { - int count = 0; +static unsigned countSubstr(const std::string &input_str, + const std::string &substr) { + unsigned count = 0; std::string::size_type srch_pos = 0, cur_pos = 0; while ((cur_pos = input_str.find(substr, srch_pos)) != std::string::npos) { count++; @@ -53,35 +69,65 @@ static int countSubstr(std::string input_str, std::string substr) { } static bool validateDotFile(const char* fName, - std::map *graphData) { - bool isTestPassed = true; + const std::map &graphData) { std::ifstream infile(fName); std::stringstream buffer; buffer << infile.rdbuf(); - std::map::iterator it; - for (it = (*graphData).begin(); it != (*graphData).end(); it++) { - if (it->second != countSubstr(buffer.str(), it->first)) { - isTestPassed = false; - break; + const std::string buffer_str = buffer.str(); + for (auto it = graphData.begin(); it != graphData.end(); it++) { + unsigned count = countSubstr(buffer_str, it->first); + if (it->second != count) { + INFO("validateDotFile: Failed for key :: " << it->first << + " : " << count << " Expected : " << it->second); + return false; } } - return isTestPassed; + return true; } +/** +* Test Description +* ------------------------ +*  - Functional Test for API - hipGraphDebugDotPrint +* Call hipGraphDebugDotPrint and provice path where to write the DOT file. +* Verify that DOT file get created or not for each flag passed. +* 1) Add MemcpyNode node to graph & validate its DebugDotPrint descriptions +* 2) Add kernel node to graph & validate its DebugDotPrint descriptions +* 3) Add memset node to graph & validate its DebugDotPrint descriptions +* 4) Add emptyNode to graph & validate its DebugDotPrint descriptions +* 5) Add childGraphNode to graph & validate its DebugDotPrint descriptions +* 6) Add eventRecord to graph & validate its DebugDotPrint descriptions +* 7) Add eventWait to graph & validate its DebugDotPrint descriptions +* 8) Add hostNode to graph & validate its DebugDotPrint descriptions +* 9) Add mecpyNode1D to graph & validate its DebugDotPrint descriptions +* 10) Add mecpyNode3D to graph & validate its DebugDotPrint descriptions +* 11) Add MemcpyNodeToSymbol to graph & validate its DebugDotPrint descriptions +* 12) Add MemcpyNodeFromSymbol to graph & validate its DebugDotPrint descriptions +* 13) Add Dependencies to graph & validate its DebugDotPrint descriptions +* Test source +* ------------------------ +*  - /unit/graph/hipGraphDebugDotPrint.cc +* Test requirements +* ------------------------ +*  - HIP_VERSION >= 5.6 +*/ + static void hipGraphDebugDotPrint_Functional(const char* fName, unsigned int flag = 0) { constexpr size_t Nbytes = N * sizeof(int); constexpr auto blocksPerCU = 6; // to hide latency constexpr auto threadsPerBlock = 256; - hipGraph_t graph; + hipGraph_t graph, childGraph; hipStream_t stream; hipGraphNode_t memcpy_A, memcpy_B, memcpy_C, kNodeAdd, memsetNode; + hipGraphNode_t emptyNode, childGraphNode, eventWait, eventRecord, hostNode; hipKernelNodeParams kNodeParams{}; int *A_d, *B_d, *C_d, *mem_d; - int *A_h, *B_h, *C_h; + int *A_h, *B_h, *C_h, *mem_h; hipGraphExec_t graphExec; size_t NElem{N}; + mem_h = reinterpret_cast(malloc(Nbytes)); HIP_CHECK(hipMalloc(&mem_d, Nbytes)); HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); @@ -100,15 +146,88 @@ static void hipGraphDebugDotPrint_Functional(const char* fName, kNodeParams.sharedMemBytes = 0; kNodeParams.kernelParams = reinterpret_cast(kernelArgs); kNodeParams.extra = nullptr; + + // Add Kernel node to graph & validate its DebugDotPrint descriptions HIP_CHECK(hipGraphAddKernelNode(&kNodeAdd, graph, nullptr, 0, &kNodeParams)); + // Add MemCpy node to graph & validate its DebugDotPrint descriptions HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_C, graph, nullptr, 0, C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); + // Add Dependencies to graph & validate its DebugDotPrint descriptions HIP_CHECK(hipGraphAddDependencies(graph, &memcpy_A, &kNodeAdd, 1)); HIP_CHECK(hipGraphAddDependencies(graph, &memcpy_B, &kNodeAdd, 1)); HIP_CHECK(hipGraphAddDependencies(graph, &kNodeAdd, &memcpy_C, 1)); + // Add emptyNode to graph & validate its DebugDotPrint descriptions + HIP_CHECK(hipGraphAddEmptyNode(&emptyNode, graph, NULL, 0)); + + // Add hostNode to graph & validate its DebugDotPrint descriptions + hipHostNodeParams hostParams = {0, 0}; + hostParams.fn = callbackfunc; + hostParams.userData = mem_h; + HIP_CHECK(hipGraphAddHostNode(&hostNode, graph, nullptr, 0, &hostParams)); + + hipEvent_t event; + HIP_CHECK(hipEventCreate(&event)); + + // Add eventRecord to graph & validate its DebugDotPrint descriptions + HIP_CHECK(hipGraphAddEventRecordNode(&eventRecord, graph, nullptr, + 0, event)); + + // Add eventWait to graph & validate its DebugDotPrint descriptions + HIP_CHECK(hipGraphAddEventWaitNode(&eventWait, graph, nullptr, 0, event)); + + HIP_CHECK(hipGraphCreate(&childGraph, 0)); + + // Add emcpyNode3D to graph & validate its DebugDotPrint descriptions + constexpr int width{10}, height{10}, depth{10}; + hipArray *devArray1; + hipChannelFormatKind formatKind = hipChannelFormatKindSigned; + hipMemcpy3DParms myparams; + uint32_t size = width * height * depth * sizeof(int); + hipGraphNode_t mcpyNode3D; + int *hData = reinterpret_cast(malloc(size)); + REQUIRE(hData != nullptr); + + // Initialize host buffer + for (int i = 0; i < depth; i++) { + for (int j = 0; j < height; j++) { + for (int k = 0; k < width; k++) { + hData[i*width*height + j*width + k] = i*width*height + j*width + k; + } + } + } + + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(sizeof(int)*8, + 0, 0, 0, formatKind); + HIP_CHECK(hipMalloc3DArray(&devArray1, &channelDesc, + make_hipExtent(width, height, depth), hipArrayDefault)); + + memset(&myparams, 0x0, sizeof(hipMemcpy3DParms)); + myparams.srcPos = make_hipPos(0, 0, 0); + myparams.dstPos = make_hipPos(0, 0, 0); + myparams.extent = make_hipExtent(width , height, depth); + myparams.srcPtr = make_hipPitchedPtr(hData, width * sizeof(int), + width, height); + myparams.dstArray = devArray1; + myparams.kind = hipMemcpyHostToDevice; + HIP_CHECK(hipGraphAddMemcpyNode(&mcpyNode3D, childGraph, + nullptr, 0, &myparams)); + + // Add MemcpyNodeToSymbol to graph & validate its DebugDotPrint description + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode; + + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, childGraph, + nullptr, 0, HIP_SYMBOL(globalIn), + B_h, Nbytes, 0, hipMemcpyHostToDevice)); + + // Add MemcpyNodeFromSymbol to graph & validate its DebugDotPrint description + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, childGraph, + nullptr, 0, B_h, HIP_SYMBOL(globalIn), + Nbytes, 0, hipMemcpyDeviceToHost)); + + // Add memset node to graph & validate its DebugDotPrint descriptions hipMemsetParams memsetParams{}; memset(&memsetParams, 0, sizeof(memsetParams)); memsetParams.dst = reinterpret_cast(mem_d); @@ -117,20 +236,42 @@ static void hipGraphDebugDotPrint_Functional(const char* fName, memsetParams.elementSize = sizeof(char); memsetParams.width = Nbytes; memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, + HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, childGraph, nullptr, 0, &memsetParams)); - std::map graphData; - graphData["->"] = 3; // number of edges - graphData["MEMCPY"] = 3; - graphData["HtoD"] = 2; - graphData["DtoH"] = 1; + // Add childGraphNode to graph & validate its DebugDotPrint descriptions + HIP_CHECK(hipGraphAddChildGraphNode(&childGraphNode, graph, + nullptr, 0, childGraph)); + + std::map graphData; + graphData["->"] = 3; // number of edges + graphData["MEMCPY"] = 6; + graphData["HtoA"] = 1; + graphData["HtoD"] = 3; + graphData["DtoH"] = 2; graphData["MEMSET"] = 1; - if ( flag == hipGraphDebugDotFlagsVerbose ) graphData["KERNEL"] = 1; + graphData["EMPTY"] = 1; + graphData["EVENT_WAIT"] = 1; + graphData["EVENT_RECORD"] = 1; + graphData["subgraph"] = 2; + graphData["HOST"] = 1; + +#if HT_NVIDIA + if ( flag == hipGraphDebugDotFlagsVerbose || + flag == hipGraphDebugDotFlagsMemcpyNodeParams ) { + graphData["HOST"] = 7; + } +#endif + + if ( flag == hipGraphDebugDotFlagsVerbose || + flag == hipGraphDebugDotFlagsKernelNodeAttributes + ) { + graphData["KERNEL"] = 1; + } HIP_CHECK(hipGraphDebugDotPrint(graph, fName, flag)); REQUIRE(true == checkFileExists(fName)); - REQUIRE(true == validateDotFile(fName, &graphData)); + REQUIRE(true == validateDotFile(fName, graphData)); deleteFile(fName); HIP_CHECK(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0)); @@ -140,10 +281,12 @@ static void hipGraphDebugDotPrint_Functional(const char* fName, // Verify graph execution result HipTest::checkVectorADD(A_h, B_h, C_h, N); + free(mem_h); HIP_CHECK(hipFree(mem_d)); HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); HIP_CHECK(hipGraphExecDestroy(graphExec)); HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipGraphDestroy(childGraph)); HIP_CHECK(hipStreamDestroy(stream)); } @@ -189,17 +332,24 @@ TEST_CASE("Unit_hipGraphDebugDotPrint_Functional") { hipGraphDebugDotFlagsKernelNodeAttributes); } } -#endif // __linux__ /** -* Negative Test for API - hipGraphDebugDotPrint Argument Check -1) Pass graph as nullptr. -2) Pass graph as uninitialize structure -3) Pass path for dot file to store as nullptr -4) Pass path for dot file to store as empth path -5) Pass flag as hipGraphDebugDotFlags MIN - 1 -6) Pass flag as hipGraphDebugDotFlags MAX + 1 -7) Pass flag as INT_MAX +* Test Description +* ------------------------ +*  - Negative Test for API - hipGraphDebugDotPrint Argument Check +* 1) Pass graph as nullptr +* 2) Pass graph as uninitialize structure +* 3) Pass path for dot file to store as nullptr +* 4) Pass path for dot file to store as empth path +* 5) Pass flag as hipGraphDebugDotFlags MIN - 1 +* 6) Pass flag as hipGraphDebugDotFlags MAX + 1 +* 7) Pass flag as INT_MAX +* Test source +* ------------------------ +*  - /unit/graph/hipGraphDebugDotPrint.cc +* Test requirements +* ------------------------ +*  - HIP_VERSION >= 5.6 */ #define DOT_FILE_PATH_NEG "./graphDotFileNeg.dot" @@ -241,6 +391,7 @@ TEST_CASE("Unit_hipGraphDebugDotPrint_Argument_Check") { ret = hipGraphDebugDotPrint(graph, DOT_FILE_PATH_NEG, INT_MAX); REQUIRE(hipSuccess == ret); } + deleteFile(DOT_FILE_PATH_NEG); HIP_CHECK(hipGraphDestroy(graph)); } - +#endif // __linux__ diff --git a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc index 03fa33636c..8f31fdc0cf 100644 --- a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc +++ b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc @@ -184,17 +184,15 @@ TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Negative_Parameters") } #endif - SECTION("Changing dst allocation device") { - if (HipTest::getDeviceCount() < 2) { - HipTest::HIP_SKIP_TEST("Test requires two connected GPUs"); - return; - } + if (HipTest::getDeviceCount() >= 2) { + SECTION("Changing dst allocation device") { HIP_CHECK(hipSetDevice(1)); LinearAllocGuard new_var(LinearAllocs::hipMalloc, sizeof(int)); HIP_CHECK_ERROR(hipGraphExecMemcpyNodeSetParamsFromSymbol( graph_exec, node, new_var.ptr(), SYMBOL(int_device_var), - sizeof(*new_var.ptr()), 0, static_cast(-1)), + sizeof(*new_var.ptr()), 0, hipMemcpyDeviceToDevice), hipErrorInvalidValue); + } } HIP_CHECK(hipGraphExecDestroy(graph_exec)); diff --git a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc index daa0c09b1b..585435e684 100644 --- a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc +++ b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc @@ -186,7 +186,7 @@ TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Negative_Parameters") { } HIP_CHECK(hipSetDevice(1)); LinearAllocGuard new_var(LinearAllocs::hipMalloc, sizeof(int)); - HIP_CHECK_ERROR(hipGraphExecMemcpyNodeSetParamsFromSymbol( + HIP_CHECK_ERROR(hipGraphExecMemcpyNodeSetParamsToSymbol( graph_exec, node, SYMBOL(int_device_var), new_var.ptr(), sizeof(*new_var.ptr()), 0, static_cast(-1)), hipErrorInvalidValue); diff --git a/catch/unit/graph/hipGraphKernelNodeCopyAttributes.cc b/catch/unit/graph/hipGraphKernelNodeCopyAttributes.cc index 7ebd9953ff..d9079c2ec3 100644 --- a/catch/unit/graph/hipGraphKernelNodeCopyAttributes.cc +++ b/catch/unit/graph/hipGraphKernelNodeCopyAttributes.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -22,10 +22,42 @@ THE SOFTWARE. #include /** - * Functional Test for API - hipGraphKernelNodeCopyAttributes - - Create graph with 2 kernel node. - Copy the attribute from 1st kernel node to 2nd kernel node. - */ +* @addtogroup hipGraphKernelNodeCopyAttributes hipGraphKernelNodeCopyAttributes +* @{ +* @ingroup GraphTest +* `hipGraphKernelNodeCopyAttributes(hipGraphNode_t hSrc, hipGraphNode_t hDst)` - +* Copies attributes from source node to destination node. +*/ + +/** +* Test Description +* ------------------------ +*  - Functional Test for API - hipGraphKernelNodeCopyAttributes +* Create graph with kernel node and add its- functionality. +* 1) Copy kernelNodeAttribute to same graph kernel node. +* 2) Copy kernelNodeAttribute to different graph kernel node. +* 3) Copy kernelNodeAttribute to cloned graph kernel node. +* 4) Copy kernelNodeAttribute to child graph kernel node. +* Test source +* ------------------------ +*  - /unit/graph/hipGraphKernelNodeCopyAttributes.cc +* Test requirements +* ------------------------ +*  - HIP_VERSION >= 5.6 +*/ + +static bool validateKernelNodeAttrValue(hipKernelNodeAttrValue in, + hipKernelNodeAttrValue out) { + if ((in.accessPolicyWindow.base_ptr != out.accessPolicyWindow.base_ptr) || + (in.accessPolicyWindow.hitProp != out.accessPolicyWindow.hitProp) || + (in.accessPolicyWindow.hitRatio != out.accessPolicyWindow.hitRatio) || + (in.accessPolicyWindow.missProp != out.accessPolicyWindow.missProp) || + (in.accessPolicyWindow.num_bytes != out.accessPolicyWindow.num_bytes) || + (in.cooperative != out.cooperative)) { + return false; + } + return true; +} TEST_CASE("Unit_hipGraphKernelNodeCopyAttributes_Functional") { constexpr size_t N = 1024; @@ -33,26 +65,25 @@ TEST_CASE("Unit_hipGraphKernelNodeCopyAttributes_Functional") { constexpr auto blocksPerCU = 6; // to hide latency constexpr auto threadsPerBlock = 256; hipGraph_t graph; - hipGraphNode_t memcpyNode, kNode; + hipGraphExec_t graphExec; + hipGraphNode_t memcpy_A, memcpy_B, memcpy_C, kernel_vecAdd; hipKernelNodeParams kNodeParams{}; hipStream_t stream; int *A_d, *B_d, *C_d; int *A_h, *B_h, *C_h; - std::vector dependencies; - hipGraphExec_t graphExec; size_t NElem{N}; - HIP_CHECK(hipStreamCreate(&stream)); HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyNode); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, nullptr, 0, B_d, B_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyNode); + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_B, graph, nullptr, 0, B_d, B_h, + Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_C, graph, nullptr, 0, C_h, C_d, + Nbytes, hipMemcpyDeviceToHost)); void* kernelArgs[] = {&A_d, &B_d, &C_d, reinterpret_cast(&NElem)}; kNodeParams.func = reinterpret_cast(HipTest::vectorADD); @@ -61,18 +92,101 @@ TEST_CASE("Unit_hipGraphKernelNodeCopyAttributes_Functional") { kNodeParams.sharedMemBytes = 0; kNodeParams.kernelParams = reinterpret_cast(kernelArgs); kNodeParams.extra = nullptr; - HIP_CHECK(hipGraphAddKernelNode(&kNode, graph, dependencies.data(), - dependencies.size(), &kNodeParams)); - dependencies.clear(); - dependencies.push_back(kNode); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, dependencies.data(), - dependencies.size(), C_h, C_d, - Nbytes, hipMemcpyDeviceToHost)); + HIP_CHECK(hipGraphAddKernelNode(&kernel_vecAdd, graph, nullptr, 0, + &kNodeParams)); - hipGraphNode_t kNode2; + // Create dependencies + HIP_CHECK(hipGraphAddDependencies(graph, &memcpy_A, &kernel_vecAdd, 1)); + HIP_CHECK(hipGraphAddDependencies(graph, &memcpy_B, &kernel_vecAdd, 1)); + HIP_CHECK(hipGraphAddDependencies(graph, &kernel_vecAdd, &memcpy_C, 1)); - HIP_CHECK(hipGraphAddKernelNode(&kNode2, graph, nullptr, 0, &kNodeParams)); - HIP_CHECK(hipGraphKernelNodeCopyAttributes(kNode2, kNode)); + hipKernelNodeAttrValue value_in, value_out; + memset(&value_in, 0, sizeof(hipKernelNodeAttrValue)); + memset(&value_out, 0, sizeof(hipKernelNodeAttrValue)); + + SECTION("Copy kernelNodeAttribute to same graph kernel node") { + hipGraphNode_t kNode2; + HIP_CHECK(hipGraphAddKernelNode(&kNode2, graph, nullptr, 0, &kNodeParams)); + + memset(&value_in, 0, sizeof(hipKernelNodeAttrValue)); + memset(&value_out, 0, sizeof(hipKernelNodeAttrValue)); + + HIP_CHECK(hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttributeAccessPolicyWindow, &value_in)); + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kernel_vecAdd, kNode2)); + HIP_CHECK(hipGraphKernelNodeGetAttribute(kNode2, + hipKernelNodeAttributeAccessPolicyWindow, &value_out)); + REQUIRE(true == validateKernelNodeAttrValue(value_in, value_out)); + + // copy back the node attributes for functional verification only + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kNode2, kernel_vecAdd)); + } + SECTION("Copy kernelNodeAttribute to different graph kernel node") { + hipGraphNode_t kNode3; + hipGraph_t graph3; + HIP_CHECK(hipGraphCreate(&graph3, 0)); + HIP_CHECK(hipGraphAddKernelNode(&kNode3, graph3, + nullptr, 0, &kNodeParams)); + + memset(&value_in, 0, sizeof(hipKernelNodeAttrValue)); + memset(&value_out, 0, sizeof(hipKernelNodeAttrValue)); + + HIP_CHECK(hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttributeAccessPolicyWindow, &value_in)); + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kernel_vecAdd, kNode3)); + HIP_CHECK(hipGraphKernelNodeGetAttribute(kNode3, + hipKernelNodeAttributeAccessPolicyWindow, &value_out)); + REQUIRE(true == validateKernelNodeAttrValue(value_in, value_out)); + + // copy back the node attributes for functional verification only + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kNode3, kernel_vecAdd)); + HIP_CHECK(hipGraphDestroy(graph3)); + } + SECTION("Copy kernelNodeAttribute to cloned graph kernel node") { + hipGraphNode_t kNode4; + hipGraph_t clonedGraph; + HIP_CHECK(hipGraphClone(&clonedGraph, graph)); + HIP_CHECK(hipGraphAddKernelNode(&kNode4, clonedGraph, + nullptr, 0, &kNodeParams)); + + memset(&value_in, 0, sizeof(hipKernelNodeAttrValue)); + memset(&value_out, 0, sizeof(hipKernelNodeAttrValue)); + + HIP_CHECK(hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttributeAccessPolicyWindow, &value_in)); + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kernel_vecAdd, kNode4)); + HIP_CHECK(hipGraphKernelNodeGetAttribute(kNode4, + hipKernelNodeAttributeAccessPolicyWindow, &value_out)); + REQUIRE(true == validateKernelNodeAttrValue(value_in, value_out)); + + // copy back the node attributes for functional verification only + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kNode4, kernel_vecAdd)); + HIP_CHECK(hipGraphDestroy(clonedGraph)); + } + SECTION("Copy kernelNodeAttribute to child graph kernel node") { + hipGraphNode_t kNode5, childGraphNode; + hipGraph_t childGraph; + HIP_CHECK(hipGraphCreate(&childGraph, 0)); + HIP_CHECK(hipGraphAddKernelNode(&kNode5, childGraph, + nullptr, 0, &kNodeParams)); + + HIP_CHECK(hipGraphAddChildGraphNode(&childGraphNode, graph, + nullptr, 0, childGraph)); + + memset(&value_in, 0, sizeof(hipKernelNodeAttrValue)); + memset(&value_out, 0, sizeof(hipKernelNodeAttrValue)); + + HIP_CHECK(hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttributeAccessPolicyWindow, &value_in)); + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kernel_vecAdd, kNode5)); + HIP_CHECK(hipGraphKernelNodeGetAttribute(kNode5, + hipKernelNodeAttributeAccessPolicyWindow, &value_out)); + REQUIRE(true == validateKernelNodeAttrValue(value_in, value_out)); + + // copy back the node attributes for functional verification only + HIP_CHECK(hipGraphKernelNodeCopyAttributes(kNode5, kernel_vecAdd)); + HIP_CHECK(hipGraphDestroy(childGraph)); + } // Instantiate and launch the graph HIP_CHECK(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0)); @@ -89,12 +203,20 @@ TEST_CASE("Unit_hipGraphKernelNodeCopyAttributes_Functional") { } /** - Negative Test for API - hipGraphKernelNodeCopyAttributes - 1) Pass source kernel node as nullptr for copy attribute api - 2) Pass destination kernel node as nullptr for copy attribute api - 3) Pass source kernel node as Uninitialize for copy attribute api - 4) Pass dest kernel node as Uninitialize for copy attribute api - */ +* Test Description +* ------------------------ +*  - Negative Test for API - hipGraphKernelNodeCopyAttributes +* 1) Pass source kernel node as nullptr for copy attribute api +* 2) Pass destination kernel node as nullptr for copy attribute api +* 3) Pass source kernel node as Uninitialize for copy attribute api +* 4) Pass dest kernel node as Uninitialize for copy attribute api +* Test source +* ------------------------ +*  - unit/graph/hipGraphKernelNodeCopyAttributes.cc +* Test requirements +* ------------------------ +*  - HIP_VERSION >= 5.6 +*/ TEST_CASE("Unit_hipGraphKernelNodeCopyAttributes_Attribute_Negative") { constexpr size_t N = 1024; @@ -162,4 +284,3 @@ TEST_CASE("Unit_hipGraphKernelNodeCopyAttributes_Attribute_Negative") { HIP_CHECK(hipGraphDestroy(graph)); HIP_CHECK(hipStreamDestroy(streamForGraph)); } - diff --git a/catch/unit/graph/hipGraphKernelNodeGetAttribute.cc b/catch/unit/graph/hipGraphKernelNodeGetAttribute.cc new file mode 100644 index 0000000000..faed511f7b --- /dev/null +++ b/catch/unit/graph/hipGraphKernelNodeGetAttribute.cc @@ -0,0 +1,195 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include + +/** +* @addtogroup hipGraphKernelNodeGetAttribute hipGraphKernelNodeGetAttribute +* @{ +* @ingroup GraphTest +* `hipGraphKernelNodeGetAttribute(hipGraphNode_t hNode, +* hipKernelNodeAttrID attr, hipKernelNodeAttrValue* value_out )` - +* Queries node attribute. +*/ + +/** +* Test Description +* ------------------------ +*  - Functional Test for API - hipGraphKernelNodeGetAttribute +* 1) GetKernelAttribute for ID hipKernelNodeAttributeCooperative +* 2) GetKernelAttribute for ID hipKernelNodeAttributeAccessPolicyWindow +* Test source +* ------------------------ +*  - unit/graph/hipGraphKernelNodeGetAttribute.cc +* Test requirements +* ------------------------ +*  - HIP_VERSION >= 5.6 +*/ + +TEST_CASE("Unit_hipGraphKernelNodeGetAttribute_Functional") { + constexpr size_t N = 1024; + constexpr size_t Nbytes = N * sizeof(int); + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpy_A, memcpy_B, memcpy_C, kernel_vecAdd; + hipKernelNodeParams kNodeParams{}; + hipStream_t stream; + int *A_d, *B_d, *C_d; + int *A_h, *B_h, *C_h; + size_t NElem{N}; + + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_B, graph, nullptr, 0, B_d, B_h, + Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_C, graph, nullptr, 0, C_h, C_d, + Nbytes, hipMemcpyDeviceToHost)); + + void* kernelArgs[] = {&A_d, &B_d, &C_d, reinterpret_cast(&NElem)}; + kNodeParams.func = reinterpret_cast(HipTest::vectorADD); + kNodeParams.gridDim = dim3(blocks); + kNodeParams.blockDim = dim3(threadsPerBlock); + kNodeParams.sharedMemBytes = 0; + kNodeParams.kernelParams = reinterpret_cast(kernelArgs); + kNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&kernel_vecAdd, graph, nullptr, 0, + &kNodeParams)); + + // Create dependencies + HIP_CHECK(hipGraphAddDependencies(graph, &memcpy_A, &kernel_vecAdd, 1)); + HIP_CHECK(hipGraphAddDependencies(graph, &memcpy_B, &kernel_vecAdd, 1)); + HIP_CHECK(hipGraphAddDependencies(graph, &kernel_vecAdd, &memcpy_C, 1)); + + hipKernelNodeAttrValue value_out; + memset(&value_out, 0, sizeof(hipKernelNodeAttrValue)); + + SECTION("GetKernelAttribute for hipKernelNodeAttributeCooperative") { + HIP_CHECK(hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttributeCooperative, &value_out)); + } + SECTION("GetKernelAttribute for hipKernelNodeAttributeAccessPolicyWindow") { + HIP_CHECK(hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttributeAccessPolicyWindow, &value_out)); + } + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + + // Verify graph execution result + HipTest::checkVectorADD(A_h, B_h, C_h, N); + + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); +} + +/** +* Test Description +* ------------------------ +*  - Negative Test for API - hipGraphKernelNodeGetAttribute +* 1) Pass kernel node as nullptr for Get attribute api & verify +* 2) Pass KernelNodeAttrID as negative value for Get attribute api & verify +* 3) Pass KernelNodeAttrID as INT_MAX value for Get attribute api & verify +* 4) Pass KernelNodeAttrValue as nullptr for Get attribute api & verify +* Test source +* ------------------------ +*  - unit/graph/hipGraphKernelNodeGetAttribute.cc +* Test requirements +* ------------------------ +*  - HIP_VERSION >= 5.6 +*/ + +TEST_CASE("Unit_hipGraphKernelNodeGetAttribute_Negative") { + constexpr size_t N = 1024; + constexpr size_t Nbytes = N * sizeof(int); + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + hipGraph_t graph; + hipGraphNode_t memcpy_A, memcpy_B, memcpy_C, kernel_vecAdd; + hipKernelNodeParams kNodeParams{}; + hipStream_t stream; + int *A_d, *B_d, *C_d; + int *A_h, *B_h, *C_h; + size_t NElem{N}; + hipError_t ret; + + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_B, graph, nullptr, 0, B_d, B_h, + Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpy_C, graph, nullptr, 0, C_h, C_d, + Nbytes, hipMemcpyDeviceToHost)); + + void* kernelArgs[] = {&A_d, &B_d, &C_d, reinterpret_cast(&NElem)}; + kNodeParams.func = reinterpret_cast(HipTest::vectorADD); + kNodeParams.gridDim = dim3(blocks); + kNodeParams.blockDim = dim3(threadsPerBlock); + kNodeParams.sharedMemBytes = 0; + kNodeParams.kernelParams = reinterpret_cast(kernelArgs); + kNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&kernel_vecAdd, graph, nullptr, 0, + &kNodeParams)); + + hipKernelNodeAttrValue value_out; + memset(&value_out, 0, sizeof(hipKernelNodeAttrValue)); + + SECTION("Pass kernel node as nullptr for Get attribute api") { + ret = hipGraphKernelNodeGetAttribute(nullptr, + hipKernelNodeAttributeAccessPolicyWindow, &value_out); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass KernelNodeAttrID as negative value for Get attribute api") { + ret = hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttrID(-1), &value_out); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass KernelNodeAttrID as INT_MAX value for Get attribute api") { + ret = hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttrID(INT_MAX), &value_out); + REQUIRE(hipErrorInvalidValue == ret); + } +#if HT_AMD // getting SIGSEGV error in Cuda Setup + SECTION("Pass KernelNodeAttrValue as nullptr for Get attribute api") { + ret = hipGraphKernelNodeGetAttribute(kernel_vecAdd, + hipKernelNodeAttributeAccessPolicyWindow, nullptr); + REQUIRE(hipErrorInvalidValue == ret); + } +#endif + + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); +} diff --git a/catch/unit/graph/hipGraphLaunch.cc b/catch/unit/graph/hipGraphLaunch.cc index 216ec421ef..21afeffce8 100644 --- a/catch/unit/graph/hipGraphLaunch.cc +++ b/catch/unit/graph/hipGraphLaunch.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -19,7 +19,7 @@ THE SOFTWARE. #include #include - +#include /* Test verifies hipGraphLaunch API Negative scenarios - 1) Pass graphExec as nullptr and verify api returns error code. @@ -32,8 +32,16 @@ Negative scenarios - Functional Scenario - 1) Check basic functionality with stream as hipStreamPerThread 2) Test hipGraphLaunch call on multiple devices. +3) Create a graph with multiple nodes. Create an executable graph. + Launch the executable graph 3 times in stream simultaneously. + Wait for stream. Validate the output. No issues should be observed +4) Create a graph with multiple nodes. Create an executable graph. + Verify if an executable graph be launched on null stream. */ +#define SIZE 1024 +#define TEST_LOOP_SIZE 3 + TEST_CASE("Unit_hipGraphLaunch_Negative") { hipError_t ret; SECTION("Pass pGraphExec as nullptr") { @@ -305,3 +313,100 @@ TEST_CASE("Unit_hipGraphLaunch_Functional_multidevice_test") { SUCCEED("Skipped the testcase as there is no device to test."); } } + +// Function to fill input data +static void fillRandInpData(int *A1_h, int *A2_h, size_t N) { + unsigned int seed = time(nullptr); + for (size_t i = 0; i < N; i++) { + A1_h[i] = (HipTest::RAND_R(&seed) & 0xFF); + A2_h[i] = (HipTest::RAND_R(&seed) & 0xFF); + } +} +// Function to validate result +static void validateOutData(int *A1_h, int *A2_h, size_t N) { + for (size_t i = 0; i < N; i++) { + int result = (A1_h[i]*A1_h[i]); + REQUIRE(result == A2_h[i]); + } +} +/* + * 1.Create a graph with multiple nodes. Create an executable graph. + * Launch the executable graph 3 times in stream simultaneously. + * Wait for stream. Validate the output. No issues should be observed + * 2.Create a graph with multiple nodes. Create an executable graph. + * Verify if an executable graph be launched on null stream. +*/ +TEST_CASE("Unit_hipGraphLaunch_Functional_MultipleLaunch") { + size_t memSize = SIZE; + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, + threadsPerBlock, SIZE); + hipGraph_t graph; + std::vector nodeDependencies; + + HIP_CHECK(hipGraphCreate(&graph, 0)); + int *A_h{nullptr}, *A_d{nullptr}, *C_d{nullptr}, *C_h{nullptr}; + + HipTest::initArrays(&A_d, &C_d, nullptr, + &A_h, &C_h, nullptr, SIZE, false); + + hipGraphNode_t memcpyH2D, memcpyD2H, kernelNode; + + // Create memcpy H2D nodes + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D, graph, nullptr, + 0, A_d, A_h, (sizeof(int)*SIZE), hipMemcpyHostToDevice)); + nodeDependencies.push_back(memcpyH2D); + // Creating kernel node + hipKernelNodeParams kerNodeParams; + void* kernelArgs[] = {reinterpret_cast(&A_d), + reinterpret_cast(&C_d), + reinterpret_cast(&memSize)}; + kerNodeParams.func = reinterpret_cast(HipTest::vector_square); + kerNodeParams.gridDim = dim3(blocks); + kerNodeParams.blockDim = dim3(threadsPerBlock); + kerNodeParams.sharedMemBytes = 0; + kerNodeParams.kernelParams = reinterpret_cast(kernelArgs); + kerNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&kernelNode, graph, nodeDependencies.data(), + nodeDependencies.size(), &kerNodeParams)); + nodeDependencies.clear(); + nodeDependencies.push_back(kernelNode); + + // Create memcpy D2H nodes + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H, graph, nodeDependencies.data(), + nodeDependencies.size(), C_h, C_d, (sizeof(int)*SIZE), + hipMemcpyDeviceToHost)); + nodeDependencies.clear(); + + // Create executable graph + hipStream_t streamForGraph; + hipGraphExec_t graphExec{nullptr}; + HIP_CHECK(hipStreamCreate(&streamForGraph)); + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, + nullptr, 0)); + // Execute graph + SECTION("Multiple Graph Launch") { + for (int iter = 0; iter < TEST_LOOP_SIZE; iter++) { + fillRandInpData(A_h, C_h, SIZE); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + validateOutData(A_h, C_h, SIZE); + } + } + SECTION("Graph launch on Null stream") { + for (int iter = 0; iter < TEST_LOOP_SIZE; iter++) { + fillRandInpData(A_h, C_h, SIZE); + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + HIP_CHECK(hipStreamSynchronize(0)); + validateOutData(A_h, C_h, SIZE); + } + } + + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + + // Free + HipTest::freeArrays(A_d, C_d, nullptr, A_h, C_h, nullptr, false); +} diff --git a/catch/unit/graph/hipGraphReleaseUserObject.cc b/catch/unit/graph/hipGraphReleaseUserObject.cc new file mode 100644 index 0000000000..613d8478bd --- /dev/null +++ b/catch/unit/graph/hipGraphReleaseUserObject.cc @@ -0,0 +1,62 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include + +#include "user_object_common.hh" + +/** + * Negative Test for API - hipGraphReleaseUserObject + 1) Pass graph as nullptr + 2) Pass User Object as nullptr + 3) Pass initialRefcount as 0 + 4) Pass initialRefcount as INT_MAX + */ +TEST_CASE("Unit_hipGraphReleaseUserObject_Negative") { + hipGraph_t graph; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + float* object = new float(); + REQUIRE(object != nullptr); + hipUserObject_t hObject; + + HIP_CHECK( + hipUserObjectCreate(&hObject, object, destroyFloatObj, 1, hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 1, hipGraphUserObjectMove)); + + SECTION("Pass graph as nullptr") { + HIP_CHECK_ERROR(hipGraphReleaseUserObject(nullptr, hObject, 1), hipErrorInvalidValue); + } + SECTION("Pass User Object as nullptr") { + HIP_CHECK_ERROR(hipGraphReleaseUserObject(graph, nullptr, 1), hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as 0") { + HIP_CHECK_ERROR(hipGraphReleaseUserObject(graph, hObject, 0), hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as INT_MAX") { + HIP_CHECK(hipGraphReleaseUserObject(graph, hObject, INT_MAX)); + } + + HIP_CHECK(hipUserObjectRelease(hObject, 1)); + HIP_CHECK(hipGraphDestroy(graph)); +} \ No newline at end of file diff --git a/catch/unit/graph/hipGraphRetainUserObject.cc b/catch/unit/graph/hipGraphRetainUserObject.cc new file mode 100644 index 0000000000..ebbe322d17 --- /dev/null +++ b/catch/unit/graph/hipGraphRetainUserObject.cc @@ -0,0 +1,243 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include + +#include "user_object_common.hh" + +/** + * Functional Test for API - hipGraphRetainUserObject + */ + +/* 1) Create GraphUserObject and retain it by calling hipGraphRetainUserObject + and release it by calling hipGraphReleaseUserObject. */ +static void hipGraphRetainUserObject_Functional_1(void* object, void destroyObj(void*)) { + hipGraph_t graph; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipUserObject_t hObject; + + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyObj, 1, hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 1, hipGraphUserObjectMove)); + + HIP_CHECK(hipGraphReleaseUserObject(graph, hObject)); + HIP_CHECK(hipUserObjectRelease(hObject)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +TEST_CASE("Unit_hipGraphRetainUserObject_Functional_1") { + SECTION("Called with int Object") { + int* object = new int(); + REQUIRE(object != nullptr); + hipGraphRetainUserObject_Functional_1(object, destroyIntObj); + } + SECTION("Called with float Object") { + float* object = new float(); + REQUIRE(object != nullptr); + hipGraphRetainUserObject_Functional_1(object, destroyFloatObj); + } + SECTION("Called with Class Object") { + BoxClass* object = new BoxClass(); + REQUIRE(object != nullptr); + hipGraphRetainUserObject_Functional_1(object, destroyClassObj); + } + SECTION("Called with Struct Object") { + BoxStruct* object = new BoxStruct(); + REQUIRE(object != nullptr); + hipGraphRetainUserObject_Functional_1(object, destroyStructObj); + } +} + +/* 2) Create UserObject and GraphUserObject and retain using custom reference + count and release it by calling hipGraphReleaseUserObject with count. */ +TEST_CASE("Unit_hipGraphRetainUserObject_Functional_2") { + constexpr size_t N = 1024; + constexpr size_t Nbytes = N * sizeof(int); + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + hipGraph_t graph; + hipGraphNode_t memcpyNode, kNode; + hipKernelNodeParams kNodeParams{}; + hipStream_t streamForGraph; + int *A_d, *B_d, *C_d; + int *A_h, *B_h, *C_h; + std::vector dependencies; + hipGraphExec_t graphExec; + size_t NElem{N}; + + HIP_CHECK(hipStreamCreate(&streamForGraph)); + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, nullptr, 0, A_d, A_h, Nbytes, + hipMemcpyHostToDevice)); + dependencies.push_back(memcpyNode); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, nullptr, 0, B_d, B_h, Nbytes, + hipMemcpyHostToDevice)); + dependencies.push_back(memcpyNode); + + void* kernelArgs[] = {&A_d, &B_d, &C_d, reinterpret_cast(&NElem)}; + kNodeParams.func = reinterpret_cast(HipTest::vectorADD); + kNodeParams.gridDim = dim3(blocks); + kNodeParams.blockDim = dim3(threadsPerBlock); + kNodeParams.kernelParams = reinterpret_cast(kernelArgs); + HIP_CHECK( + hipGraphAddKernelNode(&kNode, graph, dependencies.data(), dependencies.size(), &kNodeParams)); + + dependencies.clear(); + dependencies.push_back(kNode); + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, dependencies.data(), dependencies.size(), + C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); + + int refCount = 2; + int refCountRetain = 3; + + float* object = new float(); + REQUIRE(object != nullptr); + hipUserObject_t hObject; + + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyFloatObj, refCount, + hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + HIP_CHECK(hipUserObjectRetain(hObject, refCountRetain)); + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, refCountRetain, hipGraphUserObjectMove)); + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + + // Verify result + HipTest::checkVectorADD(A_h, B_h, C_h, N); + + HIP_CHECK(hipUserObjectRelease(hObject, refCount + refCountRetain)); + HIP_CHECK(hipGraphReleaseUserObject(graph, hObject, refCountRetain)); + + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); +} + +/** + * Negative Test for API - hipGraphRetainUserObject + 1) Pass graph as nullptr + 2) Pass User Object as nullptr + 3) Pass initialRefcount as 0 + 4) Pass initialRefcount as INT_MAX + 5) Pass flag as 0 + 6) Pass flag as INT_MAX + */ +TEST_CASE("Unit_hipGraphRetainUserObject_Negative") { + hipGraph_t graph; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + float* object = new float(); + REQUIRE(object != nullptr); + hipUserObject_t hObject; + + HIP_CHECK( + hipUserObjectCreate(&hObject, object, destroyFloatObj, 1, hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + + SECTION("Pass graph as nullptr") { + HIP_CHECK_ERROR(hipGraphRetainUserObject(nullptr, hObject, 1, hipGraphUserObjectMove), + hipErrorInvalidValue); + } + SECTION("Pass User Object as nullptr") { + HIP_CHECK_ERROR(hipGraphRetainUserObject(graph, nullptr, 1, hipGraphUserObjectMove), + hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as 0") { + HIP_CHECK_ERROR(hipGraphRetainUserObject(graph, hObject, 0, hipGraphUserObjectMove), + hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as INT_MAX") { + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, INT_MAX, hipGraphUserObjectMove)); + } + SECTION("Pass flag as 0") { HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 1, 0)); } + SECTION("Pass flag as INT_MAX") { + HIP_CHECK_ERROR(hipGraphRetainUserObject(graph, hObject, 1, INT_MAX), hipErrorInvalidValue); + } + + HIP_CHECK(hipUserObjectRelease(hObject, 1)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +TEST_CASE("Unit_hipGraphRetainUserObject_Negative_Basic") { + hipGraph_t graph; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + float* object = new float(); + REQUIRE(object != nullptr); + hipUserObject_t hObject; + + HIP_CHECK( + hipUserObjectCreate(&hObject, object, destroyFloatObj, 1, hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + + // Retain graph object with reference count 2 + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 2, hipGraphUserObjectMove)); + + // Release graph object with reference count more than 2 + HIP_CHECK(hipGraphReleaseUserObject(graph, hObject, 4)); + + // Again Retain graph object with reference count 8 + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 8, hipGraphUserObjectMove)); + + // Release graph object with reference count 1 + HIP_CHECK(hipGraphReleaseUserObject(graph, hObject, 1)); + + HIP_CHECK(hipUserObjectRelease(hObject, 1)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +TEST_CASE("Unit_hipGraphRetainUserObject_Negative_Null_Object") { + hipGraph_t graph; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + float* object = nullptr; // this is used for Null_Object test + hipUserObject_t hObject; + + HIP_CHECK( + hipUserObjectCreate(&hObject, object, destroyFloatObj, 1, hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + + // Retain graph object with reference count 2 + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 2, hipGraphUserObjectMove)); + + // Release graph object with reference count more than 2 + HIP_CHECK(hipGraphReleaseUserObject(graph, hObject, 4)); + + // Again Retain graph object with reference count 8 + HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 8, 0)); + + // Release graph object with reference count 1 + HIP_CHECK(hipGraphReleaseUserObject(graph, hObject, 1)); + + HIP_CHECK(hipUserObjectRelease(hObject, 1)); + HIP_CHECK(hipGraphDestroy(graph)); +} \ No newline at end of file diff --git a/catch/unit/graph/hipMatMul.cc b/catch/unit/graph/hipMatMul.cc new file mode 100644 index 0000000000..10a6d79ea3 --- /dev/null +++ b/catch/unit/graph/hipMatMul.cc @@ -0,0 +1,58 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +/* + This code object should be automatically built via "make build_tests". + In case it's missing, please type the following to generate it, + /opt/rocm/hip/bin/hipcc --genco hipMatMul.cc -o hipMatMul.code +*/ +#include"hip/hip_runtime.h" +__device__ int deviceGlobal = 1; + +extern "C" __global__ void matmulK(int* A, int* B, int* C, + int N) { + int ROW = blockIdx.y*blockDim.y+threadIdx.y; + int COL = blockIdx.x*blockDim.x+threadIdx.x; + int tmpSum = 0; + if ((ROW < N) && (COL < N)) { + // each thread computes one element of the block sub-matrix + for (int i = 0; i < N; i++) { + tmpSum += A[ROW * N + i] * B[i * N + COL]; + } + C[ROW * N + COL] = tmpSum; + } +} + +extern "C" __global__ void KernelandExtraParams(int* A, int* B, int* C, + int *D, int N) { + int ROW = blockIdx.y*blockDim.y+threadIdx.y; + int COL = blockIdx.x*blockDim.x+threadIdx.x; + int tmpSum = 0; + if (ROW < N && COL < N) { + // each thread computes one element of the block sub-matrix + for (int i = 0; i < N; i++) { + tmpSum += A[ROW * N + i] * B[i * N + COL]; + } + } + C[ROW * N + COL] = tmpSum; + D[ROW * N + COL] = tmpSum; +} + +extern "C" __global__ void dummyKernel() { +} diff --git a/catch/unit/graph/hipStreamBeginCapture.cc b/catch/unit/graph/hipStreamBeginCapture.cc index 81f97e5f92..cc7e85ef5f 100644 --- a/catch/unit/graph/hipStreamBeginCapture.cc +++ b/catch/unit/graph/hipStreamBeginCapture.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2022 - 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -20,8 +20,7 @@ THE SOFTWARE. #include #include #include - -#include "stream_capture_common.hh" +#include "stream_capture_common.hh" // NOLINT /** * @addtogroup hipStreamBeginCapture hipStreamBeginCapture @@ -56,7 +55,8 @@ static void hostNodeCallback(void* data) { } template -void captureStreamAndLaunchGraph(F graphFunc, hipStreamCaptureMode mode, hipStream_t stream) { +void captureStreamAndLaunchGraph(F graphFunc, hipStreamCaptureMode mode, + hipStream_t stream) { constexpr size_t N = 1000000; size_t Nbytes = N * sizeof(T); @@ -88,7 +88,8 @@ void captureStreamAndLaunchGraph(F graphFunc, hipStreamCaptureMode mode, hipStre std::fill_n(A_h.host_ptr(), N, static_cast(i)); HIP_CHECK(hipGraphLaunch(graphExec, stream)); HIP_CHECK(hipStreamSynchronize(stream)); - ArrayFindIfNot(B_h.host_ptr(), static_cast(i) * static_cast(i), N); + ArrayFindIfNot(B_h.host_ptr(), + static_cast(i) * static_cast(i), N); } HIP_CHECK(hipGraphExecDestroy(graphExec)) @@ -115,15 +116,16 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_Functional") { StreamGuard stream_guard(stream_type); hipStream_t stream = stream_guard.stream(); - const hipStreamCaptureMode captureMode = GENERATE( - hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + const hipStreamCaptureMode captureMode = GENERATE(hipStreamCaptureModeGlobal, + hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); EventsGuard events_guard(3); StreamsGuard streams_guard(2); SECTION("Linear graph capture") { captureStreamAndLaunchGraph( - [](float* A_h, float* A_d, float* B_h, float* B_d, size_t N, hipStream_t stream) { + [](float* A_h, float* A_d, float* B_h, float* B_d, size_t N, + hipStream_t stream) { return captureSequenceLinear(A_h, A_d, B_h, B_d, N, stream); }, captureMode, stream); @@ -131,10 +133,10 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_Functional") { SECTION("Branched graph capture") { captureStreamAndLaunchGraph( - [&streams_guard, &events_guard](float* A_h, float* A_d, float* B_h, float* B_d, size_t N, - hipStream_t stream) { - captureSequenceBranched(A_h, A_d, B_h, B_d, N, stream, streams_guard.stream_list(), - events_guard.event_list()); + [&streams_guard, &events_guard](float* A_h, float* A_d, float* B_h, + float* B_d, size_t N, hipStream_t stream) { + captureSequenceBranched(A_h, A_d, B_h, B_d, N, stream, + streams_guard.stream_list(), events_guard.event_list()); }, captureMode, stream); } @@ -170,7 +172,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_Parameters") { hipErrorIllegalState); } SECTION("Creating hipStream with invalid mode") { - HIP_CHECK_ERROR(hipStreamBeginCapture(stream, hipStreamCaptureMode(-1)), hipErrorInvalidValue); + HIP_CHECK_ERROR(hipStreamBeginCapture(stream, hipStreamCaptureMode(-1)), + hipErrorInvalidValue); } #if HT_NVIDIA // EXSWHTEC-216 SECTION("Stream capture on uninitialized stream returns error code.") { @@ -178,7 +181,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_Parameters") { StreamGuard sg(Streams::created); return sg.stream(); }; - HIP_CHECK_ERROR(hipStreamBeginCapture(InvalidStream(), hipStreamCaptureModeGlobal), + HIP_CHECK_ERROR(hipStreamBeginCapture(InvalidStream(), + hipStreamCaptureModeGlobal), hipErrorContextIsDestroyed); } #endif @@ -202,8 +206,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_Basic") { StreamGuard stream_guard(stream_type); hipStream_t s = stream_guard.stream(); - const hipStreamCaptureMode captureMode = GENERATE( - hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + const hipStreamCaptureMode captureMode = GENERATE(hipStreamCaptureModeGlobal, + hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); HIP_CHECK(hipStreamBeginCapture(s, captureMode)); @@ -213,7 +217,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_Basic") { /* Local function for inter stream event synchronization */ -static void interStrmEventSyncCapture(const hipStream_t& stream1, const hipStream_t& stream2) { +static void interStrmEventSyncCapture(const hipStream_t& stream1, + const hipStream_t& stream2) { hipGraph_t graph1{nullptr}, graph2{nullptr}; hipGraphExec_t graphExec1{nullptr}, graphExec2{nullptr}; @@ -260,7 +265,8 @@ static void interStrmEventSyncCapture(const hipStream_t& stream1, const hipStrea /* Local function for colligated stream capture */ -static void colligatedStrmCapture(const hipStream_t& stream1, const hipStream_t& stream2) { +static void colligatedStrmCapture(const hipStream_t& stream1, + const hipStream_t& stream2) { hipGraph_t graph1{nullptr}, graph2{nullptr}; hipGraphExec_t graphExec1{nullptr}, graphExec2{nullptr}; @@ -303,7 +309,8 @@ static void colligatedStrmCapture(const hipStream_t& stream1, const hipStream_t& /* Local function for colligated stream capture functionality */ -static void colligatedStrmCaptureFunc(const hipStream_t& stream1, const hipStream_t& stream2) { +static void colligatedStrmCaptureFunc(const hipStream_t& stream1, + const hipStream_t& stream2) { constexpr size_t N = 1000000; size_t Nbytes = N * sizeof(int); @@ -323,8 +330,10 @@ static void colligatedStrmCaptureFunc(const hipStream_t& stream1, const hipStrea // Capture 2 streams HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); - captureSequenceLinear(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream1); - captureSequenceLinear(C_h.host_ptr(), C_d.ptr(), D_h.host_ptr(), D_d.ptr(), N, stream2); + captureSequenceLinear(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), + N, stream1); + captureSequenceLinear(C_h.host_ptr(), C_d.ptr(), D_h.host_ptr(), D_d.ptr(), + N, stream2); captureSequenceCompute(A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream1); captureSequenceCompute(C_d.ptr(), D_h.host_ptr(), D_d.ptr(), N, stream2); HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); @@ -360,8 +369,9 @@ static void colligatedStrmCaptureFunc(const hipStream_t& stream1, const hipStrea /* Stream Capture thread function */ -static void threadStrmCaptureFunc(hipStream_t stream, int* A_h, int* A_d, int* B_h, int* B_d, - hipGraph_t* graph, size_t N, hipStreamCaptureMode mode) { +static void threadStrmCaptureFunc(hipStream_t stream, int* A_h, int* A_d, + int* B_h, int* B_d, hipGraph_t* graph, + size_t N, hipStreamCaptureMode mode) { // Capture stream HIP_CHECK(hipStreamBeginCapture(stream, mode)); captureSequenceLinear(A_h, A_d, B_h, B_d, N, stream); @@ -393,10 +403,10 @@ static void multithreadedTest(hipStreamCaptureMode mode) { LinearAllocGuard D_d(LinearAllocs::hipMalloc, Nbytes); // Launch 2 threads to capture the 2 streams into graphs - std::thread t1(threadStrmCaptureFunc, stream1, A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), - B_d.ptr(), &graph1, N, mode); - std::thread t2(threadStrmCaptureFunc, stream2, C_h.host_ptr(), C_d.ptr(), D_h.host_ptr(), - D_d.ptr(), &graph2, N, mode); + std::thread t1(threadStrmCaptureFunc, stream1, A_h.host_ptr(), A_d.ptr(), + B_h.host_ptr(), B_d.ptr(), &graph1, N, mode); + std::thread t2(threadStrmCaptureFunc, stream2, C_h.host_ptr(), C_d.ptr(), + D_h.host_ptr(), D_d.ptr(), &graph2, N, mode); t1.join(); t2.join(); @@ -469,9 +479,11 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_InterStrmEventSync_Flags") { TEST_CASE("Unit_hipStreamBeginCapture_Positive_InterStrmEventSync_Priority") { int minPriority = 0, maxPriority = 0; HIP_CHECK(hipDeviceGetStreamPriorityRange(&minPriority, &maxPriority)); - StreamGuard stream_guard1(Streams::withPriority, hipStreamDefault, minPriority); + StreamGuard stream_guard1(Streams::withPriority, hipStreamDefault, + minPriority); hipStream_t stream1 = stream_guard1.stream(); - StreamGuard stream_guard2(Streams::withPriority, hipStreamDefault, maxPriority); + StreamGuard stream_guard2(Streams::withPriority, hipStreamDefault, + maxPriority); hipStream_t stream2 = stream_guard2.stream(); interStrmEventSyncCapture(stream1, stream2); } @@ -517,12 +529,14 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_ColligatedStrmCapture_Flags") { * ------------------------ * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_Positive_ColligatedStrmCapture_Priority") { +TEST_CASE("Unit_hipStreamBeginCapture_Positive_ColligatedStrmCapture_Prio") { int minPriority = 0, maxPriority = 0; HIP_CHECK(hipDeviceGetStreamPriorityRange(&minPriority, &maxPriority)); - StreamGuard stream_guard1(Streams::withPriority, hipStreamDefault, minPriority); + StreamGuard stream_guard1(Streams::withPriority, hipStreamDefault, + minPriority); hipStream_t stream1 = stream_guard1.stream(); - StreamGuard stream_guard2(Streams::withPriority, hipStreamDefault, maxPriority); + StreamGuard stream_guard2(Streams::withPriority, hipStreamDefault, + maxPriority); hipStream_t stream2 = stream_guard2.stream(); colligatedStrmCapture(stream1, stream2); } @@ -563,8 +577,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_ColligatedStrmCaptureFunc") { * - HIP_VERSION >= 5.2 */ TEST_CASE("Unit_hipStreamBeginCapture_Positive_Multithreaded") { - const hipStreamCaptureMode captureMode = GENERATE( - hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + const hipStreamCaptureMode captureMode = GENERATE(hipStreamCaptureModeGlobal, + hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); multithreadedTest(captureMode); } @@ -693,7 +707,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_CapturingFromWithinStrms") { HIP_CHECK(hipEventRecord(events[2], streams[2])); HIP_CHECK(hipStreamWaitEvent(streams[0], events[1], 0)); HIP_CHECK(hipStreamWaitEvent(streams[0], events[2], 0)); - HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), hipMemcpyDefault, streams[0])); + HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), hipMemcpyDefault, + streams[0])); HIP_CHECK(hipStreamEndCapture(streams[0], &graph)); // End Capture // Reset device memory HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); @@ -735,7 +750,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_DetectingInvalidCapture") { dummyKernel<<<1, 1, 0, streams[0]>>>(); // Since stream[1] is already in capture mode due to event wait // hipStreamBeginCapture on stream[1] is expected to return error. - HIP_CHECK_ERROR(hipStreamBeginCapture(streams[1], hipStreamCaptureModeGlobal), + HIP_CHECK_ERROR(hipStreamBeginCapture(streams[1], + hipStreamCaptureModeGlobal), hipErrorIllegalState); } @@ -768,7 +784,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_CapturingMultGraphsFrom1Strm") { for (int i = 0; i < 3; i++) { HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); for (int j = 0; j <= i; j++) incrementKernel<<<1, 1, 0, stream1>>>(devMem); - HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), hipMemcpyDefault, stream1)); + HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), hipMemcpyDefault, + stream1)); HIP_CHECK(hipStreamEndCapture(stream1, &graphs[i])); } // Instantiate and execute all graphs @@ -784,7 +801,6 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_CapturingMultGraphsFrom1Strm") { } } -#if HT_NVIDIA /** * Test Description * ------------------------ @@ -808,29 +824,35 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_CheckingSyncDuringCapture") { EventsGuard events_guard(1); hipEvent_t e = events_guard[0]; - const hipStreamCaptureMode captureMode = GENERATE( - hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + const hipStreamCaptureMode captureMode = GENERATE(hipStreamCaptureModeGlobal, + hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); SECTION("Synchronize stream during capture") { - HIP_CHECK_ERROR(hipStreamSynchronize(stream), hipErrorStreamCaptureUnsupported); + HIP_CHECK_ERROR(hipStreamSynchronize(stream), + hipErrorStreamCaptureUnsupported); } + SECTION("Query stream during capture") { + HIP_CHECK_ERROR(hipStreamQuery(stream), + hipErrorStreamCaptureUnsupported); + } +#if HT_NVIDIA SECTION("Synchronize device during capture") { - HIP_CHECK_ERROR(hipDeviceSynchronize(), hipErrorStreamCaptureUnsupported); + HIP_CHECK_ERROR(hipDeviceSynchronize(), + hipErrorStreamCaptureUnsupported); } SECTION("Synchronize event during capture") { HIP_CHECK(hipEventRecord(e, stream)); HIP_CHECK_ERROR(hipEventSynchronize(e), hipErrorCapturedEvent); } - SECTION("Query stream during capture") { - HIP_CHECK_ERROR(hipStreamQuery(stream), hipErrorStreamCaptureUnsupported); - } SECTION("Query for an event during capture") { HIP_CHECK(hipEventRecord(e, stream)); HIP_CHECK_ERROR(hipEventQuery(e), hipErrorCapturedEvent); } +#endif } +#if HT_NVIDIA /** * Test Description * ------------------------ @@ -861,14 +883,17 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_UnsafeCallsDuringCapture") { HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); SECTION("hipMalloc during capture") { - HIP_CHECK_ERROR(hipMalloc(&devMem2, sizeof(int)), hipErrorStreamCaptureUnsupported); + HIP_CHECK_ERROR(hipMalloc(&devMem2, sizeof(int)), + hipErrorStreamCaptureUnsupported); } SECTION("hipMemcpy during capture") { - HIP_CHECK_ERROR(hipMemcpy(devMem.ptr(), hostMem.host_ptr(), sizeof(int), hipMemcpyHostToDevice), + HIP_CHECK_ERROR(hipMemcpy(devMem.ptr(), hostMem.host_ptr(), sizeof(int), + hipMemcpyHostToDevice), hipErrorStreamCaptureImplicit); } SECTION("hipMemset during capture") { - HIP_CHECK_ERROR(hipMemset(devMem.ptr(), 0, sizeof(int)), hipErrorStreamCaptureImplicit); + HIP_CHECK_ERROR(hipMemset(devMem.ptr(), 0, sizeof(int)), + hipErrorStreamCaptureImplicit); } } #endif @@ -889,7 +914,7 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_UnsafeCallsDuringCapture") { * ------------------------ * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_Negative_EndingCapturewhenCaptureInProgress") { +TEST_CASE("Unit_hipStreamBeginCapture_Negative_EndingCapwhenCapInProg") { hipGraph_t graph{nullptr}; StreamsGuard streams_guard(2); @@ -905,7 +930,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_EndingCapturewhenCaptureInProgres HIP_CHECK(hipEventRecord(e, stream1)); HIP_CHECK(hipStreamWaitEvent(stream2, e, 0)); dummyKernel<<<1, 1, 0, stream2>>>(); - HIP_CHECK_ERROR(hipStreamEndCapture(stream1, &graph), hipErrorStreamCaptureUnjoined); + HIP_CHECK_ERROR(hipStreamEndCapture(stream1, &graph), + hipErrorStreamCaptureUnjoined); } SECTION("End strm capture when forked strm still has operations") { EventsGuard events_guard(2); @@ -919,7 +945,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Negative_EndingCapturewhenCaptureInProgres HIP_CHECK(hipEventRecord(e2, stream2)); HIP_CHECK(hipStreamWaitEvent(stream1, e2, 0)); dummyKernel<<<1, 1, 0, stream2>>>(); - HIP_CHECK_ERROR(hipStreamEndCapture(stream1, &graph), hipErrorStreamCaptureUnjoined); + HIP_CHECK_ERROR(hipStreamEndCapture(stream1, &graph), + hipErrorStreamCaptureUnjoined); } } /** @@ -942,17 +969,19 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_MultiGPU") { SUCCEED("skipping the testcases as numDevices < 2"); return; } - hipStream_t* stream = reinterpret_cast(malloc(devcount * sizeof(hipStream_t))); + hipStream_t* stream = reinterpret_cast + (malloc(devcount * sizeof(hipStream_t))); REQUIRE(stream != nullptr); - hipGraph_t* graph = reinterpret_cast(malloc(devcount * sizeof(hipGraph_t))); + hipGraph_t* graph = reinterpret_cast + (malloc(devcount * sizeof(hipGraph_t))); REQUIRE(graph != nullptr); int **devMem{nullptr}, **hostMem{nullptr}; hostMem = reinterpret_cast(malloc(sizeof(int*) * devcount)); REQUIRE(hostMem != nullptr); devMem = reinterpret_cast(malloc(sizeof(int*) * devcount)); REQUIRE(devMem != nullptr); - hipGraphExec_t* graphExec = - reinterpret_cast(malloc(devcount * sizeof(hipGraphExec_t))); + hipGraphExec_t* graphExec = reinterpret_cast + (malloc(devcount * sizeof(hipGraphExec_t))); // Capture stream in each device for (int dev = 0; dev < devcount; dev++) { HIP_CHECK(hipSetDevice(dev)); @@ -964,14 +993,15 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_MultiGPU") { for (int i = 0; i < (dev + 1); i++) { incrementKernel<<<1, 1, 0, stream[dev]>>>(devMem[dev]); } - HIP_CHECK( - hipMemcpyAsync(hostMem[dev], devMem[dev], sizeof(int), hipMemcpyDefault, stream[dev])); + HIP_CHECK(hipMemcpyAsync(hostMem[dev], devMem[dev], sizeof(int), + hipMemcpyDefault, stream[dev])); HIP_CHECK(hipStreamEndCapture(stream[dev], &graph[dev])); } // Launch the captured graphs in the respective device for (int dev = 0; dev < devcount; dev++) { HIP_CHECK(hipSetDevice(dev)); - HIP_CHECK(hipGraphInstantiate(&graphExec[dev], graph[dev], nullptr, nullptr, 0)); + HIP_CHECK(hipGraphInstantiate(&graphExec[dev], graph[dev], nullptr, + nullptr, 0)); HIP_CHECK(hipGraphLaunch(graphExec[dev], stream[dev])); } // Validate output @@ -1038,8 +1068,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_nestedStreamCapture") { HIP_CHECK(hipEventRecord(events[3], streams[2])); HIP_CHECK(hipStreamWaitEvent(streams[0], events[3], 0)); HIP_CHECK(hipStreamWaitEvent(streams[0], events[2], 0)); - HIP_CHECK(hipMemcpyAsync(hostMem_g.host_ptr(), devMem_g.ptr(), sizeof(int), hipMemcpyDefault, - streams[0])); + HIP_CHECK(hipMemcpyAsync(hostMem_g.host_ptr(), devMem_g.ptr(), sizeof(int), + hipMemcpyDefault, streams[0])); HIP_CHECK(hipStreamEndCapture(streams[0], &graph)); // End Capture // Reset device memory HIP_CHECK(hipMemset(devMem_g.ptr(), 0, sizeof(int))); @@ -1077,15 +1107,23 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_streamReuse") { hipGraph_t graphs[3]; StreamsGuard streams(3); EventsGuard events(4); - LinearAllocGuard hostMem_g1 = LinearAllocGuard(LinearAllocs::malloc, sizeof(int)); - LinearAllocGuard hostMem_g2 = LinearAllocGuard(LinearAllocs::malloc, sizeof(int)); - LinearAllocGuard hostMem_g3 = LinearAllocGuard(LinearAllocs::malloc, sizeof(int)); - LinearAllocGuard devMem_g1 = LinearAllocGuard(LinearAllocs::hipMalloc, sizeof(int)); - LinearAllocGuard devMem_g2 = LinearAllocGuard(LinearAllocs::hipMalloc, sizeof(int)); - LinearAllocGuard devMem_g3 = LinearAllocGuard(LinearAllocs::hipMalloc, sizeof(int)); + LinearAllocGuard hostMem_g1 = LinearAllocGuard + (LinearAllocs::malloc, sizeof(int)); + LinearAllocGuard hostMem_g2 = LinearAllocGuard + (LinearAllocs::malloc, sizeof(int)); + LinearAllocGuard hostMem_g3 = LinearAllocGuard + (LinearAllocs::malloc, sizeof(int)); + LinearAllocGuard devMem_g1 = LinearAllocGuard + (LinearAllocs::hipMalloc, sizeof(int)); + LinearAllocGuard devMem_g2 = LinearAllocGuard + (LinearAllocs::hipMalloc, sizeof(int)); + LinearAllocGuard devMem_g3 = LinearAllocGuard + (LinearAllocs::hipMalloc, sizeof(int)); - std::vector hostMem = {hostMem_g1.host_ptr(), hostMem_g2.host_ptr(), hostMem_g3.host_ptr()}; - std::vector devMem = {devMem_g1.ptr(), devMem_g2.ptr(), devMem_g3.ptr()}; + std::vector hostMem = {hostMem_g1.host_ptr(), hostMem_g2.host_ptr(), + hostMem_g3.host_ptr()}; + std::vector devMem = {devMem_g1.ptr(), devMem_g2.ptr(), + devMem_g3.ptr()}; // Create a device memory of size int and initialize it to 0 for (int i = 0; i < 3; i++) { memset(hostMem[i], 0, sizeof(int)); @@ -1109,14 +1147,16 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_streamReuse") { HIP_CHECK(hipEventRecord(events[3], streams[2])); HIP_CHECK(hipStreamWaitEvent(streams[0], events[3], 0)); HIP_CHECK(hipStreamWaitEvent(streams[0], events[2], 0)); - HIP_CHECK(hipMemcpyAsync(hostMem[0], devMem[0], sizeof(int), hipMemcpyDefault, streams[0])); + HIP_CHECK(hipMemcpyAsync(hostMem[0], devMem[0], sizeof(int), + hipMemcpyDefault, streams[0])); HIP_CHECK(hipStreamEndCapture(streams[0], &graphs[0])); // End Capture // Start capturing graph2 from stream 2 HIP_CHECK(hipStreamBeginCapture(streams[1], hipStreamCaptureModeGlobal)); incrementKernel<<<1, 1, 0, streams[1]>>>(devMem[1]); incrementKernel<<<1, 1, 0, streams[1]>>>(devMem[1]); incrementKernel<<<1, 1, 0, streams[1]>>>(devMem[1]); - HIP_CHECK(hipMemcpyAsync(hostMem[1], devMem[1], sizeof(int), hipMemcpyDefault, streams[1])); + HIP_CHECK(hipMemcpyAsync(hostMem[1], devMem[1], sizeof(int), + hipMemcpyDefault, streams[1])); HIP_CHECK(hipStreamEndCapture(streams[1], &graphs[1])); // End Capture // Start capturing graph3 from stream 3 HIP_CHECK(hipStreamBeginCapture(streams[2], hipStreamCaptureModeGlobal)); @@ -1125,7 +1165,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_streamReuse") { incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[2]); incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[2]); incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[2]); - HIP_CHECK(hipMemcpyAsync(hostMem[2], devMem[2], sizeof(int), hipMemcpyDefault, streams[2])); + HIP_CHECK(hipMemcpyAsync(hostMem[2], devMem[2], sizeof(int), + hipMemcpyDefault, streams[2])); HIP_CHECK(hipStreamEndCapture(streams[2], &graphs[2])); // End Capture // Reset device memory HIP_CHECK(hipMemset(devMem[0], 0, sizeof(int))); @@ -1169,32 +1210,40 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_captureComplexGraph") { EventsGuard events(7); // Allocate Device memory and Host memory size_t N = GRIDSIZE * BLOCKSIZE; - LinearAllocGuard Ah = LinearAllocGuard(LinearAllocs::malloc, N * sizeof(int)); - LinearAllocGuard Bh = LinearAllocGuard(LinearAllocs::malloc, N * sizeof(int)); - LinearAllocGuard Ch = LinearAllocGuard(LinearAllocs::malloc, N * sizeof(int)); - LinearAllocGuard Ad = LinearAllocGuard(LinearAllocs::hipMalloc, N * sizeof(int)); - LinearAllocGuard Bd = LinearAllocGuard(LinearAllocs::hipMalloc, N * sizeof(int)); + LinearAllocGuard Ah = LinearAllocGuard + (LinearAllocs::malloc, N * sizeof(int)); + LinearAllocGuard Bh = LinearAllocGuard + (LinearAllocs::malloc, N * sizeof(int)); + LinearAllocGuard Ch = LinearAllocGuard + (LinearAllocs::malloc, N * sizeof(int)); + LinearAllocGuard Ad = LinearAllocGuard + (LinearAllocs::hipMalloc, N * sizeof(int)); + LinearAllocGuard Bd = LinearAllocGuard + (LinearAllocs::hipMalloc, N * sizeof(int)); // Capture streams into graph HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); HIP_CHECK(hipEventRecord(events[0], streams[0])); HIP_CHECK(hipStreamWaitEvent(streams[3], events[0], 0)); HIP_CHECK(hipStreamWaitEvent(streams[4], events[0], 0)); - HIP_CHECK( - hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), (N * sizeof(int)), hipMemcpyDefault, streams[0])); - HIP_CHECK( - hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), (N * sizeof(int)), hipMemcpyDefault, streams[4])); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), (N * sizeof(int)), + hipMemcpyDefault, streams[0])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), (N * sizeof(int)), + hipMemcpyDefault, streams[4])); hipHostFn_t fn = hostNodeCallback; HIPCHECK(hipLaunchHostFunc(streams[3], fn, nullptr)); HIP_CHECK(hipEventRecord(events[1], streams[0])); HIP_CHECK(hipStreamWaitEvent(streams[1], events[1], 0)); int* Ad_2nd_half = Ad.ptr() + N / 2; int* Ad_1st_half = Ad.ptr(); - mymul<<>>(Ad_2nd_half, CONST_KER2_VAL); - mymul<<>>(Ad_1st_half, CONST_KER1_VAL); + mymul<<>>(Ad_2nd_half, + CONST_KER2_VAL); + mymul<<>>(Ad_1st_half, + CONST_KER1_VAL); HIP_CHECK(hipEventRecord(events[2], streams[1])); HIP_CHECK(hipStreamWaitEvent(streams[2], events[2], 0)); - mymul<<>>(Ad_1st_half, CONST_KER3_VAL); + mymul<<>>(Ad_1st_half, + CONST_KER3_VAL); HIPCHECK(hipLaunchHostFunc(streams[2], fn, nullptr)); HIP_CHECK(hipEventRecord(events[6], streams[1])); HIP_CHECK(hipStreamWaitEvent(streams[0], events[6], 0)); @@ -1205,8 +1254,8 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_captureComplexGraph") { HIP_CHECK(hipStreamWaitEvent(streams[0], events[3], 0)); HIP_CHECK(hipEventRecord(events[4], streams[3])); HIP_CHECK(hipStreamWaitEvent(streams[0], events[4], 0)); - HIP_CHECK( - hipMemcpyAsync(Ch.host_ptr(), Ad.ptr(), (N * sizeof(int)), hipMemcpyDefault, streams[0])); + HIP_CHECK(hipMemcpyAsync(Ch.host_ptr(), Ad.ptr(), (N * sizeof(int)), + hipMemcpyDefault, streams[0])); HIP_CHECK(hipStreamEndCapture(streams[0], &graph)); // End Capture // Execute and test the graph hipGraphExec_t graphExec{nullptr}; @@ -1219,10 +1268,11 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_captureComplexGraph") { HIP_CHECK(hipStreamSynchronize(streams[0])); for (size_t i = 0; i < N; i++) { if (i > (N / 2 - 1)) { - REQUIRE(Ch.host_ptr()[i] == (Bh.host_ptr()[i] + Ah.host_ptr()[i] * CONST_KER2_VAL)); + REQUIRE(Ch.host_ptr()[i] == (Bh.host_ptr()[i] + + Ah.host_ptr()[i] * CONST_KER2_VAL)); } else { - REQUIRE(Ch.host_ptr()[i] == - (Bh.host_ptr()[i] + Ah.host_ptr()[i] * CONST_KER1_VAL * CONST_KER3_VAL)); + REQUIRE(Ch.host_ptr()[i] == (Bh.host_ptr()[i] + + Ah.host_ptr()[i] * CONST_KER1_VAL * CONST_KER3_VAL)); } } } @@ -1267,3 +1317,226 @@ TEST_CASE("Unit_hipStreamBeginCapture_Positive_captureEmptyStreams") { HIP_CHECK(hipGraphDestroy(graph)); } + +/** + * Test Description + * ------------------------ + * - Test to verify hipStreamSynchronize on a stream works when stream capture + * on another stream is ongoing. + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.6 + */ + +TEST_CASE("Unit_hipStreamBeginCapture_StreamSync_OngoingCapture") { + hipStreamCaptureMode flag = hipStreamCaptureModeRelaxed; + constexpr int GRIDSIZE = 1; + constexpr int BLOCKSIZE = 512; + constexpr int VALUE1 = 7, VALUE2 = 11; + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + // Allocate device memory + LinearAllocGuard Ah = LinearAllocGuard(LinearAllocs::malloc, + BLOCKSIZE * sizeof(int)); + LinearAllocGuard Ad = LinearAllocGuard(LinearAllocs::hipMalloc, + BLOCKSIZE * sizeof(int)); + LinearAllocGuard Bh = LinearAllocGuard(LinearAllocs::malloc, + BLOCKSIZE * sizeof(int)); + LinearAllocGuard Bd = LinearAllocGuard(LinearAllocs::hipMalloc, + BLOCKSIZE * sizeof(int)); + // Fill input data + std::fill_n(Ah.host_ptr(), BLOCKSIZE, VALUE1); + std::fill_n(Bh.host_ptr(), BLOCKSIZE, VALUE2); + // Stream create + StreamsGuard stream0(1); + // Capture streams into graph + SECTION("Stream Creation Before Capture") { + StreamsGuard stream1(1); + HIP_CHECK(hipStreamBeginCapture(stream0[0], flag)); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipStreamSynchronize(stream1[0])); + myadd<<>>(Ad.ptr(), Bd.ptr()); + HIP_CHECK(hipStreamEndCapture(stream0[0], &graph)); // End Capture + } + SECTION("Synchronizing multiple streams during Capture") { + StreamsGuard stream1(1), stream2(1); + HIP_CHECK(hipStreamBeginCapture(stream0[0], flag)); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream2[0])); + HIP_CHECK(hipStreamSynchronize(stream1[0])); + HIP_CHECK(hipStreamSynchronize(stream2[0])); + myadd<<>>(Ad.ptr(), Bd.ptr()); + HIP_CHECK(hipStreamEndCapture(stream0[0], &graph)); // End Capture + } + SECTION("Stream Creation After Capture") { + HIP_CHECK(hipStreamBeginCapture(stream0[0], flag)); + StreamsGuard stream1(1); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipStreamSynchronize(stream1[0])); + myadd<<>>(Ad.ptr(), Bd.ptr()); + HIP_CHECK(hipStreamEndCapture(stream0[0], &graph)); // End Capture + } + SECTION("Stream Synchronize Before Capture") { + StreamsGuard stream1(1); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipStreamSynchronize(stream1[0])); + HIP_CHECK(hipStreamBeginCapture(stream0[0], flag)); + myadd<<>>(Ad.ptr(), Bd.ptr()); + HIP_CHECK(hipStreamEndCapture(stream0[0], &graph)); // End Capture + } + SECTION("Stream Synchronize After Capture") { + HIP_CHECK(hipStreamBeginCapture(stream0[0], flag)); + myadd<<>>(Ad.ptr(), Bd.ptr()); + HIP_CHECK(hipStreamEndCapture(stream0[0], &graph)); // End Capture + StreamsGuard stream1(1); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream1[0])); + HIP_CHECK(hipStreamSynchronize(stream1[0])); + } + // Execute and test the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream0[0])); + HIP_CHECK(hipStreamSynchronize(stream0[0])); + // Check output + HIP_CHECK(hipMemcpy(Ah.host_ptr(), Ad.ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDeviceToHost)); + for (int idx = 0; idx < BLOCKSIZE; idx++) { + REQUIRE(Ah.host_ptr()[idx] == (VALUE1 + VALUE2)); + } + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify hipStreamSynchronize on a stream behavior when stream capture + * on another stream is ongoing in another thread. + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.6 + */ +// Local function executed as thread +static void strmSyncThread(int *Ah, int *Ad, int *Bh, int *Bd, + int BLOCKSIZE, hipError_t *error) { + StreamsGuard stream(1); + HIP_CHECK(hipMemcpyAsync(Ad, Ah, BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream[0])); + HIP_CHECK(hipMemcpyAsync(Bd, Bh, BLOCKSIZE * sizeof(int), + hipMemcpyDefault, stream[0])); + *error = hipStreamSynchronize(stream[0]); +} + +// Local function executed as thread +static void captureStrmThread(hipGraph_t *graph, int *Ah, int *Ad, + int *Bh, int *Bd, int BLOCKSIZE, int GRIDSIZE, + hipStreamCaptureMode flag, hipError_t *error) { + StreamsGuard stream(1); + // Capture streams into graph + HIP_CHECK(hipStreamBeginCapture(stream[0], flag)); + std::thread t1(strmSyncThread, Ah, Ad, Bh, Bd, BLOCKSIZE, error); + t1.join(); + myadd<<>>(Ad, Bd); + HIP_CHECK(hipStreamEndCapture(stream[0], graph)); // End Capture +} + +TEST_CASE("Unit_hipStreamBeginCapture_StreamSync_OngoingCapture_MThread") { + constexpr int GRIDSIZE = 1; + constexpr int BLOCKSIZE = 512; + constexpr int VALUE1 = 7, VALUE2 = 11; + hipGraph_t graph{nullptr}; + // Allocate device memory + LinearAllocGuard Ah = LinearAllocGuard(LinearAllocs::malloc, + BLOCKSIZE * sizeof(int)); + LinearAllocGuard Ad = LinearAllocGuard(LinearAllocs::hipMalloc, + BLOCKSIZE * sizeof(int)); + LinearAllocGuard Bh = LinearAllocGuard(LinearAllocs::malloc, + BLOCKSIZE * sizeof(int)); + LinearAllocGuard Bd = LinearAllocGuard(LinearAllocs::hipMalloc, + BLOCKSIZE * sizeof(int)); + // Fill input data + std::fill_n(Ah.host_ptr(), BLOCKSIZE, VALUE1); + std::fill_n(Bh.host_ptr(), BLOCKSIZE, VALUE2); + // Stream create + hipError_t error = hipSuccess; + SECTION("Capture Flag = hipStreamCaptureModeGlobal Single Threaded") { + StreamsGuard stream(2); + // Capture streams into graph + HIP_CHECK(hipStreamBeginCapture(stream[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), + BLOCKSIZE * sizeof(int), hipMemcpyDefault, stream[1])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), + BLOCKSIZE * sizeof(int), hipMemcpyDefault, stream[1])); + error = hipStreamSynchronize(stream[1]); + REQUIRE(error == hipErrorStreamCaptureUnsupported); + } +#if HT_NVIDIA + SECTION("Capture Flag = hipStreamCaptureModeThreadLocal Single Threaded") { + StreamsGuard stream(2); + // Capture streams into graph + HIP_CHECK(hipStreamBeginCapture(stream[0], + hipStreamCaptureModeThreadLocal)); + HIP_CHECK(hipMemcpyAsync(Ad.ptr(), Ah.host_ptr(), + BLOCKSIZE * sizeof(int), hipMemcpyDefault, stream[1])); + HIP_CHECK(hipMemcpyAsync(Bd.ptr(), Bh.host_ptr(), + BLOCKSIZE * sizeof(int), hipMemcpyDefault, stream[1])); + error = hipStreamSynchronize(stream[1]); + REQUIRE(error == hipErrorStreamCaptureUnsupported); + } +#endif +#if HT_AMD + SECTION("Capture Flag = hipStreamCaptureModeGlobal Multithreaded") { + captureStrmThread(&graph, Ah.host_ptr(), Ad.ptr(), + Bh.host_ptr(), Bd.ptr(), BLOCKSIZE, GRIDSIZE, + hipStreamCaptureModeGlobal, &error); + REQUIRE(error == hipErrorStreamCaptureUnsupported); + } +#endif + SECTION("Capture Flag = hipStreamCaptureModeThreadLocal Multithreaded") { + captureStrmThread(&graph, Ah.host_ptr(), Ad.ptr(), + Bh.host_ptr(), Bd.ptr(), BLOCKSIZE, GRIDSIZE, + hipStreamCaptureModeThreadLocal, &error); + REQUIRE(error == hipSuccess); + } + SECTION("Capture Flag = hipStreamCaptureModeRelaxed Multithreaded") { + captureStrmThread(&graph, Ah.host_ptr(), Ad.ptr(), + Bh.host_ptr(), Bd.ptr(), BLOCKSIZE, GRIDSIZE, + hipStreamCaptureModeRelaxed, &error); + REQUIRE(error == hipSuccess); + } + if (graph != nullptr) { + hipGraphExec_t graphExec{nullptr}; + StreamsGuard stream(1); + // Execute and test the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream[0])); + HIP_CHECK(hipStreamSynchronize(stream[0])); + // Check output + HIP_CHECK(hipMemcpy(Ah.host_ptr(), Ad.ptr(), BLOCKSIZE * sizeof(int), + hipMemcpyDeviceToHost)); + for (int idx = 0; idx < BLOCKSIZE; idx++) { + REQUIRE(Ah.host_ptr()[idx] == (VALUE1 + VALUE2)); + } + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); + } +} diff --git a/catch/unit/graph/hipStreamCaptureExtModuleLaunchKernel.cc b/catch/unit/graph/hipStreamCaptureExtModuleLaunchKernel.cc new file mode 100644 index 0000000000..b1f0880f74 --- /dev/null +++ b/catch/unit/graph/hipStreamCaptureExtModuleLaunchKernel.cc @@ -0,0 +1,215 @@ +/* + Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR + IMPLIED, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ +#include +#include "hip/hip_ext.h" +#include +#include +#include +#include +#if !defined(S_IFREG) && defined(_S_IFREG) +#define S_IFREG _S_IFREG +#endif + +struct gridblockDim { + unsigned int gridX; + unsigned int gridY; + unsigned int gridZ; + unsigned int blockX; + unsigned int blockY; + unsigned int blockZ; +}; +class GraphModuleLaunchKernel { + int N = 64; + int SIZE = N*N; + int *A, *B, *C; + hipDeviceptr_t *Ad, *Bd; + hipStream_t stream1, stream2; + hipModule_t module; + hipFunction_t multKernel; + struct { + void* _Ad; + void* _Bd; + void* _Cd; + int _n; + } args1, args2; + size_t size1, size2; + + static constexpr char matmulK[] = "matmulK"; + + public : + GraphModuleLaunchKernel() { + allocateMemory(); + moduleLoad(); + } + + ~GraphModuleLaunchKernel() { + deAllocateMemory(); + } + + void allocateMemory(); + void deAllocateMemory(); + void moduleLoad(); + bool extModuleKernelExecutionMatmul(); + bool extModuleKernelExecutionMatmulwithStreamCapture(bool LaunchByDifferentStream = false); + static constexpr char fileName[] = "hipMatMul.code"; +}; + +void GraphModuleLaunchKernel::allocateMemory() { + A = new int[N*N*sizeof(int)]; + B = new int[N*N*sizeof(int)]; + for (int i=0; i < N; i++) { + for (int j=0; j < N; j++) { + A[i*N +j] = 1; + B[i*N +j] = 1; + } + } + HIPCHECK(hipStreamCreate(&stream1)); + HIPCHECK(hipStreamCreate(&stream2)); + HIPCHECK(hipMalloc(reinterpret_cast(&Ad), + SIZE*sizeof(int))); + HIPCHECK(hipMalloc(reinterpret_cast(&Bd), + SIZE*sizeof(int))); + HIPCHECK(hipHostMalloc(reinterpret_cast(&C), SIZE*sizeof(int))); + HIPCHECK(hipMemcpy(Ad, A, SIZE*sizeof(int), hipMemcpyHostToDevice)); + HIPCHECK(hipMemcpy(Bd, B, SIZE*sizeof(int), hipMemcpyHostToDevice)); + args1._Ad = Ad; + args1._Bd = Bd; + args1._Cd = C; + args1._n = N; + args2._Ad = NULL; + args2._Bd = NULL; + args2._Cd = NULL; + args2._n = 0; + size1 = sizeof(args1); + size2 = sizeof(args2); +} + +void GraphModuleLaunchKernel::moduleLoad() { + HIPCHECK(hipModuleLoad(&module, fileName)); + HIPCHECK(hipModuleGetFunction(&multKernel, module, matmulK)); +} + +void GraphModuleLaunchKernel::deAllocateMemory() { + HIPCHECK(hipStreamDestroy(stream1)); + HIPCHECK(hipStreamDestroy(stream2)); + delete[] A; + delete[] B; + HIPCHECK(hipFree(Ad)); + HIPCHECK(hipFree(Bd)); + HIPCHECK(hipHostFree(C)); + HIPCHECK(hipModuleUnload(module)); +} + +bool GraphModuleLaunchKernel::extModuleKernelExecutionMatmul() { + bool testStatus = true; + int mismatch = 0; + void* config1[] = {HIP_LAUNCH_PARAM_BUFFER_POINTER, &args1, + HIP_LAUNCH_PARAM_BUFFER_SIZE, &size1, + HIP_LAUNCH_PARAM_END}; + HIPCHECK(hipExtModuleLaunchKernel(multKernel, N, N, 1, 32, 32 , 1, 0, + stream1, NULL, + reinterpret_cast(&config1), + NULL, NULL, 0)); + HIPCHECK(hipStreamSynchronize(stream1)); + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (C[i*N + j] != N) + mismatch++; + } + } + if (mismatch) { + printf("Test failed: the result of matrix multiplications incorrect.\n"); + testStatus = false; + } + return testStatus; +} + +bool GraphModuleLaunchKernel::extModuleKernelExecutionMatmulwithStreamCapture(bool LaunchByDifferentStream) { + bool testStatus = true; + int mismatch = 0; + + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + + void* config1[] = {HIP_LAUNCH_PARAM_BUFFER_POINTER, &args1, + HIP_LAUNCH_PARAM_BUFFER_SIZE, &size1, + HIP_LAUNCH_PARAM_END}; + + HIPCHECK(hipExtModuleLaunchKernel(multKernel, N, N, 1, 32, 32 , 1, 0, + stream1, NULL, + reinterpret_cast(&config1), + NULL, NULL, 0)); + + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); + + // Validate end capture is successful + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + REQUIRE(graphExec != nullptr); + + // Replay the recorded sequence + HIP_CHECK(hipGraphLaunch(graphExec, LaunchByDifferentStream ? stream2 : stream1)); + + HIP_CHECK(hipStreamSynchronize(LaunchByDifferentStream ? stream2 : stream1)); + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (C[i*N + j] != N) + mismatch++; + } + } + if (mismatch) { + printf("Test failed: the result of matrix multiplications incorrect.\n"); + testStatus = false; + } + return testStatus; +} + +TEST_CASE("Unit_hipStreamCapture_ExtModuleLaunchKernel") { + struct stat fileStat; + if (stat(GraphModuleLaunchKernel::fileName, &fileStat) + || !(fileStat.st_mode & S_IFREG)) { + FAIL("module file " << GraphModuleLaunchKernel::fileName + << " doesn't exist! aborted! \n" + << "To generate the file, type\n" + << "/opt/rocm/hip/bin/hipcc --genco hipMatMul.cc -o hipMatMul.code"); + return; + } + HIPCHECK(hipSetDevice(0)); + GraphModuleLaunchKernel kernelLaunch; + + SECTION("extModuleKernelExecutionMatmul") { + REQUIRE(kernelLaunch.extModuleKernelExecutionMatmul()); + } + + SECTION("extModuleKernelExecutionMatmul_withStreamCapture") { + REQUIRE(kernelLaunch.extModuleKernelExecutionMatmulwithStreamCapture()); + } + + SECTION("extModuleKernelExecutionMatmul_withStreamCapture_launchByDifferentStream") { + REQUIRE(kernelLaunch.extModuleKernelExecutionMatmulwithStreamCapture(true)); + } +} diff --git a/catch/unit/graph/hipUserObjectCreate.cc b/catch/unit/graph/hipUserObjectCreate.cc index 0d67e168b0..6e086aae22 100644 --- a/catch/unit/graph/hipUserObjectCreate.cc +++ b/catch/unit/graph/hipUserObjectCreate.cc @@ -6,8 +6,10 @@ in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -18,9 +20,8 @@ THE SOFTWARE. */ #include -#include -#include +#include "user_object_common.hh" /** * Functional Test for API - hipUserObjectCreate @@ -34,76 +35,33 @@ THE SOFTWARE. hipUserObjectRelease with count as X+Y. */ -struct BoxStruct { - int count; - BoxStruct() { - INFO("Constructor called for Struct!\n"); - } - ~BoxStruct() { - INFO("Destructor called for Struct!\n"); - } -}; - -class BoxClass { - public: - BoxClass() { - INFO("Constructor called for Class!\n"); - } - ~BoxClass() { - INFO("Destructor called for Class!\n"); - } -}; - -static void destroyStructObj(void *ptr) { - BoxStruct *ptr1 = reinterpret_cast(ptr); - delete ptr1; -} - -static void destroyClassObj(void *ptr) { - BoxClass *ptr2 = reinterpret_cast(ptr); - delete ptr2; -} - -static void destroyIntObj(void *ptr) { - int *ptr2 = reinterpret_cast(ptr); - delete ptr2; -} - -static void destroyFloatObj(void *ptr) { - float *ptr2 = reinterpret_cast(ptr); - delete ptr2; -} - /* 1) Call hipUserObjectCreate once and release it by calling hipUserObjectRelease */ -static void hipUserObjectCreate_Functional_1(void *object, - void destroyObj(void *)) { +static void hipUserObjectCreate_Functional_1(void* object, void destroyObj(void*)) { hipUserObject_t hObject; - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyObj, 1, - hipUserObjectNoDestructorSync)); + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyObj, 1, hipUserObjectNoDestructorSync)); REQUIRE(hObject != nullptr); HIP_CHECK(hipUserObjectRelease(hObject)); } TEST_CASE("Unit_hipUserObjectCreate_Functional_1") { SECTION("Called with int Object") { - int *object = new int(); + int* object = new int(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_1(object, destroyIntObj); } SECTION("Called with float Object") { - float *object = new float(); + float* object = new float(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_1(object, destroyFloatObj); } SECTION("Called with Class Object") { - BoxClass *object = new BoxClass(); + BoxClass* object = new BoxClass(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_1(object, destroyClassObj); } SECTION("Called with Struct Object") { - BoxStruct *object = new BoxStruct(); + BoxStruct* object = new BoxStruct(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_1(object, destroyStructObj); } @@ -111,35 +69,33 @@ TEST_CASE("Unit_hipUserObjectCreate_Functional_1") { /* 2) Call hipUserObjectCreate refCount as X and release it by calling hipUserObjectRelease with same refCount. */ -static void hipUserObjectCreate_Functional_2(void *object, - void destroyObj(void *)) { +static void hipUserObjectCreate_Functional_2(void* object, void destroyObj(void*)) { int refCount = 5; hipUserObject_t hObject; - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyObj, - refCount, hipUserObjectNoDestructorSync)); + HIP_CHECK( + hipUserObjectCreate(&hObject, object, destroyObj, refCount, hipUserObjectNoDestructorSync)); REQUIRE(hObject != nullptr); HIP_CHECK(hipUserObjectRelease(hObject, refCount)); } TEST_CASE("Unit_hipUserObjectCreate_Functional_2") { SECTION("Called with int Object") { - int *object = new int(); + int* object = new int(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_2(object, destroyIntObj); } SECTION("Called with float Object") { - float *object = new float(); + float* object = new float(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_2(object, destroyFloatObj); } SECTION("Called with Class Object") { - BoxClass *object = new BoxClass(); + BoxClass* object = new BoxClass(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_2(object, destroyClassObj); } SECTION("Called with Struct Object") { - BoxStruct *object = new BoxStruct(); + BoxStruct* object = new BoxStruct(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_2(object, destroyStructObj); } @@ -147,12 +103,9 @@ TEST_CASE("Unit_hipUserObjectCreate_Functional_2") { /* 3) Call hipUserObjectCreate, retain it by calling hipUserObjectRetain and release it by calling hipUserObjectRelease twice. */ -static void hipUserObjectCreate_Functional_3(void *object, - void destroyObj(void *)) { +static void hipUserObjectCreate_Functional_3(void* object, void destroyObj(void*)) { hipUserObject_t hObject; - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyObj, - 1, hipUserObjectNoDestructorSync)); + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyObj, 1, hipUserObjectNoDestructorSync)); REQUIRE(hObject != nullptr); HIP_CHECK(hipUserObjectRetain(hObject)); HIP_CHECK(hipUserObjectRelease(hObject)); @@ -161,22 +114,22 @@ static void hipUserObjectCreate_Functional_3(void *object, TEST_CASE("Unit_hipUserObjectCreate_Functional_3") { SECTION("Called with int Object") { - int *object = new int(); + int* object = new int(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_3(object, destroyIntObj); } SECTION("Called with float Object") { - float *object = new float(); + float* object = new float(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_3(object, destroyFloatObj); } SECTION("Called with Class Object") { - BoxClass *object = new BoxClass(); + BoxClass* object = new BoxClass(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_3(object, destroyClassObj); } SECTION("Called with Struct Object") { - BoxStruct *object = new BoxStruct(); + BoxStruct* object = new BoxStruct(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_3(object, destroyStructObj); } @@ -185,43 +138,40 @@ TEST_CASE("Unit_hipUserObjectCreate_Functional_3") { /* 4) Call hipUserObjectCreate with refCount as X, retain it by calling hipUserObjectRetain with count as Y and release it by calling hipUserObjectRelease with count as X+Y. */ -static void hipUserObjectCreate_Functional_4(void *object, - void destroyObj(void *)) { +static void hipUserObjectCreate_Functional_4(void* object, void destroyObj(void*)) { int refCount = 5; int refCountRetain = 8; hipUserObject_t hObject; - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyObj, - refCount, hipUserObjectNoDestructorSync)); + HIP_CHECK( + hipUserObjectCreate(&hObject, object, destroyObj, refCount, hipUserObjectNoDestructorSync)); REQUIRE(hObject != nullptr); HIP_CHECK(hipUserObjectRetain(hObject, refCountRetain)); - HIP_CHECK(hipUserObjectRelease(hObject, refCount+refCountRetain)); + HIP_CHECK(hipUserObjectRelease(hObject, refCount + refCountRetain)); } TEST_CASE("Unit_hipUserObjectCreate_Functional_4") { SECTION("Called with int Object") { - int *object = new int(); + int* object = new int(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_4(object, destroyIntObj); } SECTION("Called with float Object") { - float *object = new float(); + float* object = new float(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_4(object, destroyFloatObj); } SECTION("Called with Class Object") { - BoxClass *object = new BoxClass(); + BoxClass* object = new BoxClass(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_4(object, destroyClassObj); } SECTION("Called with Struct Object") { - BoxStruct *object = new BoxStruct(); + BoxStruct* object = new BoxStruct(); REQUIRE(object != nullptr); hipUserObjectCreate_Functional_4(object, destroyStructObj); } } - /** * Negative Test for API - hipUserObjectCreate 1) Pass User Object as nullptr @@ -231,411 +181,53 @@ TEST_CASE("Unit_hipUserObjectCreate_Functional_4") { 5) Pass initialRefcount as INT_MAX 6) Pass flag other than hipUserObjectNoDestructorSync */ - TEST_CASE("Unit_hipUserObjectCreate_Negative") { - hipError_t ret; - int *object = new int(); + int* object = new int(); REQUIRE(object != nullptr); hipUserObject_t hObject; SECTION("Pass User Object as nullptr") { - ret = hipUserObjectCreate(nullptr, object, destroyIntObj, - 1, hipUserObjectNoDestructorSync); - REQUIRE(hipErrorInvalidValue == ret); + HIP_CHECK_ERROR( + hipUserObjectCreate(nullptr, object, destroyIntObj, 1, hipUserObjectNoDestructorSync), + hipErrorInvalidValue); } SECTION("Pass object as nullptr") { - ret = hipUserObjectCreate(&hObject, nullptr, destroyIntObj, - 1, hipUserObjectNoDestructorSync); - REQUIRE(hipSuccess == ret); + HIP_CHECK( + hipUserObjectCreate(&hObject, nullptr, destroyIntObj, 1, hipUserObjectNoDestructorSync)); } SECTION("Pass Callback function as nullptr") { - ret = hipUserObjectCreate(&hObject, object, nullptr, - 1, hipUserObjectNoDestructorSync); - REQUIRE(hipErrorInvalidValue == ret); + HIP_CHECK_ERROR( + hipUserObjectCreate(&hObject, object, nullptr, 1, hipUserObjectNoDestructorSync), + hipErrorInvalidValue); } SECTION("Pass initialRefcount as 0") { - ret = hipUserObjectCreate(&hObject, object, destroyIntObj, - 0, hipUserObjectNoDestructorSync); - REQUIRE(hipErrorInvalidValue == ret); + HIP_CHECK_ERROR( + hipUserObjectCreate(&hObject, object, destroyIntObj, 0, hipUserObjectNoDestructorSync), + hipErrorInvalidValue); } SECTION("Pass initialRefcount as INT_MAX") { - ret = hipUserObjectCreate(&hObject, object, destroyIntObj, - INT_MAX, hipUserObjectNoDestructorSync); - REQUIRE(hipSuccess == ret); + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyIntObj, INT_MAX, + hipUserObjectNoDestructorSync)); } SECTION("Pass flag other than hipUserObjectNoDestructorSync") { - ret = hipUserObjectCreate(&hObject, object, destroyIntObj, - 1, hipUserObjectFlags(9)); - REQUIRE(hipErrorInvalidValue == ret); - } -} - -/** - * Negative Test for API - hipUserObjectRelease - 1) Pass User Object as nullptr - 2) Pass initialRefcount as 0 - 3) Pass initialRefcount as INT_MAX - */ - -TEST_CASE("Unit_hipUserObjectRelease_Negative") { - hipError_t ret; - int *object = new int(); - REQUIRE(object != nullptr); - - hipUserObject_t hObject; - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyIntObj, - 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - - SECTION("Pass User Object as nullptr") { - ret = hipUserObjectRelease(nullptr, 1); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as 0") { - ret = hipUserObjectRelease(hObject, 0); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as INT_MAX") { - ret = hipUserObjectRelease(hObject, INT_MAX); - REQUIRE(hipSuccess == ret); - } -} - -/** - * Negative Test for API - hipUserObjectRetain - 1) Pass User Object as nullptr - 2) Pass initialRefcount as 0 - 3) Pass initialRefcount as INT_MAX - */ - -TEST_CASE("Unit_hipUserObjectRetain_Negative") { - hipError_t ret; - int *object = new int(); - REQUIRE(object != nullptr); - - hipUserObject_t hObject; - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyIntObj, - 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - - SECTION("Pass User Object as nullptr") { - ret = hipUserObjectRetain(nullptr, 1); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as 0") { - ret = hipUserObjectRetain(hObject, 0); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as INT_MAX") { - ret = hipUserObjectRetain(hObject, INT_MAX); - REQUIRE(hipSuccess == ret); + HIP_CHECK_ERROR(hipUserObjectCreate(&hObject, object, destroyIntObj, 1, hipUserObjectFlags(9)), + hipErrorInvalidValue); } } TEST_CASE("Unit_hipUserObj_Negative_Test") { - hipError_t ret; - int *object = new int(); + int* object = new int(); REQUIRE(object != nullptr); hipUserObject_t hObject; + // Create a new hObject with 2 reference - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyIntObj, - 2, hipUserObjectNoDestructorSync)); + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyIntObj, 2, hipUserObjectNoDestructorSync)); REQUIRE(hObject != nullptr); // Release more than created. - ret = hipUserObjectRelease(hObject, 4); - REQUIRE(hipSuccess == ret); + HIP_CHECK(hipUserObjectRelease(hObject, 4)); // Retain reference to a removed user object - ret = hipUserObjectRetain(hObject, 1); - REQUIRE(hipSuccess == ret); + HIP_CHECK(hipUserObjectRetain(hObject, 1)); } - -/** - * Functional Test for API - hipGraphRetainUserObject - */ - -/* 1) Create GraphUserObject and retain it by calling hipGraphRetainUserObject - and release it by calling hipGraphReleaseUserObject. */ - -static void hipGraphRetainUserObject_Functional_1(void *object, - void destroyObj(void *)) { - hipGraph_t graph; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - hipUserObject_t hObject; - - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyObj, - 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 1, - hipGraphUserObjectMove)); - - HIP_CHECK(hipGraphReleaseUserObject(graph, hObject)); - HIP_CHECK(hipUserObjectRelease(hObject)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -TEST_CASE("Unit_hipGraphRetainUserObject_Functional_1") { - SECTION("Called with int Object") { - int *object = new int(); - REQUIRE(object != nullptr); - hipGraphRetainUserObject_Functional_1(object, destroyIntObj); - } - SECTION("Called with float Object") { - float *object = new float(); - REQUIRE(object != nullptr); - hipGraphRetainUserObject_Functional_1(object, destroyFloatObj); - } - SECTION("Called with Class Object") { - BoxClass *object = new BoxClass(); - REQUIRE(object != nullptr); - hipGraphRetainUserObject_Functional_1(object, destroyClassObj); - } - SECTION("Called with Struct Object") { - BoxStruct *object = new BoxStruct(); - REQUIRE(object != nullptr); - hipGraphRetainUserObject_Functional_1(object, destroyStructObj); - } -} - -/* 2) Create UserObject and GraphUserObject and retain using custom reference - count and release it by calling hipGraphReleaseUserObject with count. */ - -TEST_CASE("Unit_hipGraphRetainUserObject_Functional_2") { - constexpr size_t N = 1024; - constexpr size_t Nbytes = N * sizeof(int); - constexpr auto blocksPerCU = 6; // to hide latency - constexpr auto threadsPerBlock = 256; - hipGraph_t graph; - hipGraphNode_t memcpyNode, kNode; - hipKernelNodeParams kNodeParams{}; - hipStream_t streamForGraph; - int *A_d, *B_d, *C_d; - int *A_h, *B_h, *C_h; - std::vector dependencies; - hipGraphExec_t graphExec; - size_t NElem{N}; - - HIP_CHECK(hipStreamCreate(&streamForGraph)); - HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - - HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, nullptr, 0, A_d, A_h, Nbytes, - hipMemcpyHostToDevice)); - dependencies.push_back(memcpyNode); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, nullptr, 0, B_d, B_h, Nbytes, - hipMemcpyHostToDevice)); - dependencies.push_back(memcpyNode); - - void* kernelArgs[] = {&A_d, &B_d, &C_d, reinterpret_cast(&NElem)}; - kNodeParams.func = reinterpret_cast(HipTest::vectorADD); - kNodeParams.gridDim = dim3(blocks); - kNodeParams.blockDim = dim3(threadsPerBlock); - kNodeParams.sharedMemBytes = 0; - kNodeParams.kernelParams = reinterpret_cast(kernelArgs); - kNodeParams.extra = nullptr; - HIP_CHECK( - hipGraphAddKernelNode(&kNode, graph, dependencies.data(), dependencies.size(), &kNodeParams)); - - dependencies.clear(); - dependencies.push_back(kNode); - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyNode, graph, dependencies.data(), dependencies.size(), - C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); - - float* object = new float(); - REQUIRE(object != nullptr); - hipUserObject_t hObject; - - HIP_CHECK( - hipUserObjectCreate(&hObject, object, destroyFloatObj, 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 1, - hipGraphUserObjectMove)); // Pass ownership to hipGraph - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, NULL, NULL, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); - - // Verify result - HipTest::checkVectorADD(A_h, B_h, C_h, N); - - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); -} - - -/** - * Negative Test for API - hipGraphRetainUserObject - 1) Pass graph as nullptr - 2) Pass User Object as nullptr - 3) Pass initialRefcount as 0 - 4) Pass initialRefcount as INT_MAX - 5) Pass flag as 0 - 6) Pass flag as INT_MAX - */ - -TEST_CASE("Unit_hipGraphRetainUserObject_Negative") { - hipError_t ret; - hipGraph_t graph; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - float *object = new float(); - REQUIRE(object != nullptr); - hipUserObject_t hObject; - - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyFloatObj, - 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - - SECTION("Pass graph as nullptr") { - ret = hipGraphRetainUserObject(nullptr, hObject, 1, - hipGraphUserObjectMove); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass User Object as nullptr") { - ret = hipGraphRetainUserObject(graph, nullptr, 1, - hipGraphUserObjectMove); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as 0") { - ret = hipGraphRetainUserObject(graph, hObject, 0, - hipGraphUserObjectMove); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as INT_MAX") { - ret = hipGraphRetainUserObject(graph, hObject, INT_MAX, - hipGraphUserObjectMove); - REQUIRE(hipSuccess == ret); - } - SECTION("Pass flag as 0") { - ret = hipGraphRetainUserObject(graph, hObject, 1, 0); - REQUIRE(hipSuccess == ret); - } - SECTION("Pass flag as INT_MAX") { - ret = hipGraphRetainUserObject(graph, hObject, 1, INT_MAX); - REQUIRE(hipErrorInvalidValue == ret); - } - - HIP_CHECK(hipUserObjectRelease(hObject, 1)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -/** - * Negative Test for API - hipGraphReleaseUserObject - 1) Pass graph as nullptr - 2) Pass User Object as nullptr - 3) Pass initialRefcount as 0 - 4) Pass initialRefcount as INT_MAX - */ - -TEST_CASE("Unit_hipGraphReleaseUserObject_Negative") { - hipError_t ret; - hipGraph_t graph; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - float *object = new float(); - REQUIRE(object != nullptr); - hipUserObject_t hObject; - - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyFloatObj, - 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 1, - hipGraphUserObjectMove)); - - SECTION("Pass graph as nullptr") { - ret = hipGraphReleaseUserObject(nullptr, hObject, 1); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass User Object as nullptr") { - ret = hipGraphReleaseUserObject(graph, nullptr, 1); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as 0") { - ret = hipGraphReleaseUserObject(graph, hObject, 0); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass initialRefcount as INT_MAX") { - ret = hipGraphReleaseUserObject(graph, hObject, INT_MAX); - REQUIRE(hipSuccess == ret); - } - HIP_CHECK(hipUserObjectRelease(hObject, 1)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -TEST_CASE("Unit_hipGraphRetainUserObject_Negative_Basic") { - hipError_t ret; - hipGraph_t graph; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - float *object = new float(); - REQUIRE(object != nullptr); - hipUserObject_t hObject; - - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyFloatObj, - 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - // Retain graph object with reference count 2 - HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 2, - hipGraphUserObjectMove)); - - // Release graph object with reference count more than 2 - ret = hipGraphReleaseUserObject(graph, hObject, 4); - REQUIRE(hipSuccess == ret); - - // Again Retain graph object with reference count 8 - ret = hipGraphRetainUserObject(graph, hObject, 8, - hipGraphUserObjectMove); - REQUIRE(hipSuccess == ret); - // Release graph object with reference count 1 - ret = hipGraphReleaseUserObject(graph, hObject, 1); - REQUIRE(hipSuccess == ret); - - HIP_CHECK(hipUserObjectRelease(hObject, 1)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -TEST_CASE("Unit_hipGraphRetainUserObject_Negative_Null_Object") { - hipError_t ret; - hipGraph_t graph; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - float *object = nullptr; // this is used for Null_Object test - hipUserObject_t hObject; - - HIP_CHECK(hipUserObjectCreate(&hObject, object, - destroyFloatObj, - 1, hipUserObjectNoDestructorSync)); - REQUIRE(hObject != nullptr); - // Retain graph object with reference count 2 - HIP_CHECK(hipGraphRetainUserObject(graph, hObject, 2, - hipGraphUserObjectMove)); - - // Release graph object with reference count more than 2 - ret = hipGraphReleaseUserObject(graph, hObject, 4); - REQUIRE(hipSuccess == ret); - - // Again Retain graph object with reference count 8 - ret = hipGraphRetainUserObject(graph, hObject, 8, 0); - REQUIRE(hipSuccess == ret); - // Release graph object with reference count 1 - ret = hipGraphReleaseUserObject(graph, hObject, 1); - REQUIRE(hipSuccess == ret); - - HIP_CHECK(hipUserObjectRelease(hObject, 1)); - HIP_CHECK(hipGraphDestroy(graph)); -} - diff --git a/catch/unit/graph/hipUserObjectRelease.cc b/catch/unit/graph/hipUserObjectRelease.cc new file mode 100644 index 0000000000..bb73fdeac2 --- /dev/null +++ b/catch/unit/graph/hipUserObjectRelease.cc @@ -0,0 +1,48 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include + +#include "user_object_common.hh" + +/** + * Negative Test for API - hipUserObjectRelease + 1) Pass User Object as nullptr + 2) Pass initialRefcount as 0 + 3) Pass initialRefcount as INT_MAX + */ +TEST_CASE("Unit_hipUserObjectRelease_Negative") { + int* object = new int(); + REQUIRE(object != nullptr); + + hipUserObject_t hObject; + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyIntObj, 1, hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + + SECTION("Pass User Object as nullptr") { + HIP_CHECK_ERROR(hipUserObjectRelease(nullptr, 1), hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as 0") { + HIP_CHECK_ERROR(hipUserObjectRelease(hObject, 0), hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as INT_MAX") { HIP_CHECK(hipUserObjectRelease(hObject, INT_MAX)); } +} \ No newline at end of file diff --git a/catch/unit/graph/hipUserObjectRetain.cc b/catch/unit/graph/hipUserObjectRetain.cc new file mode 100644 index 0000000000..895c898482 --- /dev/null +++ b/catch/unit/graph/hipUserObjectRetain.cc @@ -0,0 +1,48 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include + +#include "user_object_common.hh" + +/** + * Negative Test for API - hipUserObjectRetain + 1) Pass User Object as nullptr + 2) Pass initialRefcount as 0 + 3) Pass initialRefcount as INT_MAX + */ +TEST_CASE("Unit_hipUserObjectRetain_Negative") { + int* object = new int(); + REQUIRE(object != nullptr); + + hipUserObject_t hObject; + HIP_CHECK(hipUserObjectCreate(&hObject, object, destroyIntObj, 1, hipUserObjectNoDestructorSync)); + REQUIRE(hObject != nullptr); + + SECTION("Pass User Object as nullptr") { + HIP_CHECK_ERROR(hipUserObjectRetain(nullptr, 1), hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as 0") { + HIP_CHECK_ERROR(hipUserObjectRetain(hObject, 0), hipErrorInvalidValue); + } + SECTION("Pass initialRefcount as INT_MAX") { HIP_CHECK(hipUserObjectRetain(hObject, INT_MAX)); } +} \ No newline at end of file diff --git a/catch/unit/graph/user_object_common.hh b/catch/unit/graph/user_object_common.hh new file mode 100644 index 0000000000..1929199b43 --- /dev/null +++ b/catch/unit/graph/user_object_common.hh @@ -0,0 +1,61 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +#include + +struct BoxStruct { + int count; + BoxStruct() { INFO("Constructor called for Struct!\n"); } + ~BoxStruct() { INFO("Destructor called for Struct!\n"); } +}; + +class BoxClass { + public: + BoxClass() { INFO("Constructor called for Class!\n"); } + ~BoxClass() { INFO("Destructor called for Class!\n"); } +}; + +namespace { + +void destroyStructObj(void* ptr) { + BoxStruct* ptr1 = reinterpret_cast(ptr); + delete ptr1; +} + +void destroyClassObj(void* ptr) { + BoxClass* ptr2 = reinterpret_cast(ptr); + delete ptr2; +} + +void destroyIntObj(void* ptr) { + int* ptr2 = reinterpret_cast(ptr); + delete ptr2; +} + +void destroyFloatObj(void* ptr) { + float* ptr2 = reinterpret_cast(ptr); + delete ptr2; +} + +} // anonymous namespace \ No newline at end of file diff --git a/catch/unit/memory/hipHmmOvrSubscriptionTst.cc b/catch/unit/memory/hipHmmOvrSubscriptionTst.cc index de5d48ee4b..22fd5eb4b6 100644 --- a/catch/unit/memory/hipHmmOvrSubscriptionTst.cc +++ b/catch/unit/memory/hipHmmOvrSubscriptionTst.cc @@ -140,6 +140,14 @@ static void DisplayHmmFlgs(int *Signal) { } TEST_CASE("Unit_HMM_OverSubscriptionTst") { + hipDeviceProp_t prop; + HIP_CHECK(hipGetDeviceProperties(&prop, 0)); + char *p = nullptr; + p = strstr(prop.gcnArchName, "xnack+"); + if (p == nullptr) { + INFO("Skipped due current device is non xnack device."); + return; + } int HmmEnabled = 0; // The following Shared Mem is to get Max GPU Mem // The size requested is for three ints diff --git a/catch/unit/memory/hipMalloc3DArray.cc b/catch/unit/memory/hipMalloc3DArray.cc index 73d50ad943..d5b840d7dd 100644 --- a/catch/unit/memory/hipMalloc3DArray.cc +++ b/catch/unit/memory/hipMalloc3DArray.cc @@ -55,8 +55,6 @@ static void Malloc3DArray_DiffSizes(int gpu) { size_t width{size}, height{size}, depth{size}; hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); std::array arr; - size_t pavail, avail; - HIP_CHECK_THREAD(hipMemGetInfo(&pavail, nullptr)); for (int i = 0; i < ARRAY_LOOP; i++) { HIP_CHECK_THREAD(hipMalloc3DArray(&arr[i], &channelDesc, make_hipExtent(width, height, depth), @@ -65,9 +63,6 @@ static void Malloc3DArray_DiffSizes(int gpu) { for (int i = 0; i < ARRAY_LOOP; i++) { HIP_CHECK_THREAD(hipFreeArray(arr[i])); } - - HIP_CHECK_THREAD(hipMemGetInfo(&avail, nullptr)); - REQUIRE_THREAD(pavail == avail); } } @@ -85,7 +80,6 @@ TEST_CASE("Unit_hipMalloc3DArray_MultiThread") { std::vector threadlist; int devCnt = 0; devCnt = HipTest::getDeviceCount(); - const auto pavail = getFreeMem(); for (int i = 0; i < devCnt; i++) { threadlist.push_back(std::thread(Malloc3DArray_DiffSizes, i)); } @@ -94,12 +88,6 @@ TEST_CASE("Unit_hipMalloc3DArray_MultiThread") { t.join(); } HIP_CHECK_THREAD_FINALIZE(); - const auto avail = getFreeMem(); - - if (pavail != avail) { - WARN("Memory leak of hipMalloc3D API in multithreaded scenario"); - REQUIRE(false); - } } namespace { diff --git a/catch/unit/memory/hipMallocArray.cc b/catch/unit/memory/hipMallocArray.cc index 530eb11077..1ea94d2146 100644 --- a/catch/unit/memory/hipMallocArray.cc +++ b/catch/unit/memory/hipMallocArray.cc @@ -57,9 +57,6 @@ static void MallocArray_DiffSizes(int gpu) { for (const auto& size : runs) { hipChannelFormatDesc desc = hipCreateChannelDesc(); std::array A_d; - size_t pavail, avail; - HIP_CHECK_THREAD(hipMemGetInfo(&pavail, nullptr)); - for (int i = 0; i < ARRAY_LOOP; i++) { HIP_CHECK_THREAD( hipMallocArray(&A_d[i], &desc, std::get<0>(size), std::get<1>(size), hipArrayDefault)); @@ -67,9 +64,6 @@ static void MallocArray_DiffSizes(int gpu) { for (int i = 0; i < ARRAY_LOOP; i++) { HIP_CHECK_THREAD(hipFreeArray(A_d[i])); } - - HIP_CHECK_THREAD(hipMemGetInfo(&avail, nullptr)); - REQUIRE_THREAD(pavail == avail); } } @@ -87,7 +81,6 @@ TEST_CASE("Unit_hipMallocArray_MultiThread") { std::vector threadlist; int devCnt = 0; devCnt = HipTest::getDeviceCount(); - const auto pavail = getFreeMem(); for (int i = 0; i < devCnt; i++) { threadlist.push_back(std::thread(MallocArray_DiffSizes, i)); } @@ -96,12 +89,6 @@ TEST_CASE("Unit_hipMallocArray_MultiThread") { t.join(); } HIP_CHECK_THREAD_FINALIZE(); - - const auto avail = getFreeMem(); - if (pavail != avail) { - WARN("Memory leak of hipMalloc3D API in multithreaded scenario"); - REQUIRE(false); - } } // Kernels /////////////////////////////////////// diff --git a/catch/unit/memory/hipMallocConcurrency.cc b/catch/unit/memory/hipMallocConcurrency.cc index 885a13ee7e..17028d232d 100644 --- a/catch/unit/memory/hipMallocConcurrency.cc +++ b/catch/unit/memory/hipMallocConcurrency.cc @@ -85,7 +85,6 @@ static std::atomic g_thTestPassed{true}; static bool validateMemoryOnGPU(int gpu, bool concurOnOneGPU = false) { int *A_d, *B_d, *C_d; int *A_h, *B_h, *C_h; - size_t prevAvl, prevTot, curAvl, curTot; bool TestPassed = true; constexpr auto N = 4 * 1024 * 1024; constexpr auto blocksPerCU = 6; // to hide latency @@ -93,16 +92,7 @@ static bool validateMemoryOnGPU(int gpu, bool concurOnOneGPU = false) { size_t Nbytes = N * sizeof(int); HIP_CHECK(hipSetDevice(gpu)); - HIP_CHECK(hipMemGetInfo(&prevAvl, &prevTot)); HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); - HIP_CHECK(hipMemGetInfo(&curAvl, &curTot)); - - if (!concurOnOneGPU && (prevAvl < curAvl || prevTot != curTot)) { - //In concurrent calls on one GPU, we cannot verify leaking in this way - printf("%s : Memory allocation mismatch observed." - "Possible memory leak.\n", __func__); - TestPassed &= false; - } unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); @@ -121,16 +111,7 @@ static bool validateMemoryOnGPU(int gpu, bool concurOnOneGPU = false) { TestPassed = false; } - HIP_CHECK(hipMemGetInfo(&prevAvl, &prevTot)); HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HIP_CHECK(hipMemGetInfo(&curAvl, &curTot)); - - if (!concurOnOneGPU && (curAvl < prevAvl || prevTot != curTot)) { - // In concurrent calls on one GPU, we cannot verify leaking in this way - UNSCOPED_INFO("validateMemoryOnGPU : Memory allocation mismatch observed." - << "Possible memory leak."); - TestPassed = false; - } return TestPassed; } @@ -140,8 +121,7 @@ static bool validateMemoryOnGPU(int gpu, bool concurOnOneGPU = false) { * Regress memory allocation and free in loop */ static bool regressAllocInLoop(int gpu) { - bool TestPassed = true; - size_t tot, avail, ptot, pavail, numBytes; + size_t numBytes; int i = 0; int* ptr; @@ -150,41 +130,18 @@ static bool regressAllocInLoop(int gpu) { // Exercise allocation in loop with bigger chunks for (i = 0; i < MaxAllocFree_BigChunks; i++) { - HIP_CHECK(hipMemGetInfo(&pavail, &ptot)); HIP_CHECK(hipMalloc(&ptr, numBytes)); - HIP_CHECK(hipMemGetInfo(&avail, &tot)); HIP_CHECK(hipFree(ptr)); - - if (pavail - avail < numBytes) { // We expect pavail-avail >= numBytes - UNSCOPED_INFO("LoopAllocation " << i << " : Memory allocation of " << numBytes - << " not matching with hipMemGetInfo - FAIL." - << "pavail=" << pavail << ", ptot=" << ptot - << ", avail=" << avail << ", tot=" << tot - << ", pavail-avail=" << pavail - avail); - TestPassed = false; - break; - } } // Exercise allocation in loop with smaller chunks and maximum iters - HIP_CHECK(hipMemGetInfo(&pavail, &ptot)); numBytes = BuffSizeSC; for (i = 0; i < MaxAllocFree_SmallChunks; i++) { HIP_CHECK(hipMalloc(&ptr, numBytes)); - HIP_CHECK(hipFree(ptr)); } - - HIP_CHECK(hipMemGetInfo(&avail, &tot)); - - if ((pavail != avail) || (ptot != tot)) { - UNSCOPED_INFO("LoopAllocation : Memory allocation mismatch observed." - << "Possible memory leak."); - TestPassed &= false; - } - - return TestPassed; + return true; } /** @@ -194,23 +151,13 @@ static bool regressAllocInLoop(int gpu) { static bool validateMemoryOnGpuMThread(int gpu, bool concurOnOneGPU = false) { int *A_d, *B_d, *C_d; int *A_h, *B_h, *C_h; - size_t prevAvl, prevTot, curAvl, curTot; bool TestPassed = true; constexpr auto N = 4 * 1024 * 1024; constexpr auto blocksPerCU = 6; // to hide latency constexpr auto threadsPerBlock = 256; size_t Nbytes = N * sizeof(int); HIPCHECK(hipSetDevice(gpu)); - HIPCHECK(hipMemGetInfo(&prevAvl, &prevTot)); HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); - HIPCHECK(hipMemGetInfo(&curAvl, &curTot)); - - if (!concurOnOneGPU && (prevAvl < curAvl || prevTot != curTot)) { - //In concurrent calls on one GPU, we cannot verify leaking in this way - printf("%s : Memory allocation mismatch observed." - "Possible memory leak.\n", __func__); - TestPassed &= false; - } unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); @@ -229,24 +176,7 @@ static bool validateMemoryOnGpuMThread(int gpu, bool concurOnOneGPU = false) { TestPassed = false; } - HIPCHECK(hipMemGetInfo(&prevAvl, &prevTot)); HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HIPCHECK(hipMemGetInfo(&curAvl, &curTot)); - - if (!concurOnOneGPU && (curAvl < prevAvl || prevTot != curTot)) { - // In concurrent calls on one GPU, we cannot verify leaking in this way - UNSCOPED_INFO("validateMemoryOnGPU : Memory allocation mismatch observed." - << "Possible memory leak."); - TestPassed = false; - } - - if (!concurOnOneGPU && (prevAvl != curAvl || prevTot != curTot)) { - // In concurrent calls on one GPU, we cannot verify leaking in this way - UNSCOPED_INFO( - "validateMemoryOnGpuMThread : Memory allocation mismatch observed." - "Possible memory leak."); - TestPassed = false; - } return TestPassed; } @@ -256,8 +186,7 @@ static bool validateMemoryOnGpuMThread(int gpu, bool concurOnOneGPU = false) { * In Multithreaded Environment */ static bool regressAllocInLoopMthread(int gpu) { - bool TestPassed = true; - size_t tot, avail, ptot, pavail, numBytes; + size_t numBytes; int i = 0; int* ptr; @@ -266,41 +195,18 @@ static bool regressAllocInLoopMthread(int gpu) { // Exercise allocation in loop with bigger chunks for (i = 0; i < MaxAllocFree_BigChunks; i++) { - HIPCHECK(hipMemGetInfo(&pavail, &ptot)); HIPCHECK(hipMalloc(&ptr, numBytes)); - HIPCHECK(hipMemGetInfo(&avail, &tot)); HIPCHECK(hipFree(ptr)); - - if (pavail - avail < numBytes) { // We expect pavail-avail >= numBytes - UNSCOPED_INFO("LoopAllocation " << i << " : Memory allocation of " << numBytes - << " not matching with hipMemGetInfo - FAIL." - << "pavail=" << pavail << ", ptot=" << ptot - << ", avail=" << avail << ", tot=" << tot - << ", pavail-avail=" << pavail - avail); - TestPassed = false; - break; - } } // Exercise allocation in loop with smaller chunks and maximum iters - HIPCHECK(hipMemGetInfo(&pavail, &ptot)); numBytes = BuffSizeSC; - for (i = 0; i < MaxAllocFree_SmallChunks; i++) { HIPCHECK(hipMalloc(&ptr, numBytes)); - HIPCHECK(hipFree(ptr)); } - HIPCHECK(hipMemGetInfo(&avail, &tot)); - - if ((pavail != avail) || (ptot != tot)) { - UNSCOPED_INFO("LoopAllocation : Memory allocation mismatch observed." - << "Possible memory leak."); - TestPassed &= false; - } - - return TestPassed; + return true; } /* @@ -359,7 +265,7 @@ TEST_CASE("Unit_hipMalloc_LoopRegressionAllocFreeCycles") { * of time. */ TEST_CASE("Unit_hipMalloc_AllocateAndPoolBuffers") { - size_t avail{0}, tot{0}, pavail{0}, ptot{0}; + size_t avail{0}, tot{0}; bool ret{false}; hipError_t err{}; std::vector ptrlist{}; @@ -370,8 +276,6 @@ TEST_CASE("Unit_hipMalloc_AllocateAndPoolBuffers") { HIP_CHECK(hipGetDeviceCount(&devCnt)); REQUIRE(devCnt > 0); - HIP_CHECK(hipMemGetInfo(&pavail, &ptot)); - // Allocate small chunks of memory million times for (int i = 0; i < MaxAllocPoolIter; i++) { if ((err = hipMalloc(&ptr, BuffSize)) != hipSuccess) { @@ -392,13 +296,8 @@ TEST_CASE("Unit_hipMalloc_AllocateAndPoolBuffers") { for (auto& t : ptrlist) { HIP_CHECK(hipFree(t)); } - - HIP_CHECK(hipMemGetInfo(&avail, &tot)); - ret = validateMemoryOnGPU(0); REQUIRE(ret == true); - REQUIRE(pavail == avail); - REQUIRE(ptot == tot); } diff --git a/catch/unit/memory/hipMallocPitch.cc b/catch/unit/memory/hipMallocPitch.cc index 5a20671e14..19c803c7c2 100644 --- a/catch/unit/memory/hipMallocPitch.cc +++ b/catch/unit/memory/hipMallocPitch.cc @@ -346,8 +346,6 @@ static void MemoryAllocDiffSizes(int gpu) { } else { width = LARGECHUNK_NUMW * sizeof(T); } - size_t tot, avail, ptot, pavail; - HIP_CHECK(hipMemGetInfo(&pavail, &ptot)); for (int i = 0; i < CHUNK_LOOP; i++) { HIP_CHECK(hipMallocPitch(reinterpret_cast(&A_d[i]), &pitch_A, width, sizes)); @@ -355,10 +353,6 @@ static void MemoryAllocDiffSizes(int gpu) { for (int i = 0; i < CHUNK_LOOP; i++) { HIP_CHECK(hipFree(A_d[i])); } - HIP_CHECK(hipMemGetInfo(&avail, &tot)); - if (pavail != avail) { - HIPASSERT(false); - } } } @@ -487,9 +481,6 @@ TEST_CASE("Unit_hipMallocPitch_MultiThread", "") { int devCnt = 0; devCnt = HipTest::getDeviceCount(); - - size_t tot, avail, ptot, pavail; - HIP_CHECK(hipMemGetInfo(&pavail, &ptot)); for (int i = 0; i < devCnt; i++) { threadlist.push_back(std::thread(threadFunc, i)); } @@ -497,12 +488,6 @@ TEST_CASE("Unit_hipMallocPitch_MultiThread", "") { for (auto &t : threadlist) { t.join(); } - HIP_CHECK(hipMemGetInfo(&avail, &tot)); - - if (pavail != avail) { - WARN("Memory leak of hipMallocPitch API in multithreaded scenario"); - REQUIRE(false); - } } /* diff --git a/catch/unit/occupancy/hipOccupancyMaxPotentialBlockSizeVariableSMemWithFlags.cc b/catch/unit/occupancy/hipOccupancyMaxPotentialBlockSizeVariableSMemWithFlags.cc index 6da8f3ce92..2adf25797a 100644 --- a/catch/unit/occupancy/hipOccupancyMaxPotentialBlockSizeVariableSMemWithFlags.cc +++ b/catch/unit/occupancy/hipOccupancyMaxPotentialBlockSizeVariableSMemWithFlags.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2022 - 2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -211,13 +211,11 @@ TEST_CASE("Unit_hipOccupancyMaxPotBlkSizeVariableSMemWithFlags_NegTst") { blockSizeToDynamicSMemSize, 0, 0); REQUIRE(ret == hipErrorInvalidValue); } -#if HT_NVIDIA SECTION("invalid flag") { ret = hipOccupancyMaxPotentialBlockSizeVariableSMemWithFlags(&minGridSize, &blockSize, f1, blockSizeToDynamicSMemSize, 0, 0xffff); REQUIRE(ret == hipErrorInvalidValue); } -#endif } /** diff --git a/catch/unit/rtc/CMakeLists.txt b/catch/unit/rtc/CMakeLists.txt index 7d4b4893ac..42d5ec5d9a 100644 --- a/catch/unit/rtc/CMakeLists.txt +++ b/catch/unit/rtc/CMakeLists.txt @@ -3,6 +3,7 @@ set(TEST_SRC saxpy.cc warpsize.cc hipRtcFunctional.cc + hipStreamCaptureRtc.cc ) # AMD only tests diff --git a/catch/unit/rtc/hipStreamCaptureRtc.cc b/catch/unit/rtc/hipStreamCaptureRtc.cc new file mode 100644 index 0000000000..ab5296c4d5 --- /dev/null +++ b/catch/unit/rtc/hipStreamCaptureRtc.cc @@ -0,0 +1,136 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include + +static constexpr auto kernel_src { + R"_KERN_EMBED_( + extern "C" __global__ void kernel_func(float* f) + { + f[0] = 1.0; + } + )_KERN_EMBED_" +}; + + +TEST_CASE("Unit_hipStreamCaptureRtc") { + hipStream_t stream = nullptr; + hipGraph_t graph = nullptr; + hipGraphExec_t graph_exec = nullptr; + + float data_h = 0.0; + float* data_d = nullptr; + + // Init data + HIPCHECK(hipMalloc(&data_d, sizeof(float))); + HIPCHECK(hipMemcpy(data_d, &data_h, sizeof(float), hipMemcpyHostToDevice)); + + // Compile kernel + std::vector code; + hiprtcProgram prog; + HIPRTC_CHECK(hiprtcCreateProgram(&prog, kernel_src, "hipStreamCaptureRtc.cu", 0, nullptr, nullptr)); + + hipDeviceProp_t props; + int device = 0; + HIP_CHECK(hipSetDevice(device)); + HIP_CHECK(hipGetDeviceProperties(&props, device)); +#ifdef __HIP_PLATFORM_AMD__ + std::string sarg = std::string("--gpu-architecture=") + props.gcnArchName; +#else + std::string sarg = std::string("--fmad=false"); +#endif + + std::vector options = { sarg.c_str() }; + + auto compileResult = hiprtcCompileProgram(prog, options.size(), options.data()); + if (compileResult != HIPRTC_SUCCESS) { + size_t logSize = 0; + hiprtcGetProgramLogSize(prog, &logSize); + if (logSize) { + std::vector log(logSize, '\0'); + if (hiprtcGetProgramLog(prog, log.data()) == HIPRTC_SUCCESS) { + FAIL("hiprtcCompileProgram failed with log" << log.data()); + return; + } + } + FAIL("hiprtcCompileProgram failed without log"); + return; + } + + size_t codeSize = 0; + HIPRTC_CHECK(hiprtcGetCodeSize(prog, &codeSize)); + + code.resize(codeSize); + HIPRTC_CHECK(hiprtcGetCode(prog, code.data())); + HIPRTC_CHECK(hiprtcDestroyProgram(&prog)); + + hipModule_t module = nullptr; + hipFunction_t kernel = nullptr; +#if HT_NVIDIA + HIPCHECK(hipInit(0)); + hipCtx_t ctx; + HIPCHECK(hipCtxCreate(&ctx, 0, device)); +#endif + + HIPCHECK(hipModuleLoadData(&module, code.data())); + + HIPCHECK(hipModuleGetFunction(&kernel, module, "kernel_func")); + + // Start capture + HIPCHECK(hipStreamCreate(&stream)); + HIPCHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + + // Launch kernel + auto size = sizeof(float*); + void *config[] = { HIP_LAUNCH_PARAM_BUFFER_POINTER, &data_d, + HIP_LAUNCH_PARAM_BUFFER_SIZE, &size, HIP_LAUNCH_PARAM_END }; + HIPCHECK(hipModuleLaunchKernel(kernel, 1, 1, 1, 1, 1, 1, 0, stream, nullptr, config)); + HIPCHECK(hipStreamEndCapture(stream, &graph)); + + size_t numNodes = 0; + HIPCHECK(hipGraphGetNodes(graph, nullptr, &numNodes)); + INFO("Num of nodes returned by GetNodes : " << numNodes); + REQUIRE(numNodes == 1); + + // Ensure that no actual work has been done for the captured + // stream before graph execution + float tmp = 2.0; + HIPCHECK(hipMemcpy(&tmp, data_d, sizeof(float), hipMemcpyDeviceToHost)); + REQUIRE(tmp == 0.0); + + HIPCHECK(hipGraphInstantiate(&graph_exec, graph, NULL, NULL, 0)); + HIPCHECK(hipGraphDestroy(graph)); + + HIPCHECK(hipGraphLaunch(graph_exec, stream)); + + HIPCHECK(hipStreamSynchronize(stream)); + HIPCHECK(hipStreamDestroy(stream)); + + // Check that the work was done + HIPCHECK(hipMemcpy(&tmp, data_d, sizeof(float), hipMemcpyDeviceToHost)); + HIPCHECK(hipFree(data_d)); + + REQUIRE(tmp == 1.0); +#if HT_NVIDIA + HIPCHECK(hipCtxDestroy(ctx)); +#endif +} diff --git a/catch/unit/stream/hipStreamCreateWithPriority.cc b/catch/unit/stream/hipStreamCreateWithPriority.cc index 39b4b98061..03b34e4da1 100644 --- a/catch/unit/stream/hipStreamCreateWithPriority.cc +++ b/catch/unit/stream/hipStreamCreateWithPriority.cc @@ -1,5 +1,5 @@ /* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -18,38 +18,20 @@ THE SOFTWARE. */ /** -Testcase Scenarios : + * @addtogroup hipStreamCreateWithPriority hipStreamCreateWithPriority + * @{ + * @ingroup StreamTest + * `hipStreamCreateWithPriority (hipStream_t *stream, unsigned int flags, int priority)` - + * begins graph capture on a stream + */ -1)Create streams with default flag for all available priority levels and -queue tasks in each of these streams, perform device synchronize and validate behavior. - -2)Create streams with non-blocking flag for all available priority levels and -queue tasks in each of these streams, perform stream synchronize and validate behavior. - -3)Create streams with default flag for all available priority levels and -queue tasks in each of these streams, perform stream synchronize and validate behavior. - -4)Create streams with non-blocking flag for all available priority levels and -queue tasks in each of these streams, perform device synchronize and validate behavior. - -5)Create a stream for each priority level with default flag, Launch memcpy and kernel -tasks on these streams from multiple threads. Validate all the results. - -6)Create a stream for each priority level with non-blocking flag, Launch memcpy and -kernel tasks on these streams from multiple threads. Validate all the results. - -7) Validate negative scenarios for hipStreamCreateWithPriority api. - -8) Validate stream priorities with event after classifying them as low, medium, high. -*/ - -#include "streamCommon.hh" #include #include #include +#include "streamCommon.hh" // NOLINT -#define MEMCPYSIZE 64*1024*1024 -#define MEMCPYSIZE2 1024*1024 +#define MEMCPYSIZE1 (64*1024*1024) +#define MEMCPYSIZE2 (1024*1024) #define NUMITERS 2 #define GRIDSIZE 1024 #define BLOCKSIZE 256 @@ -87,7 +69,7 @@ void funcTestsForAllPriorityLevelsWrtNullStrm(unsigned int flags, int priority; int priority_low{}; int priority_high{}; - size_t size = MEMCPYSIZE2*sizeof(int); + size_t size = MEMCPYSIZE2 * sizeof(int); // Test is to get the Stream Priority Range HIP_CHECK(hipDeviceGetStreamPriorityRange(&priority_low, &priority_high)); @@ -194,7 +176,7 @@ void funcTestsForAllPriorityLevelsWrtNullStrm(unsigned int flags, */ void queueTasksInStreams(std::vector *stream, const int arrsize) { - size_t size = MEMCPYSIZE2*sizeof(int); + size_t size = MEMCPYSIZE2 * sizeof(int); // Allocate memory int **A_d = reinterpret_cast(malloc(arrsize*sizeof(int *))); int **C_d = reinterpret_cast(malloc(arrsize*sizeof(int *))); @@ -211,8 +193,8 @@ void queueTasksInStreams(std::vector *stream, HIPASSERT(A_h[idx] != nullptr); C_h[idx] = reinterpret_cast(malloc(size)); HIPASSERT(C_h[idx] != nullptr); - HIPCHECK(hipMalloc(&A_d[idx], size)); - HIPCHECK(hipMalloc(&C_d[idx], size)); + HIP_CHECK(hipMalloc(&A_d[idx], size)); + HIP_CHECK(hipMalloc(&C_d[idx], size)); } // Initialize host memory constexpr int initVal = 2; @@ -223,20 +205,20 @@ void queueTasksInStreams(std::vector *stream, } // Launch task on each stream for (int idx = 0; idx < arrsize; idx++) { - HIPCHECK(hipMemcpyAsync(A_d[idx], A_h[idx], size, + HIP_CHECK(hipMemcpyAsync(A_d[idx], A_h[idx], size, hipMemcpyHostToDevice, (*stream)[idx])); hipLaunchKernelGGL((HipTest::vector_square), dim3(GRIDSIZE), dim3(BLOCKSIZE), 0, (*stream)[idx], A_d[idx], C_d[idx], MEMCPYSIZE2); HIP_CHECK(hipGetLastError()); - HIPCHECK(hipMemcpyAsync(C_h[idx], C_d[idx], size, + HIP_CHECK(hipMemcpyAsync(C_h[idx], C_d[idx], size, hipMemcpyDeviceToHost, (*stream)[idx])); } bool isPassed = true; // Validate the output of each queue for (int idx = 0; idx < arrsize; idx++) { - HIPCHECK(hipStreamSynchronize((*stream)[idx])); + HIP_CHECK(hipStreamSynchronize((*stream)[idx])); for (int idy = 0; idy < MEMCPYSIZE2; idy++) { if (C_h[idx][idy] != A_h[idx][idy] * A_h[idx][idy]) { UNSCOPED_INFO("Data mismatch at idx:" << idx << " idy:" << idy); @@ -248,8 +230,8 @@ void queueTasksInStreams(std::vector *stream, } // Deallocate memory for (int idx = 0; idx < arrsize; idx++) { - HIPCHECK(hipFree(reinterpret_cast(C_d[idx]))); - HIPCHECK(hipFree(reinterpret_cast(A_d[idx]))); + HIP_CHECK(hipFree(reinterpret_cast(C_d[idx]))); + HIP_CHECK(hipFree(reinterpret_cast(A_d[idx]))); free(C_h[idx]); free(A_h[idx]); } @@ -322,7 +304,7 @@ bool runFuncTestsForAllPriorityLevelsMultThread(unsigned int flags) { template bool validateStreamPrioritiesWithEvents() { - size_t size = NUMITERS*MEMCPYSIZE; + size_t size = NUMITERS * MEMCPYSIZE1; // get the range of priorities available #define OP(x) \ @@ -421,13 +403,13 @@ bool validateStreamPrioritiesWithEvents() { #undef OP // launch kernels repeatedly on each of the prioritiy streams - for (int i = 0; i < static_cast(size); i += MEMCPYSIZE) { + for (int i = 0; i < static_cast(size); i += MEMCPYSIZE1) { int j = i / sizeof(T); #define OP(x) \ if (enable_priority_##x) { \ hipLaunchKernelGGL((memcpy_kernel), dim3(GRIDSIZE), \ dim3(BLOCKSIZE), 0, stream_##x, dst_d_##x + j, src_d_##x + j, \ - (MEMCPYSIZE / sizeof(T))); \ + (MEMCPYSIZE1 / sizeof(T))); \ HIP_CHECK(hipGetLastError()); \ } OP(low) @@ -522,19 +504,330 @@ bool validateStreamPrioritiesWithEvents() { return true; } +#define LOW_PRIORITY_STREAMCOUNT 2 +#define HIGH_PRIORITY_STREAMCOUNT 2 +#define NORMAL_PRIORITY_STREAMCOUNT 2 +template +void TestForMultipleStreamWithPriority(void) { + size_t size = NUMITERS * MEMCPYSIZE1; + // get the range of priorities available + #define OP(x) \ + int priority_##x; \ + bool enable_priority_##x = false; + OP(low) + OP(normal) + OP(high) + #undef OP + HIP_CHECK(hipDeviceGetStreamPriorityRange(&priority_low, &priority_high)); + INFO("HIP stream priority range - low: " << priority_low << ",high: " + << priority_high << ",normal: " + << (priority_low + priority_high)/2); + + // Check if priorities are indeed supported + // Enable/disable priorities based on number of available priority levels + enable_priority_low = true; + enable_priority_high = true; + if ((priority_low - priority_high) > 1) { + enable_priority_normal = true; + } + if (enable_priority_normal) { + priority_normal = ((priority_low + priority_high) / 2); + } + // create streams with low priority + hipStream_t stream_low[LOW_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipStreamCreateWithPriority(&stream_low[i], + hipStreamDefault, priority_low)); + } + } + // create streams with normal priority + hipStream_t stream_normal[NORMAL_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipStreamCreateWithPriority(&stream_normal[i], + hipStreamDefault, priority_normal)); + } + } + // create streams with high priority + hipStream_t stream_high[HIGH_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipStreamCreateWithPriority(&stream_high[i], + hipStreamDefault, priority_high)); + } + } + // allocate and initialise host source and destination buffers for + // low streams + T* src_h_low[LOW_PRIORITY_STREAMCOUNT]; + T* dst_h_low[LOW_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + src_h_low[i] = reinterpret_cast(malloc(size)); + REQUIRE(src_h_low[i] != nullptr); + mem_init(src_h_low[i], (size / sizeof(T))); + dst_h_low[i] = reinterpret_cast(malloc(size)); + REQUIRE(dst_h_low[i] != nullptr); + memset(dst_h_low[i], 0, size); + } + } + // allocate and initialise host source and destination buffers for + // normal streams + T* src_h_normal[NORMAL_PRIORITY_STREAMCOUNT]; + T* dst_h_normal[NORMAL_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + src_h_normal[i] = reinterpret_cast(malloc(size)); + REQUIRE(src_h_normal[i] != nullptr); + mem_init(src_h_normal[i], (size / sizeof(T))); + dst_h_normal[i] = reinterpret_cast(malloc(size)); + REQUIRE(dst_h_normal[i] != nullptr); + memset(dst_h_normal[i], 0, size); + } + } + // allocate and initialise host source and destination buffers for + // high streams + T* src_h_high[HIGH_PRIORITY_STREAMCOUNT]; + T* dst_h_high[HIGH_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + src_h_high[i] = reinterpret_cast(malloc(size)); + REQUIRE(src_h_high[i] != nullptr); + mem_init(src_h_high[i], (size / sizeof(T))); + dst_h_high[i] = reinterpret_cast(malloc(size)); + REQUIRE(dst_h_high[i] != nullptr); + memset(dst_h_high[i], 0, size); + } + } + // allocate and initialize device source and destination buffers for + // low streams + T* src_d_low[LOW_PRIORITY_STREAMCOUNT]; + T* dst_d_low[LOW_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipMalloc(&src_d_low[i], size)); + HIP_CHECK(hipMemcpy(src_d_low[i], src_h_low[i], size, + hipMemcpyHostToDevice)); + HIP_CHECK(hipMalloc(&dst_d_low[i], size)); + } + } + // allocate and initialize device source and destination buffers for + // normal streams + T* src_d_normal[NORMAL_PRIORITY_STREAMCOUNT]; + T* dst_d_normal[NORMAL_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipMalloc(&src_d_normal[i], size)); + HIP_CHECK(hipMemcpy(src_d_normal[i], src_h_normal[i], size, + hipMemcpyHostToDevice)); + HIP_CHECK(hipMalloc(&dst_d_normal[i], size)); + } + } + // allocate and initialize device source and destination buffers for + // high streams + T* src_d_high[HIGH_PRIORITY_STREAMCOUNT]; + T* dst_d_high[HIGH_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipMalloc(&src_d_high[i], size)); + HIP_CHECK(hipMemcpy(src_d_high[i], src_h_high[i], size, + hipMemcpyHostToDevice)); + HIP_CHECK(hipMalloc(&dst_d_high[i], size)); + } + } + + hipEvent_t event_start_low[LOW_PRIORITY_STREAMCOUNT]; + hipEvent_t event_end_low[LOW_PRIORITY_STREAMCOUNT]; + // create events for measuring time spent in kernel execution for low + // priority streams + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipEventCreate(&event_start_low[i])); + HIP_CHECK(hipEventCreate(&event_end_low[i])); + } + } + + hipEvent_t event_start_normal[NORMAL_PRIORITY_STREAMCOUNT]; + hipEvent_t event_end_normal[NORMAL_PRIORITY_STREAMCOUNT]; + // create events for measuring time spent in kernel execution for + // normal priority streams + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipEventCreate(&event_start_normal[i])); + HIP_CHECK(hipEventCreate(&event_end_normal[i])); + } + } + + hipEvent_t event_start_high[HIGH_PRIORITY_STREAMCOUNT]; + hipEvent_t event_end_high[HIGH_PRIORITY_STREAMCOUNT]; + // create events for measuring time spent in kernel execution for + // high priority streams + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipEventCreate(&event_start_high[i])); + HIP_CHECK(hipEventCreate(&event_end_high[i])); + } + } + // record start events for each of the low priority streams + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipEventRecord(event_start_low[i], stream_low[i])); + } + } + // record start events for each of the normal priority streams + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipEventRecord(event_start_normal[i], stream_normal[i])); + } + } + // record start events for each of the high priority streams + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipEventRecord(event_start_high[i], stream_high[i])); + } + } + // launch kernels repeatedly on each of the low prioritiy stream + for (int k = 0; k < LOW_PRIORITY_STREAMCOUNT; ++k) { + for (int i = 0; i < size; i += MEMCPYSIZE1) { + int j = i / sizeof(T); + if (enable_priority_low) { + hipLaunchKernelGGL((memcpy_kernel), dim3(GRIDSIZE), dim3(BLOCKSIZE), + 0, stream_low[k], dst_d_low[k] + j, src_d_low[k] + j, + (MEMCPYSIZE1 / sizeof(T))); + } + } + } + // launch kernels repeatedly on each of the normal prioritiy stream + for (int k = 0; k < NORMAL_PRIORITY_STREAMCOUNT; ++k) { + for (int i = 0; i < size; i += MEMCPYSIZE1) { + int j = i / sizeof(T); + if (enable_priority_normal) { + hipLaunchKernelGGL((memcpy_kernel), dim3(GRIDSIZE), dim3(BLOCKSIZE), + 0, stream_normal[k], dst_d_normal[k] + j, src_d_normal[k] + j, + (MEMCPYSIZE1 / sizeof(T))); + } + } + } + // launch kernels repeatedly on each of the high prioritiy stream + for (int k = 0; k < HIGH_PRIORITY_STREAMCOUNT; ++k) { + for (int i = 0; i < size; i += MEMCPYSIZE1) { + int j = i / sizeof(T); + if (enable_priority_high) { + hipLaunchKernelGGL((memcpy_kernel), dim3(GRIDSIZE), dim3(BLOCKSIZE), + 0, stream_high[k], dst_d_high[k] + j, src_d_high[k] + j, + (MEMCPYSIZE1 / sizeof(T))); + } + } + } + // record end events for each of the low priority streams + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipEventRecord(event_end_low[i], stream_low[i])); + } + } + // record end events for each of the normal priority streams + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipEventRecord(event_end_normal[i], stream_normal[i])); + } + } + // record end events for each of the high priority streams + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipEventRecord(event_end_high[i], stream_high[i])); + } + } + // synchronize events for each of the low priority streams + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipEventSynchronize(event_end_low[i])); + } + } + // synchronize events for each of the normal priority streams + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipEventSynchronize(event_end_normal[i])); + } + } + // synchronize events for each of the high priority streams + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipEventSynchronize(event_end_high[i])); + } + } + // compute time spent for memcpy in each low stream + float time_spent_low[LOW_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipEventElapsedTime(&time_spent_low[i], event_start_low[i], + event_end_low[i])); + } + } + // compute time spent for memcpy in each normal stream + float time_spent_normal[NORMAL_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipEventElapsedTime(&time_spent_normal[i], + event_start_normal[i], event_end_normal[i])); + } + } + // compute time spent for memcpy in each high stream + float time_spent_high[HIGH_PRIORITY_STREAMCOUNT]; + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipEventElapsedTime(&time_spent_high[i], event_start_high[i], + event_end_high[i])); + } + } + // sanity check for low priority streams + for (int i = 0; i < LOW_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_low) { + HIP_CHECK(hipMemcpy(dst_h_low[i], dst_d_low[i], size, + hipMemcpyDeviceToHost)); + REQUIRE(memcmp(dst_h_low[i], src_h_low[i], size) == 0); + } + } + // sanity check for normal priority streams + for (int i = 0; i < NORMAL_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_normal) { + HIP_CHECK(hipMemcpy(dst_h_normal[i], dst_d_normal[i], size, + hipMemcpyDeviceToHost)); + REQUIRE(memcmp(dst_h_normal[i], src_h_normal[i], size) == 0); + } + } + // sanity check for high priority streams + for (int i = 0; i < HIGH_PRIORITY_STREAMCOUNT; ++i) { + if (enable_priority_high) { + HIP_CHECK(hipMemcpy(dst_h_high[i], dst_d_high[i], size, + hipMemcpyDeviceToHost)); + REQUIRE(memcmp(dst_h_high[i], src_h_high[i], size) == 0); + } + } +} } // namespace hipStreamCreateWithPriorityTest /** -Tests following scenarios. - 1)Create streams with default flag for all available priority levels and - queue tasks in each of these streams, perform device synchronize and validate behavior. - 2)Create streams with non-blocking flag for all available priority levels and - queue tasks in each of these streams, perform stream synchronize and validate behavior. - 3)Create streams with default flag for all available priority levels and - queue tasks in each of these streams, perform stream synchronize and validate behavior. - 4)Create streams with non-blocking flag for all available priority levels and - queue tasks in each of these streams, perform device synchronize and validate behavior. -*/ + * Test Description + * ------------------------ + * - Create streams with default flag for all available priority levels and + * queue tasks in each of these streams, perform device synchronize and validate + * behavior. + * - Create streams with non-blocking flag for all available priority levels + * and queue tasks in each of these streams, perform stream synchronize and + * validate behavior. + * - Create streams with default flag for all available priority levels and + * queue tasks in each of these streams, perform stream synchronize and validate + * behavior. + * - Create streams with non-blocking flag for all available priority levels + * and queue tasks in each of these streams, perform device synchronize and validate + * behavior. + * Test source + * ------------------------ + * - catch\unit\stream\hipStreamCreateWithPriority.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ TEST_CASE("Unit_hipStreamCreateWithPriority_FunctionalForAllPriorities") { SECTION("Default flag and device synchronize") { hipStreamCreateWithPriorityTest:: @@ -558,8 +851,16 @@ TEST_CASE("Unit_hipStreamCreateWithPriority_FunctionalForAllPriorities") { } /** - * Create a stream for each priority level with default flag, Launch memcpy and kernel - * tasks on these streams from multiple threads. Validate all the results. + * Test Description + * ------------------------ + * - Create a stream for each priority level with default flag, Launch + * memcpy and kernel tasks on these streams from multiple threads. Validate + * all the results. + * ------------------------ + * - catch\unit\stream\hipStreamCreateWithPriority.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ TEST_CASE("Unit_hipStreamCreateWithPriority_MulthreadDefaultflag") { bool TestPassed = true; @@ -569,8 +870,16 @@ TEST_CASE("Unit_hipStreamCreateWithPriority_MulthreadDefaultflag") { } /** - * Create a stream for each priority level with non-blocking flag, Launch memcpy and - * kernel tasks on these streams from multiple threads. Validate all the results. + * Test Description + * ------------------------ + * - Create a stream for each priority level with non-blocking flag, Launch + * memcpy and kernel tasks on these streams from multiple threads. Validate all + * the results. + * ------------------------ + * - catch\unit\stream\hipStreamCreateWithPriority.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ TEST_CASE("Unit_hipStreamCreateWithPriority_MulthreadNonblockingflag") { bool TestPassed = true; @@ -579,13 +888,17 @@ TEST_CASE("Unit_hipStreamCreateWithPriority_MulthreadNonblockingflag") { REQUIRE(TestPassed); } - /** - * Scenario1: Validates functionality of hipStreamCreateWithPriority when - stream = nullptr. - * Scenario2: Validates functionality of hipStreamCreateWithPriority when - flag = 0xffffffff. -*/ + * Test Description + * ------------------------ + * - Validates functionality of hipStreamCreateWithPriority when stream = nullptr + * - Validates functionality of hipStreamCreateWithPriority when flag = 0xffffffff + * ------------------------ + * - catch\unit\stream\hipStreamCreateWithPriority.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ TEST_CASE("Unit_hipStreamCreateWithPriority_NegTst") { hipStream_t stream{nullptr}; int priority_low{0}; @@ -601,15 +914,26 @@ TEST_CASE("Unit_hipStreamCreateWithPriority_NegTst") { SECTION("stream = nullptr test") { REQUIRE(hipErrorInvalidValue == - hipStreamCreateWithPriority(nullptr, hipStreamDefault, priority_low)); + hipStreamCreateWithPriority(nullptr, hipStreamDefault, priority_low)); } SECTION("flag value invalid test") { - REQUIRE(hipErrorInvalidValue == hipStreamCreateWithPriority(&stream, 0xffffffff, priority_low)); + REQUIRE(hipErrorInvalidValue == + hipStreamCreateWithPriority(&stream, 0xffffffff, priority_low)); } } -TEST_CASE("Unit_hipStreamCreateWithPriority") { +/** + * Test Description + * ------------------------ + * - Set and Get Priority Value + * ------------------------ + * - catch\unit\stream\hipStreamCreateWithPriority.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamCreateWithPriority_CheckPriorityVal") { int id = GENERATE(range(0, HipTest::getDeviceCount())); HIP_CHECK(hipSetDevice(id)); @@ -619,45 +943,73 @@ TEST_CASE("Unit_hipStreamCreateWithPriority") { hipStream_t stream{nullptr}; SECTION("Setting high priority") { - HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamDefault, priority_high)); + HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamDefault, + priority_high)); REQUIRE(stream != nullptr); REQUIRE(hip::checkStreamPriorityAndFlags(stream, priority_high)); } SECTION("Setting low priority") { - HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamDefault, priority_low)); + HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamDefault, + priority_low)); REQUIRE(stream != nullptr); REQUIRE(hip::checkStreamPriorityAndFlags(stream, priority_low)); } SECTION("Setting lowest possible priority") { - HIP_CHECK( - hipStreamCreateWithPriority(&stream, hipStreamDefault, std::numeric_limits::max())); + HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamDefault, + std::numeric_limits::max())); REQUIRE(stream != nullptr); REQUIRE(hip::checkStreamPriorityAndFlags(stream, priority_low)); } SECTION("Setting highest possible priority") { - HIP_CHECK( - hipStreamCreateWithPriority(&stream, hipStreamDefault, std::numeric_limits::min())); + HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamDefault, + std::numeric_limits::min())); REQUIRE(stream != nullptr); REQUIRE(hip::checkStreamPriorityAndFlags(stream, priority_high)); } SECTION("Setting flags to hipStreamNonBlocking") { - HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamNonBlocking, priority_high)); + HIP_CHECK(hipStreamCreateWithPriority(&stream, hipStreamNonBlocking, + priority_high)); REQUIRE(stream != nullptr); - REQUIRE(hip::checkStreamPriorityAndFlags(stream, priority_high, hipStreamNonBlocking)); + REQUIRE(hip::checkStreamPriorityAndFlags(stream, priority_high, + hipStreamNonBlocking)); } HIP_CHECK(hipStreamDestroy(stream)); } /** - * Validate stream priorities with event after classifying them as low, medium and high. + * Test Description + * ------------------------ + * - Validate stream priorities with event after classifying them as low, + * medium and high. + * ------------------------ + * - catch\unit\stream\hipStreamCreateWithPriority.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ TEST_CASE("Unit_hipStreamCreateWithPriority_ValidateWithEvents") { bool TestPassed = true; - TestPassed = hipStreamCreateWithPriorityTest::validateStreamPrioritiesWithEvents(); + TestPassed = + hipStreamCreateWithPriorityTest::validateStreamPrioritiesWithEvents(); REQUIRE(TestPassed); } + +/** + * Test Description + * ------------------------ + * - Test that create Multiple streams with priority low, normal and high + * then these streams used to launch the kernel in random seq high,normal,low. + * ------------------------ + * - catch\unit\stream\hipStreamCreateWithPriority.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamCreateWithPriority_TestMultipleStreamWithPriority") { + hipStreamCreateWithPriorityTest::TestForMultipleStreamWithPriority(); +} diff --git a/catch/unit/stream/hipStreamGetDevice.cc b/catch/unit/stream/hipStreamGetDevice.cc index e4878041dc..9f2eef521e 100644 --- a/catch/unit/stream/hipStreamGetDevice.cc +++ b/catch/unit/stream/hipStreamGetDevice.cc @@ -17,25 +17,68 @@ OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include #include +#include +#include +#define NUMBER_OF_THREADS 10 +static bool thread_results[NUMBER_OF_THREADS]; + +/** + * @addtogroup hipStreamGetDevice hipStreamGetDevice + * @{ + * @ingroup StreamTest + * `hipError_t hipStreamGetDevice(hipStream_t stream, hipDevice_t* device)` - + * Get the device assocaited with the stream. + * @} + */ + +/** + * Test Description + * ------------------------ + * - NegativeTest case. + * Pass device as nullptr and verify hipErrorInvalidValue + * Pass stream as nullptr and verify hipErrorInvalidValue + * Pass device as nullptr for hipStreamPerThread and verify hipErrorInvalidValue + + * Test source + * ------------------------ + * - catch/unit/stream/hipStreamGetDevice.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.6 + */ TEST_CASE("Unit_hipStreamGetDevice_Negative") { hipStream_t stream; HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK_ERROR(hipStreamGetDevice(nullptr, nullptr), hipErrorInvalidValue); // null stream + HIP_CHECK_ERROR(hipStreamGetDevice(nullptr, nullptr), hipErrorInvalidValue); HIP_CHECK_ERROR(hipStreamGetDevice(hipStreamPerThread, nullptr), - hipErrorInvalidValue); // stream per thread - HIP_CHECK_ERROR(hipStreamGetDevice(stream, nullptr), hipErrorInvalidValue); // created stream + hipErrorInvalidValue); + HIP_CHECK_ERROR(hipStreamGetDevice(stream, nullptr), hipErrorInvalidValue); HIP_CHECK(hipStreamDestroy(stream)); } -// Iterate over all devices, create stream on the device and match the device we get from stream +/** + * Test Description + * ------------------------ + * - NegativeTest case. + * Test case to validate hipStreamGetDevice on user created stream, + * Null stream and hipStreamPerThread. + + * Test source + * ------------------------ + * - catch/unit/stream/hipStreamGetDevice.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.6 + */ + TEST_CASE("Unit_hipStreamGetDevice_Usecase") { int device_count = 0; - HIP_CHECK(hipGetDeviceCount(&device_count)); - REQUIRE(device_count != 0); // atleast 1 device + REQUIRE(device_count != 0); SECTION("Null Stream") { CTX_CREATE(); @@ -43,7 +86,7 @@ TEST_CASE("Unit_hipStreamGetDevice_Usecase") { hipDevice_t device_from_stream, device_from_ordinal; HIP_CHECK(hipStreamGetDevice(nullptr, &device_from_stream)); - HIP_CHECK(hipDeviceGet(&device_from_ordinal, 0)); // default device + HIP_CHECK(hipDeviceGet(&device_from_ordinal, 0)); REQUIRE(device_from_stream == device_from_ordinal); CTX_DESTROY(); @@ -55,7 +98,7 @@ TEST_CASE("Unit_hipStreamGetDevice_Usecase") { hipDevice_t device_from_stream, device_from_ordinal; HIP_CHECK(hipStreamGetDevice(hipStreamPerThread, &device_from_stream)); - HIP_CHECK(hipDeviceGet(&device_from_ordinal, 0)); // default device + HIP_CHECK(hipDeviceGet(&device_from_ordinal, 0)); REQUIRE(device_from_stream == device_from_ordinal); CTX_DESTROY(); @@ -72,7 +115,128 @@ TEST_CASE("Unit_hipStreamGetDevice_Usecase") { HIP_CHECK(hipStreamGetDevice(stream, &device_from_stream)); HIP_CHECK(hipDeviceGet(&device_from_ordinal, i)); - REQUIRE(device_from_stream == device_from_ordinal); // maybe match uuid?? + REQUIRE(device_from_stream == device_from_ordinal); } } } + +/** + * Test Description + * ------------------------ + * - NegativeTest case. + * Test case to multi-threaded scenario for hipStreamGetDevice + + * Test source + * ------------------------ + * - catch/unit/stream/hipStreamGetDevice.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.6 + */ + +static bool validateStreamGetDevice() { + int gpu = 0; + hipDevice_t device_from_stream; + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamGetDevice(stream, &device_from_stream)); + + REQUIRE(device_from_stream == gpu); + return true; +} + +static void thread_Test(int threadNum) { + thread_results[threadNum] = validateStreamGetDevice(); +} + +static bool test_hipStreamGetDevice_MThread() { + std::vector tests; + + // Spawn the test threads + for (int idx = 0; idx < NUMBER_OF_THREADS; idx++) { + thread_results[idx] = false; + tests.push_back(std::thread(thread_Test, idx)); + } + // Wait for all threads to complete + for (std::thread &t : tests) { + t.join(); + } + // Wait for thread + bool status = true; + for (int idx = 0; idx < NUMBER_OF_THREADS; idx++) { + status = status & thread_results[idx]; + } + return status; +} + +TEST_CASE("Unit_hipStreamGetDevice_MThread") { + REQUIRE(true == test_hipStreamGetDevice_MThread()); +} + +/** + * Test Description + * ------------------------ + * - NegativeTest case. + * Create a stream with gpu1, then set device to gpu0 + * call hipStreamGetDevice on the stream and verify the + * returned device is same as initial set device. + + * Test source + * ------------------------ + * - catch/unit/stream/hipStreamGetDevice.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.6 + */ + +TEST_CASE("Unit_hipStreamGetDevice_SetDiffDevice") { + hipDevice_t device_from_stream; + int device_count = 0; + HIP_CHECK(hipGetDeviceCount(&device_count)); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } + for (int i = 0; i < device_count; ++i) { + HIP_CHECK(hipSetDevice(i)); + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + for (int j = 0; j < device_count; ++j) { + if (i != j) { + HIP_CHECK(hipSetDevice(j)); + HIP_CHECK(hipStreamGetDevice(stream, &device_from_stream)); + REQUIRE(device_from_stream == i); + } + } + } +} + +/** + * Test Description + * ------------------------ + * - NegativeTest case. + * Set hip Devices to each available gpu and probe + * hipStreamGetDevice with null stream. + + * Test source + * ------------------------ + * - catch/unit/stream/hipStreamGetDevice.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.6 + * Test to be run only on AMD machine as it's failing in CUDA. + */ +#if HT_AMD +TEST_CASE("Unit_hipStreamGetDevice_NullStream") { + int device_count = 0; + HIP_CHECK(hipGetDeviceCount(&device_count)); + REQUIRE(device_count != 0); + + for (int i = 0; i < device_count; i++) { + HIP_CHECK(hipSetDevice(i)); + hipDevice_t device_from_stream; + HIP_CHECK(hipStreamGetDevice(0, &device_from_stream)); + REQUIRE(device_from_stream == i); + } +} +#endif diff --git a/catch/unit/surface/CMakeLists.txt b/catch/unit/surface/CMakeLists.txt new file mode 100644 index 0000000000..4bc01e0794 --- /dev/null +++ b/catch/unit/surface/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2023 Advanced Micro Devices, Inc. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# Common Tests - Test independent of all platforms +set(TEST_SRC + hipSurfaceObj1D.cc + hipSurfaceObj2D.cc + hipSurfaceObj3D.cc +) + +hip_add_exe_to_target(NAME SurfaceTest + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests) \ No newline at end of file diff --git a/catch/unit/surface/hipSurfaceObj1D.cc b/catch/unit/surface/hipSurfaceObj1D.cc new file mode 100644 index 0000000000..5068e770b6 --- /dev/null +++ b/catch/unit/surface/hipSurfaceObj1D.cc @@ -0,0 +1,317 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#include +#include +#include + +template +__global__ void +surf1DKernelR(hipSurfaceObject_t surfaceObject, + T* outputData, int width) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + if (x < width) { + surf1Dread(outputData + x, surfaceObject, x * sizeof(T)); + } +#endif +} + +template +__global__ void +surf1DKernelW(hipSurfaceObject_t surfaceObject, + T* inputData, int width) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + if (x < width) { + surf1Dwrite(inputData[x], surfaceObject, x * sizeof(T)); + } +#endif +} + +template +__global__ void +surf1DKernelRW(hipSurfaceObject_t surfaceObject, + hipSurfaceObject_t outputSurfObj, int width) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + if (x < width) { + T data; + surf1Dread(&data, surfaceObject, x * sizeof(T)); + surf1Dwrite(data, outputSurfObj, x * sizeof(T)); + } +#endif +} + +template +static void runTestR(const int width) +{ + unsigned int size = width * sizeof(T); + T *hData = (T*) malloc (size); + memset(hData, 0, size); + for (int j = 0; j < width; j++) + { + initVal(hData[j]); + } + + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + + hipArray *hipArray = nullptr; + HIP_CHECK(hipMallocArray(&hipArray, &channelDesc, width, 0, hipArraySurfaceLoadStore)); + + HIP_CHECK(hipMemcpyToArray(hipArray, 0, 0, hData, size, hipMemcpyHostToDevice)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + T *hOutputData = nullptr; + HIP_CHECK(hipHostMalloc((void**)&hOutputData, size)); + memset(hOutputData, 0, size); + + dim3 dimBlock (16, 1, 1); + dim3 dimGrid ((width + dimBlock.x - 1) / dimBlock.x, 1, 1); + + surf1DKernelR<<>>(surfaceObject, hOutputData, width); + + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + for (int j = 0; j < width; j++) { + if (!isEqual(hData[j], hOutputData[j])) { + printf("Difference [ %d ]:%s ----%s\n", j, + getString(hData[j]).c_str(), getString(hOutputData[j]).c_str()); + REQUIRE(false); + } + } + + HIP_CHECK(hipDestroySurfaceObject(surfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + free(hData); + HIP_CHECK(hipHostFree(hOutputData)); + REQUIRE(true); +} + +template +static void runTestW(const int width) +{ + unsigned int size = width * sizeof(T); + T *hData = nullptr; + HIP_CHECK(hipHostMalloc((void**)&hData, size)); + memset(hData, 0, size); + + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + + hipArray *hipArray = nullptr; + HIP_CHECK(hipMallocArray(&hipArray, &channelDesc, width, 0, hipArraySurfaceLoadStore)); + + HIP_CHECK(hipMemcpyToArray(hipArray, 0, 0, hData, size, hipMemcpyHostToDevice)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + for (int j = 0; j < width; j++) + { + initVal(hData[j]); + } + + dim3 dimBlock (16, 1, 1); + dim3 dimGrid ((width + dimBlock.x - 1) / dimBlock.x, 1, 1); + + surf1DKernelW<<>>(surfaceObject, hData, width); + + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + T *hOutputData = (T*) malloc (size); + memset(hOutputData, 0, size); + HIP_CHECK(hipMemcpyFromArray(hOutputData, hipArray, 0, 0, size, hipMemcpyDeviceToHost)); + + for (int j = 0; j < width; j++) { + if (!isEqual(hData[j], hOutputData[j])) { + printf("Difference [ %d ]:%s ----%s\n", j, + getString(hData[j]).c_str(), getString(hOutputData[j]).c_str()); + REQUIRE(false); + } + } + + HIP_CHECK(hipDestroySurfaceObject(surfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + HIP_CHECK(hipHostFree(hData)); + free(hOutputData); + REQUIRE(true); +} + + +template +static void runTestRW(const int width) +{ + unsigned int size = width * sizeof(T); + T *hData = (T*) malloc (size); + memset(hData, 0, size); + for (int j = 0; j < width; j++) + { + initVal(hData[j]); + } + + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + + hipArray *hipArray = nullptr, *hipOutArray = nullptr; + HIP_CHECK(hipMallocArray(&hipArray, &channelDesc, width, 0, hipArraySurfaceLoadStore)); + + HIP_CHECK(hipMemcpyToArray(hipArray, 0, 0, hData, size, hipMemcpyHostToDevice)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + HIP_CHECK(hipMallocArray(&hipOutArray, &channelDesc, width, 0, hipArraySurfaceLoadStore)); + + hipResourceDesc resOutDesc; + memset(&resOutDesc, 0, sizeof(resOutDesc)); + resOutDesc.resType = hipResourceTypeArray; + resOutDesc.res.array.array = hipOutArray; + + hipSurfaceObject_t outSurfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject (&outSurfaceObject, &resOutDesc)); + + dim3 dimBlock (16, 1, 1); + dim3 dimGrid ((width + dimBlock.x - 1) / dimBlock.x, 1, 1); + + surf1DKernelRW<<>>(surfaceObject, outSurfaceObject, width); + + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + T *hOutputData = (T*) malloc (size); + memset(hOutputData, 0, size); + HIP_CHECK(hipMemcpyFromArray(hOutputData, hipOutArray, 0, 0, size, hipMemcpyDeviceToHost)); + + for (int j = 0; j < width; j++) { + if (!isEqual(hData[j], hOutputData[j])) { + printf("Difference [ %d ]:%s ----%s\n", j, + getString(hData[j]).c_str(), getString(hOutputData[j]).c_str()); + REQUIRE(false); + } + } + + HIP_CHECK(hipDestroySurfaceObject(surfaceObject)); + HIP_CHECK(hipDestroySurfaceObject(outSurfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + HIP_CHECK(hipFreeArray(hipOutArray)); + free(hData); + free(hOutputData); + REQUIRE(true); +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj1D_type_R", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj1D_type_R - 31") { + runTestR(31); + } + + SECTION("Unit_hipSurfaceObj1D_type_R - 67") { + runTestR(67); + } + + SECTION("Unit_hipSurfaceObj1D_type_R - 131") { + runTestR(131); + } + + SECTION("Unit_hipSurfaceObj1D_type_R - 263") { + runTestR(263); + } +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj1D_type_W", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj1D_type_W - 31") { + runTestW(31); + } + + SECTION("Unit_hipSurfaceObj1D_type_W - 63") { + runTestW(63); + } + + SECTION("Unit_hipSurfaceObj1D_type_W - 131") { + runTestW(131); + } + + SECTION("Unit_hipSurfaceObj1D_type_W - 263") { + runTestW(263); + } +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj1D_type_RW", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj1D_type_RW - 23") { + runTestRW(23); + } + + SECTION("Unit_hipSurfaceObj1D_type_RW - 67") { + runTestRW(67); + } + + SECTION("Unit_hipSurfaceObj1D_type_RW - 131") { + runTestRW(131); + } + + SECTION("Unit_hipSurfaceObj1D_type_RW - 263") { + runTestRW(263); + } +} diff --git a/catch/unit/surface/hipSurfaceObj2D.cc b/catch/unit/surface/hipSurfaceObj2D.cc new file mode 100644 index 0000000000..22f143e7df --- /dev/null +++ b/catch/unit/surface/hipSurfaceObj2D.cc @@ -0,0 +1,364 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#include +#include +#include + +#define LOG_DATA 0 + +template +__global__ void +surf2DKernelR(hipSurfaceObject_t surfaceObject, + T* outputData, int width, int height) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + if (x < width && y < height) { + surf2Dread(outputData + y * width + x, surfaceObject, x * sizeof(T), y); + } +#endif +} + +template +__global__ void +surf2DKernelW(hipSurfaceObject_t surfaceObject, + T* inputData, int width, int height) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + if (x < width && y < height) { + surf2Dwrite(inputData[y * width + x], surfaceObject, x * sizeof(T), y); + } +#endif +} + +template +__global__ void +surf2DKernelRW(hipSurfaceObject_t surfaceObject, + hipSurfaceObject_t outputSurfObj, int width, int height) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + if (x < width && y < height) { + T data; + surf2Dread(&data, surfaceObject, x * sizeof(T), y); + surf2Dwrite(data, outputSurfObj, x * sizeof(T), y); + } +#endif +} + +template +static void runTestR(const int width, const int height) +{ + unsigned int size = width * height * sizeof(T); + T* hData = (T*) malloc(size); + memset(hData, 0, size); + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + initVal(hData[i * width + j]); + } + } + + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + hipArray *hipArray = nullptr; + HIP_CHECK(hipMallocArray (&hipArray, &channelDesc, width, height, + hipArraySurfaceLoadStore)); + + // Need set source pitch, but we don't have any padding here + const size_t spitch = width * sizeof(T); + HIP_CHECK(hipMemcpy2DToArray(hipArray, 0, 0, hData, spitch, spitch, height, + hipMemcpyHostToDevice)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + T* hOutputData = nullptr; + HIP_CHECK(hipHostMalloc((void**)&hOutputData, size)); + memset(hOutputData, 0, size); + + dim3 dimBlock (16, 16, 1); + dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y -1)/ dimBlock.y, 1); + surf2DKernelR<<>>(surfaceObject, hOutputData, width, height); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int index = i * width + j; + if (!isEqual(hData[index], hOutputData[index])) { + printf("Difference [ %d %d ]:%s ----%s\n", i, j, + getString(hData[index]).c_str(), getString(hOutputData[index]).c_str()); + REQUIRE(false); + } + } + } + + HIP_CHECK(hipDestroySurfaceObject(surfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + free(hData); + HIP_CHECK(hipHostFree(hOutputData)); + REQUIRE(true); +} + +template +static void runTestW(const int width, const int height) +{ + unsigned int size = width * height * sizeof(T); + T* hData = nullptr; + HIP_CHECK(hipHostMalloc((void**)&hData, size)); + memset(hData, 0, size); + + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + hipArray *hipArray = nullptr; + HIP_CHECK(hipMallocArray (&hipArray, &channelDesc, width, height, + hipArraySurfaceLoadStore)); + + // Need set source pitch, but we don't have any padding here + const size_t spitch = width * sizeof(T); + HIP_CHECK(hipMemcpy2DToArray(hipArray, 0, 0, hData, spitch, spitch, height, + hipMemcpyHostToDevice)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + initVal(hData[i * width + j]); + } + } + + dim3 dimBlock (16, 16, 1); + dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y -1)/ dimBlock.y, 1); + surf2DKernelW<<>>(surfaceObject, hData, width, height); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + T* hOutputData = (T*) malloc(size); + + memset(hOutputData, 0, size); + HIP_CHECK(hipMemcpy2DFromArray(hOutputData, spitch, hipArray, 0, 0, spitch, + height, hipMemcpyDeviceToHost)); + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int index = i * width + j; + if (!isEqual(hData[index], hOutputData[index])) { + printf("Difference [ %d %d ]:%s ----%s\n", i, j, + getString(hData[index]).c_str(), getString(hOutputData[index]).c_str()); + REQUIRE(false); + } + } + } + + HIP_CHECK(hipDestroySurfaceObject(surfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + HIP_CHECK(hipHostFree(hData)); + free(hOutputData); + REQUIRE(true); +} + +template +static void runTestRW(const int width, const int height) +{ + unsigned int size = width * height * sizeof(T); + T* hData = (T*) malloc(size); + memset(hData, 0, size); + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + initVal(hData[i * width + j]); + } + } +#if LOG_DATA + printf ("hData: "); + for (int i = 0; i < 32; i++) + { + printf ("%s ", getString(hData[i]).c_str()); + } + printf ("\n"); +#endif + + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + hipArray *hipArray = nullptr, *hipOutArray = nullptr; + HIP_CHECK(hipMallocArray (&hipArray, &channelDesc, width, height, + hipArraySurfaceLoadStore)); + + // Need set source pitch, but we don't have any padding here + const size_t spitch = width * sizeof(T); + HIP_CHECK(hipMemcpy2DToArray(hipArray, 0, 0, hData, spitch, spitch, height, + hipMemcpyHostToDevice)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + HIP_CHECK(hipMallocArray(&hipOutArray, &channelDesc, width, height, + hipArraySurfaceLoadStore)); + + hipResourceDesc resOutDesc; + memset(&resOutDesc, 0, sizeof(resOutDesc)); + resOutDesc.resType = hipResourceTypeArray; + resOutDesc.res.array.array = hipOutArray; + + hipSurfaceObject_t outSurfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject (&outSurfaceObject, &resOutDesc)); + + dim3 dimBlock (16, 16, 1); + dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y -1)/ dimBlock.y, 1); + surf2DKernelRW<<>>(surfaceObject, outSurfaceObject, width, height); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + T* hOutputData = (T*) malloc(size); + + memset(hOutputData, 0, size); + HIP_CHECK(hipMemcpy2DFromArray(hOutputData, spitch, hipOutArray, 0, 0, spitch, + height, hipMemcpyDeviceToHost)); + +#if LOG_DATA + printf ("dData: "); + for (int i = 0; i < 32; i++) + { + printf ("%s ", getString(hOutputData[i]).c_str()); + } + printf ("\n"); +#endif + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int index = i * width + j; + if (!isEqual(hData[index], hOutputData[index])) { + printf("Difference [ %d %d ]:%s ----%s\n", i, j, + getString(hData[index]).c_str(), getString(hOutputData[index]).c_str()); + REQUIRE(false); + } + } + } + + HIP_CHECK(hipDestroySurfaceObject(surfaceObject)); + HIP_CHECK(hipDestroySurfaceObject(outSurfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + HIP_CHECK(hipFreeArray(hipOutArray)); + free(hData); + free(hOutputData); + REQUIRE(true); +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj2D_type_R", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj2D_type_R - 23, 67") { + runTestR(23, 67); + } + + SECTION("Unit_hipSurfaceObj2D_type_R - 67, 23") { + runTestR(67, 23); + } + + SECTION("Unit_hipSurfaceObj2D_type_R - 131, 67") { + runTestR(131, 67); + } + + SECTION("Unit_hipSurfaceObj2D_type_R - 263, 131") { + runTestR(263, 131); + } +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj2D_type_W", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj2D_type_W - 23, 67") { + runTestW(23, 67); + } + + SECTION("Unit_hipSurfaceObj2D_type_W - 67, 23") { + runTestW(67, 23); + } + + SECTION("Unit_hipSurfaceObj2D_type_W - 131, 67") { + runTestW(131, 67); + } + + SECTION("Unit_hipSurfaceObj2D_type_W - 263, 23") { + runTestW(263, 23); + } +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj2D_type_RW", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj2D_type_RW - 23, 67") { + runTestRW(23, 67); + } + + SECTION("Unit_hipSurfaceObj2D_type_RW - 67, 131") { + runTestRW(67, 131); + } + + SECTION("Unit_hipSurfaceObj2D_type_RW - 131, 263") { + runTestRW(131, 263); + } + + SECTION("Unit_hipSurfaceObj2D_type_RW - 263, 67") { + runTestRW(263, 67); + } +} diff --git a/catch/unit/surface/hipSurfaceObj3D.cc b/catch/unit/surface/hipSurfaceObj3D.cc new file mode 100644 index 0000000000..804cc5fb81 --- /dev/null +++ b/catch/unit/surface/hipSurfaceObj3D.cc @@ -0,0 +1,400 @@ +/* +Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#include +#include +#include + +template +__global__ void +surf3DKernelR(hipSurfaceObject_t surfaceObject, + T* outputData, int width, int height, int depth) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + int z = blockIdx.z * blockDim.z + threadIdx.z; + if (x < width && y < height && z < depth) { + surf3Dread(outputData + z * width * height + y * width + x, + surfaceObject, x * sizeof(T), y, z); + } +#endif +} + +template +__global__ void +surf3DKernelW(hipSurfaceObject_t surfaceObject, + T* inputData, int width, int height, int depth) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + int z = blockIdx.z * blockDim.z + threadIdx.z; + if (x < width && y < height && z < depth) { + surf3Dwrite(inputData[z * width * height + y * width + x], + surfaceObject, x * sizeof(T), y, z); + } +#endif +} + +template +__global__ void +surf3DKernelRW(hipSurfaceObject_t surfaceObject, + hipSurfaceObject_t outputSurfObj, int width, int height, int depth) +{ +#if !defined(__HIP_NO_IMAGE_SUPPORT) || !__HIP_NO_IMAGE_SUPPORT + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + int z = blockIdx.z * blockDim.z + threadIdx.z; + if (x < width && y < height && z < depth) { + T data; + surf3Dread(&data, surfaceObject, x * sizeof(T), y, z); + surf3Dwrite(data, outputSurfObj, x * sizeof(T), y, z); + } +#endif +} + +template +static void runTestR(const int width, const int height, const int depth) +{ + unsigned int size = width * height * depth * sizeof(T); + T *hData = (T*) malloc(size); + memset(hData, 0, size); + for (int i = 0; i < depth; i++) { + for (int j = 0; j < height; j++) { + for (int k = 0; k < width; k++) { + initVal(hData[i * width * height + j * width + k]); + } + } + } + + // Allocate array and copy image data + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + hipArray *hipArray = nullptr; + HIP_CHECK(hipMalloc3DArray(&hipArray, &channelDesc, make_hipExtent(width, height, depth), + hipArraySurfaceLoadStore)); + + hipMemcpy3DParms myparms; + memset(&myparms, 0, sizeof(myparms)); + myparms.srcPos = make_hipPos(0,0,0); + myparms.dstPos = make_hipPos(0,0,0); + myparms.srcPtr = make_hipPitchedPtr(hData, width * sizeof(T), width, height); + myparms.dstArray = hipArray; + myparms.extent = make_hipExtent(width, height, depth); + myparms.kind = hipMemcpyHostToDevice; + + HIP_CHECK(hipMemcpy3D(&myparms)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + T *hOutputData = nullptr; + HIP_CHECK(hipHostMalloc((void**)&hOutputData, size)); + memset(hOutputData, 0, size); + + dim3 dimBlock(8, 8, 8); // 512 threads + dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y -1)/ dimBlock.y, + (depth + dimBlock.z - 1) / dimBlock.z); + + surf3DKernelR<<>>(surfaceObject, hOutputData, width, height, depth); + + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + for (int i = 0; i < depth; i++) { + for (int j = 0; j < height; j++) { + for (int k = 0; k < width; k++) { + int index = i * width * height + j * width + k; + if (!isEqual(hData[index], hOutputData[index])) { + printf("Difference [ %d %d %d]:%s ----%s\n", i, j, k, + getString(hData[index]).c_str(), getString(hOutputData[index]).c_str()); + REQUIRE(false); + } + } + } + } + + HIP_CHECK(hipDestroySurfaceObject (surfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + free(hData); + HIP_CHECK(hipHostFree(hOutputData)); + REQUIRE(true); +} + +template +static void runTestW(const int width, const int height, const int depth) +{ + unsigned int size = width * height * depth * sizeof(T); + T *hData = nullptr; + HIP_CHECK(hipHostMalloc((void**)&hData, size)); + memset(hData, 0, size); + + // Allocate array and copy image data + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + hipArray *hipArray = nullptr; + HIP_CHECK(hipMalloc3DArray(&hipArray, &channelDesc, make_hipExtent(width, height, depth), + hipArraySurfaceLoadStore)); + + hipMemcpy3DParms myparms; + memset(&myparms, 0, sizeof(myparms)); + myparms.srcPos = make_hipPos(0,0,0); + myparms.dstPos = make_hipPos(0,0,0); + myparms.srcPtr = make_hipPitchedPtr(hData, width * sizeof(T), width, height); + myparms.dstArray = hipArray; + myparms.extent = make_hipExtent(width, height, depth); + myparms.kind = hipMemcpyHostToDevice; + + HIP_CHECK(hipMemcpy3D(&myparms)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + for (int i = 0; i < depth; i++) { + for (int j = 0; j < height; j++) { + for (int k = 0; k < width; k++) { + initVal(hData[i * width * height + j * width + k]); + } + } + } + + dim3 dimBlock(8, 8, 8); // 512 threads + dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y -1)/ dimBlock.y, + (depth + dimBlock.z - 1) / dimBlock.z); + + surf3DKernelW<<>>(surfaceObject, hData, width, height, depth); + + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + T *hOutputData = (T*) malloc (size); + memset(hOutputData, 0, size); + + memset(&myparms, 0, sizeof(myparms)); + myparms.srcPos = make_hipPos(0,0,0); + myparms.dstPos = make_hipPos(0,0,0); + myparms.srcArray= hipArray; + myparms.dstPtr = make_hipPitchedPtr(hOutputData, width * sizeof(T), width, height); + myparms.extent = make_hipExtent(width, height, depth); + myparms.kind = hipMemcpyDeviceToHost; + + HIP_CHECK(hipMemcpy3D(&myparms)); + + for (int i = 0; i < depth; i++) { + for (int j = 0; j < height; j++) { + for (int k = 0; k < width; k++) { + int index = i * width * height + j * width + k; + if (!isEqual(hData[index], hOutputData[index])) { + printf("Difference [ %d %d %d]:%s ----%s\n", i, j, k, + getString(hData[index]).c_str(), getString(hOutputData[index]).c_str()); + REQUIRE(false); + } + } + } + } + + HIP_CHECK(hipDestroySurfaceObject (surfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + HIP_CHECK(hipHostFree(hData)); + free(hOutputData); + REQUIRE(true); +} + +template +static void runTestRW(const int width, const int height, const int depth) +{ + unsigned int size = width * height * depth * sizeof(T); + T *hData = (T*) malloc(size); + memset(hData, 0, size); + for (int i = 0; i < depth; i++) { + for (int j = 0; j < height; j++) { + for (int k = 0; k < width; k++) { + initVal(hData[i * width * height + j * width + k]); + } + } + } + + // Allocate array and copy image data + hipChannelFormatDesc channelDesc = hipCreateChannelDesc(); + hipArray *hipArray = nullptr, *hipOutArray = nullptr; + HIP_CHECK(hipMalloc3DArray(&hipArray, &channelDesc, make_hipExtent(width, height, depth), + hipArraySurfaceLoadStore)); + + hipMemcpy3DParms myparms; + memset(&myparms, 0, sizeof(myparms)); + myparms.srcPos = make_hipPos(0,0,0); + myparms.dstPos = make_hipPos(0,0,0); + myparms.srcPtr = make_hipPitchedPtr(hData, width * sizeof(T), width, height); + myparms.dstArray = hipArray; + myparms.extent = make_hipExtent(width, height, depth); + myparms.kind = hipMemcpyHostToDevice; + + HIP_CHECK(hipMemcpy3D(&myparms)); + + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.array = hipArray; + + // Create surface object + hipSurfaceObject_t surfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&surfaceObject, &resDesc)); + + HIP_CHECK(hipMalloc3DArray(&hipOutArray, &channelDesc, make_hipExtent(width, height, depth), + hipArraySurfaceLoadStore)); + + hipResourceDesc resOutDesc; + memset(&resOutDesc, 0, sizeof(resOutDesc)); + resOutDesc.resType = hipResourceTypeArray; + resOutDesc.res.array.array = hipOutArray; + + hipSurfaceObject_t outSurfaceObject = 0; + HIP_CHECK(hipCreateSurfaceObject(&outSurfaceObject, &resOutDesc)); + + dim3 dimBlock(8, 8, 8); // 512 threads + dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y -1)/ dimBlock.y, + (depth + dimBlock.z - 1) / dimBlock.z); + + surf3DKernelRW<<>>(surfaceObject, outSurfaceObject, width, height, depth); + + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipDeviceSynchronize()); + + T *hOutputData = (T*) malloc (size); + memset(hOutputData, 0, size); + + memset(&myparms, 0, sizeof(myparms)); + myparms.srcPos = make_hipPos(0,0,0); + myparms.dstPos = make_hipPos(0,0,0); + myparms.srcArray= hipOutArray; + myparms.dstPtr = make_hipPitchedPtr(hOutputData, width * sizeof(T), width, height); + myparms.extent = make_hipExtent(width, height, depth); + myparms.kind = hipMemcpyDeviceToHost; + + HIP_CHECK(hipMemcpy3D(&myparms)); + + for (int i = 0; i < depth; i++) { + for (int j = 0; j < height; j++) { + for (int k = 0; k < width; k++) { + int index = i * width * height + j * width + k; + if (!isEqual(hData[index], hOutputData[index])) { + printf("Difference [ %d %d %d]:%s ----%s\n", i, j, k, + getString(hData[index]).c_str(), getString(hOutputData[index]).c_str()); + REQUIRE(false); + } + } + } + } + + HIP_CHECK(hipDestroySurfaceObject (surfaceObject)); + HIP_CHECK(hipDestroySurfaceObject (outSurfaceObject)); + HIP_CHECK(hipFreeArray(hipArray)); + HIP_CHECK(hipFreeArray(hipOutArray)); + free(hData); + free(hOutputData); + REQUIRE(true); +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj3D_type_R", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj3D_type_R - 31, 67, 131") { + runTestR(31, 67, 131); + } + + SECTION("Unit_hipSurfaceObj3D_type_R - 67, 31, 263") { + runTestR(67, 31, 263); + } + + SECTION("Unit_hipSurfaceObj3D_type_R - 131, 131, 67") { + runTestR(131, 131, 67); + } + + SECTION("Unit_hipSurfaceObj3D_type_R - 263, 131, 263") { + runTestR(263, 131, 263); + } +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj3D_type_W", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj3D_type_W - 31, 67, 131") { + runTestW(31, 67, 131); + } + + SECTION("Unit_hipSurfaceObj3D_type_W - 67, 67, 31") { + runTestW(67, 67, 31); + } + + SECTION("Unit_hipSurfaceObj3D_type_W - 131, 131, 67") { + runTestW(131, 131, 67); + } + + SECTION("Unit_hipSurfaceObj3D_type_W - 263, 131, 263") { + runTestW(263, 131, 263); + } +} + +TEMPLATE_TEST_CASE("Unit_hipSurfaceObj3D_type_RW", "", + char, uchar, short, ushort, int, uint, float, + char1, uchar1, short1, ushort1, int1, uint1, float1, + char2, uchar2, short2, ushort2, int2, uint2, float2, + char4, uchar4, short4, ushort4, int4, uint4, float4) +{ + CHECK_IMAGE_SUPPORT + auto err = hipGetLastError(); // reset last err due to previous negative tests + + SECTION("Unit_hipSurfaceObj3D_type_RW - 31, 31, 67") { + runTestRW(31, 31, 67); + } + + SECTION("Unit_hipSurfaceObj3D_type_RW - 67, 67, 31") { + runTestRW(67, 67, 31); + } + + SECTION("Unit_hipSurfaceObj3D_type_RW - 131, 67, 263") { + runTestRW(131, 67, 263); + } + + SECTION("Unit_hipSurfaceObj3D_type_RW - 263, 131, 263") { + runTestRW(263, 131, 263); + } +} diff --git a/catch/unit/texture/hipTextureObjFetchVector.cc b/catch/unit/texture/hipTextureObjFetchVector.cc index b965ac74e8..d31e5fbd7f 100644 --- a/catch/unit/texture/hipTextureObjFetchVector.cc +++ b/catch/unit/texture/hipTextureObjFetchVector.cc @@ -18,6 +18,7 @@ THE SOFTWARE. */ #include +#include #include #include @@ -31,47 +32,6 @@ __global__ void tex1dKernelFetch(T *val, hipTextureObject_t obj, int N) { #endif } -template -static inline __host__ __device__ constexpr int rank() { - return sizeof(T) / sizeof(decltype(T::x)); -} - -template -static inline T getRandom() { - double r = 0; - if (std::is_signed < T > ::value) { - r = (std::rand() - RAND_MAX / 2.0) / (RAND_MAX / 2.0 + 1.); - } else { - r = std::rand() / (RAND_MAX + 1.); - } - return static_cast(std::numeric_limits < T > ::max() * r); -} - -template< - typename T, - typename std::enable_if() == 1>::type* = nullptr> -static inline void initVal(T &val) { - val.x = getRandom(); -} - -template< - typename T, - typename std::enable_if() == 2>::type* = nullptr> -static inline void initVal(T &val) { - val.x = getRandom(); - val.y = getRandom(); -} - -template< - typename T, - typename std::enable_if() == 4>::type* = nullptr> -static inline void initVal(T &val) { - val.x = getRandom(); - val.y = getRandom(); - val.z = getRandom(); - val.w = getRandom(); -} - template< typename T, typename std::enable_if() == 1>::type* = nullptr> @@ -112,31 +72,6 @@ static inline void printVector(T &val) { std::cout << ")"; } -template< - typename T, - typename std::enable_if() == 1>::type* = nullptr> -static inline bool isEqual(const T &val0, const T &val1) { - return val0.x == val1.x; -} - -template< - typename T, - typename std::enable_if() == 2>::type* = nullptr> -static inline bool isEqual(const T &val0, const T &val1) { - return val0.x == val1.x && - val0.y == val1.y; -} - -template< - typename T, - typename std::enable_if() == 4>::type* = nullptr> -static inline bool isEqual(const T &val0, const T &val1) { - return val0.x == val1.x && - val0.y == val1.y && - val0.z == val1.z && - val0.w == val1.w; -} - template bool runTest() { const int N = 1024; @@ -144,7 +79,7 @@ bool runTest() { // Allocating the required buffer on gpu device T *texBuf, *texBufOut; T val[N], output[N]; - + hipGetLastError(); // Clear err due to negative tests memset(output, 0, sizeof(output)); std::srand(std::time(nullptr)); // use current time as seed for random generator diff --git a/samples/1_Utils/hipDispatchLatency/Makefile b/samples/1_Utils/hipDispatchLatency/Makefile index fbe37d2932..988827071c 100644 --- a/samples/1_Utils/hipDispatchLatency/Makefile +++ b/samples/1_Utils/hipDispatchLatency/Makefile @@ -35,7 +35,7 @@ hipDispatchLatency.out: hipDispatchLatency.cpp $(HIPCC) $(CXXFLAGS) hipDispatchLatency.cpp -o $@ hipDispatchEnqueueRateMT.out: hipDispatchEnqueueRateMT.cpp - $(HIPCC) $(CXXFLAGS) hipDispatchEnqueueRateMT.cpp -o $@ + $(HIPCC) $(CXXFLAGS) hipDispatchEnqueueRateMT.cpp -lpthread -o $@ test_kernel.code: test_kernel.cpp $(HIP_PATH)/bin/hipcc --genco $(GENCO_FLAGS) $^ -o $@ diff --git a/samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt b/samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt index 3c7c306866..8404ac58b2 100644 --- a/samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt +++ b/samples/2_Cookbook/15_static_library/host_functions/CMakeLists.txt @@ -37,7 +37,7 @@ endif() add_library(HipOptLibrary STATIC ${CPP_SOURCES}) # Set-up the correct flags to generate the static library. -target_link_libraries(HipOptLibrary PRIVATE --emit-static-lib) +target_link_options(HipOptLibrary PRIVATE --emit-static-lib) target_include_directories(HipOptLibrary PRIVATE /opt/rocm/hsa/include) # Create test executable that uses libHipOptLibrary.a diff --git a/samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp b/samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp index 68f54184b4..2fe948a0f3 100644 --- a/samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp +++ b/samples/2_Cookbook/15_static_library/host_functions/hipOptLibrary.cpp @@ -23,40 +23,47 @@ #include #include #include +#include -#define HIP_ASSERT(status) assert(status == hipSuccess) +#define HIP_ASSERT(status) \ + { \ + if ((status != hipSuccess)) { \ + std::cerr << "Failed in: " << __LINE__ << " on hip call: " #status << std::endl; \ + throw std::runtime_error("generic failure"); \ + } \ + } #define LEN 512 __global__ void copy(uint32_t* A, uint32_t* B) { - size_t tid = threadIdx.x + blockIdx.x * blockDim.x; - B[tid] = A[tid]; + size_t tid = threadIdx.x + blockIdx.x * blockDim.x; + B[tid] = A[tid]; } void run_test1() { - uint32_t *A_h, *B_h, *A_d, *B_d; - size_t valbytes = LEN * sizeof(uint32_t); + uint32_t *A_h, *B_h, *A_d, *B_d; + size_t valbytes = LEN * sizeof(uint32_t); - A_h = (uint32_t*)malloc(valbytes); - B_h = (uint32_t*)malloc(valbytes); - for (uint32_t i = 0; i < LEN; i++) { - A_h[i] = i; - B_h[i] = 0; - } + A_h = (uint32_t*)malloc(valbytes); + B_h = (uint32_t*)malloc(valbytes); + for (uint32_t i = 0; i < LEN; i++) { + A_h[i] = i; + B_h[i] = 0; + } - HIP_ASSERT(hipMalloc((void**)&A_d, valbytes)); - HIP_ASSERT(hipMalloc((void**)&B_d, valbytes)); + HIP_ASSERT(hipMalloc((void**)&A_d, valbytes)); + HIP_ASSERT(hipMalloc((void**)&B_d, valbytes)); - HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice)); - hipLaunchKernelGGL(copy, dim3(LEN/64), dim3(64), 0, 0, A_d, B_d); - HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost)); + HIP_ASSERT(hipMemcpy(A_d, A_h, valbytes, hipMemcpyHostToDevice)); + hipLaunchKernelGGL(copy, dim3(LEN / 64), dim3(64), 0, 0, A_d, B_d); + HIP_ASSERT(hipMemcpy(B_h, B_d, valbytes, hipMemcpyDeviceToHost)); - for (uint32_t i = 0; i < LEN; i++) { - assert(A_h[i] == B_h[i]); - } + for (uint32_t i = 0; i < LEN; i++) { + assert(A_h[i] == B_h[i]); + } - HIP_ASSERT(hipFree(A_d)); - HIP_ASSERT(hipFree(B_d)); - free(A_h); - free(B_h); - std::cout << "Test Passed!\n"; + HIP_ASSERT(hipFree(A_d)); + HIP_ASSERT(hipFree(B_d)); + free(A_h); + free(B_h); + std::cout << "Test Passed!\n"; }