/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ /** Testcase Scenarios : (TestCase 1):: 1) Execute atomicAdd in multi threaded scenario by diverging the data across multiple threads and validate the output at the end of all operations. 2) Execute atomicAddNoRet in multi threaded scenario by diverging the data across multiple threads and validate the output at the end of all operations. (TestCase 2):: 3) Execute atomicAdd API and validate the result. 4) Execute atomicAddNoRet API and validate the result. (TestCase 3):: 5) atomicadd/NoRet negative scenarios (TBD). */ #include #include #include /* * Defines initial and increment values */ #define INCREMENT_VALUE 10 #define INT_INITIAL_VALUE 10 #define FLOAT_INITIAL_VALUE 10.50 #define DOUBLE_INITIAL_VALUE 200.12 #define LONG_INITIAL_VALUE 10000 #define UNSIGNED_INITIAL_VALUE 20 #if HT_NVIDIA // atomicAddNoRet is unavailable in cuda template __device__ void atomicAddNoRet(T* x, int y) { atomicAdd(x, static_cast(y)); } #endif bool p_atomicNoRet = false; template __global__ void atomicnoret_manywaves(T* C_d) { atomicAddNoRet(C_d, INCREMENT_VALUE); } template __global__ void atomic_manywaves(T* C_d) { atomicAdd(C_d, INCREMENT_VALUE); } template __global__ void atomicnoret_simple(T* C_d) { atomicAddNoRet(C_d, INCREMENT_VALUE); } template __global__ void atomic_simple(T* C_d) { atomicAdd(C_d, INCREMENT_VALUE); } template bool atomictest_manywaves(const T& initial_val) { unsigned int ThreadsperBlock = 10; unsigned int numBlocks = 1; T memSize = sizeof(T); T* hOData = reinterpret_cast(malloc(memSize)); *hOData = initial_val; T* dOData; HIP_CHECK(hipMalloc(&dOData, memSize)); // copy host memory to device to initialize to zero HIP_CHECK(hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice)); // execute the kernel hipLaunchKernelGGL(atomic_manywaves, dim3(numBlocks), dim3(ThreadsperBlock), 0, 0, dOData); // Copy result from device to host HIP_CHECK(hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost)); REQUIRE(hOData[0] == initial_val + static_cast(INCREMENT_VALUE * (ThreadsperBlock * numBlocks))); // Cleanup memory free(hOData); HIP_CHECK(hipFree(dOData)); return true; } template bool atomictestnoret_manywaves(const T& initial_val) { unsigned int ThreadsperBlock = 10; unsigned int numBlocks = 1; T memSize = sizeof(T); T* hOData = reinterpret_cast(malloc(memSize)); *hOData = initial_val; T* dOData; HIP_CHECK(hipMalloc(&dOData, memSize)); // copy host memory to device to initialize to zero HIP_CHECK(hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice)); // execute the kernel hipLaunchKernelGGL(atomicnoret_manywaves, dim3(numBlocks), dim3(ThreadsperBlock), 0, 0, dOData); // Copy result from device to host HIP_CHECK(hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost)); REQUIRE(hOData[0] == initial_val + (INCREMENT_VALUE * (ThreadsperBlock * numBlocks))); // Cleanup memory free(hOData); HIP_CHECK(hipFree(dOData)); return true; } template bool atomictest_simple(const T& initial_val) { unsigned int ThreadsperBlock = 1; unsigned int numBlocks = 1; T memSize = sizeof(T); T* hOData = reinterpret_cast(malloc(memSize)); *hOData = initial_val; T* dOData; HIP_CHECK(hipMalloc(&dOData, memSize)); // copy host memory to device to initialize to zero HIP_CHECK(hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice)); // execute the kernel hipLaunchKernelGGL(atomic_simple, dim3(numBlocks), dim3(ThreadsperBlock), 0, 0, dOData); // Copy result from device to host HIP_CHECK(hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost)); REQUIRE(hOData[0] == initial_val + INCREMENT_VALUE); // Cleanup memory free(hOData); HIP_CHECK(hipFree(dOData)); return true; } template bool atomictestnoret_simple(const T& initial_val) { unsigned int ThreadsperBlock = 1; unsigned int numBlocks = 1; T memSize = sizeof(T); T* hOData = reinterpret_cast(malloc(memSize)); *hOData = initial_val; T* dOData; HIP_CHECK(hipMalloc(&dOData, memSize)); // copy host memory to device to initialize to zero HIP_CHECK(hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice)); // execute the kernel hipLaunchKernelGGL(atomicnoret_simple, dim3(numBlocks), dim3(ThreadsperBlock), 0, 0, dOData); // Copy result from device to host HIP_CHECK(hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost)); REQUIRE(hOData[0] == initial_val + INCREMENT_VALUE); // Cleanup memory free(hOData); HIP_CHECK(hipFree(dOData)); return true; } HIP_TEST_CASE(Unit_hipTestAtomicAdd) { bool TestPassed = true; SECTION("atomic tests with many waves") { REQUIRE(TestPassed == atomictest_manywaves(INT_INITIAL_VALUE)); REQUIRE(TestPassed == atomictest_manywaves(UNSIGNED_INITIAL_VALUE)); REQUIRE(TestPassed == atomictest_manywaves(FLOAT_INITIAL_VALUE)); #if HT_AMD REQUIRE(TestPassed == atomictest_manywaves(LONG_INITIAL_VALUE)); REQUIRE(TestPassed == atomictest_manywaves(DOUBLE_INITIAL_VALUE)); #endif } SECTION("atomic tests with many waves and no return") { REQUIRE(TestPassed == atomictestnoret_manywaves(FLOAT_INITIAL_VALUE)); } SECTION("simple atomic tests") { REQUIRE(TestPassed == atomictest_simple(INT_INITIAL_VALUE)); REQUIRE(TestPassed == atomictest_simple(UNSIGNED_INITIAL_VALUE)); REQUIRE(TestPassed == atomictest_simple(FLOAT_INITIAL_VALUE)); #if HT_AMD REQUIRE(TestPassed == atomictest_simple(LONG_INITIAL_VALUE)); REQUIRE(TestPassed == atomictest_simple(DOUBLE_INITIAL_VALUE)); #endif } SECTION("Simple atomic test with no return") { REQUIRE(TestPassed == atomictestnoret_simple(FLOAT_INITIAL_VALUE)); } }