From 72e64327397e5c1d6f13a4439b303052d5ff1ff2 Mon Sep 17 00:00:00 2001 From: milos-mozetic <118800401+milos-mozetic@users.noreply.github.com> Date: Mon, 6 Mar 2023 04:28:26 +0100 Subject: [PATCH 01/13] EXSWHTEC-174 - Implement unit tests for Callback Activity APIs (#6) Implemented positive and negative test cases for the following APIs: - hipApiName - hipKernelNameRef - hipKernelNameRefByPtr - hipGetStreamDeviceId Enabled build of Callback tests only for AMD platforms, as there is no support for Nvidia platforms. --- .../config/config_amd_linux_common.json | 1 + .../config/config_amd_windows_common.json | 2 + catch/unit/CMakeLists.txt | 1 + catch/unit/callback/CMakeLists.txt | 44 +++++++ catch/unit/callback/SimpleKernel.cc | 27 +++++ catch/unit/callback/hipApiName.cc | 79 ++++++++++++ catch/unit/callback/hipGetStreamDeviceId.cc | 112 ++++++++++++++++++ catch/unit/callback/hipKernelNameRef.cc | 71 +++++++++++ catch/unit/callback/hipKernelNameRefByPtr.cc | 105 ++++++++++++++++ 9 files changed, 442 insertions(+) create mode 100644 catch/unit/callback/CMakeLists.txt create mode 100644 catch/unit/callback/SimpleKernel.cc create mode 100644 catch/unit/callback/hipApiName.cc create mode 100644 catch/unit/callback/hipGetStreamDeviceId.cc create mode 100644 catch/unit/callback/hipKernelNameRef.cc create mode 100644 catch/unit/callback/hipKernelNameRefByPtr.cc diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index a4e1fd0cb1..5fdaefd781 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -15,6 +15,7 @@ "Unit_hipMemset_Negative_OutOfBoundsPtr", "Unit_hipDeviceReset_Positive_Basic", "Unit_hipDeviceReset_Positive_Threaded", + "Unit_hipKernelNameRef_Negative_Parameters", "Unit_hipMemAdvise_AccessedBy_All_Devices", "Unit_hipMemAdvise_No_Flag_Interference", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep", diff --git a/catch/hipTestMain/config/config_amd_windows_common.json b/catch/hipTestMain/config/config_amd_windows_common.json index 73265230ea..5bfc0356d7 100644 --- a/catch/hipTestMain/config/config_amd_windows_common.json +++ b/catch/hipTestMain/config/config_amd_windows_common.json @@ -95,6 +95,8 @@ "Unit_hipStreamSynchronize_NullStreamAndStreamPerThread", "Note: intermittent Seg fault failure ", "Unit_hipGraphAddEventRecordNode_Functional_WithoutFlags", + "Unit_hipKernelNameRef_Negative_Parameters", + "Unit_hipKernelNameRef_Positive_Basic", "Unit_hipMemAdvise_AccessedBy_All_Devices", "Unit_hipMemAdvise_No_Flag_Interference", "Unit_hipGraphAddEventRecordNode_Functional_WithoutFlags", diff --git a/catch/unit/CMakeLists.txt b/catch/unit/CMakeLists.txt index 57eb632947..63bbeda241 100644 --- a/catch/unit/CMakeLists.txt +++ b/catch/unit/CMakeLists.txt @@ -35,6 +35,7 @@ add_subdirectory(compiler) add_subdirectory(errorHandling) add_subdirectory(cooperativeGrps) if(HIP_PLATFORM STREQUAL "amd") +add_subdirectory(callback) #add_subdirectory(clock) # Vulkan interop APIs currently undefined for Nvidia add_subdirectory(vulkan_interop) diff --git a/catch/unit/callback/CMakeLists.txt b/catch/unit/callback/CMakeLists.txt new file mode 100644 index 0000000000..204d5bc32c --- /dev/null +++ b/catch/unit/callback/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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. + +# Common Tests - Test independent of all platforms + +set(TEST_SRC + hipApiName.cc + hipGetStreamDeviceId.cc + hipKernelNameRef.cc +) + +if(UNIX) + set(TEST_SRC ${TEST_SRC} hipKernelNameRefByPtr.cc) + + add_custom_target(SimpleKernel.code COMMAND ${CMAKE_CXX_COMPILER} --genco ${OFFLOAD_ARCH_STR} + ${CMAKE_CURRENT_SOURCE_DIR}/SimpleKernel.cc + -o ${CMAKE_CURRENT_BINARY_DIR}/../../unit/callback/SimpleKernel.code + -I${HIP_PATH}/include -I${CMAKE_CURRENT_SOURCE_DIR}/../../include) +endif() + +hip_add_exe_to_target(NAME CallbackTest + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests) + +if(UNIX) + add_dependencies(CallbackTest SimpleKernel.code) +endif() diff --git a/catch/unit/callback/SimpleKernel.cc b/catch/unit/callback/SimpleKernel.cc new file mode 100644 index 0000000000..3bb045ffd1 --- /dev/null +++ b/catch/unit/callback/SimpleKernel.cc @@ -0,0 +1,27 @@ +/* +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 "hip/hip_runtime.h" + +extern "C" __global__ void simple_kernel() { + printf("Hello World!"); +} diff --git a/catch/unit/callback/hipApiName.cc b/catch/unit/callback/hipApiName.cc new file mode 100644 index 0000000000..51f778c4dc --- /dev/null +++ b/catch/unit/callback/hipApiName.cc @@ -0,0 +1,79 @@ +/* +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 + +/** + * @addtogroup hipApiName hipApiName + * @{ + * @ingroup CallbackTest + * `hipApiName(uint32_t id)` - + * returns the name of API with passed ID + */ + +const char* kUnknownApi{"unknown"}; +const uint32_t kApiNumber{1024}; + +/** + * Test Description + * ------------------------ + * - Acquires HIP API names and checks that they are valid + * Test source + * ------------------------ + * - unit/callback/hipApiName.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipApiName_Positive_Basic") { + std::vector hip_api_names; + + for(uint32_t i = 0; i < kApiNumber; ++i) { + if(strcmp(hipApiName(i), kUnknownApi)) { + hip_api_names.emplace_back(hipApiName(i)); + } + } + + REQUIRE(!hip_api_names.empty()); +} + +/** + * Test Description + * ------------------------ + * - Checks that upper and lower limit IDs are mapped to unknown APIs: + * -# When the `uint32_t` upper limit is passed + * - Expected output: return "unknown" + * -# When the `uint32_t` lower limit is passed + * - Expected output: return "unknown" + * Test source + * ------------------------ + * - unit/callback/hipApiName.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipApiName_Negative_ReservedIds") { + REQUIRE_THAT(hipApiName(std::numeric_limits::min()), Catch::Equals(kUnknownApi)); + REQUIRE_THAT(hipApiName(std::numeric_limits::max()), Catch::Equals(kUnknownApi)); +} diff --git a/catch/unit/callback/hipGetStreamDeviceId.cc b/catch/unit/callback/hipGetStreamDeviceId.cc new file mode 100644 index 0000000000..e8d718387e --- /dev/null +++ b/catch/unit/callback/hipGetStreamDeviceId.cc @@ -0,0 +1,112 @@ +/* +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 + +/** + * @addtogroup hipGetStreamDeviceId hipGetStreamDeviceId + * @{ + * @ingroup CallbackTest + * `hipGetStreamDeviceId(hipStream_t stream)` - + * returns the ID of the device on which the stream is active + */ + +/** + * Test Description + * ------------------------ + * - Creates a new stream for each available device + * - Verifies that the Device Stream ID is equal to the Device ID + * Test source + * ------------------------ + * - unit/callback/hipGetStreamDeviceId.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipGetStreamDeviceId_Positive_Threaded_Basic") { + int id = GENERATE(range(0, HipTest::getDeviceCount())); + HIP_CHECK(hipSetDevice(id)); + + StreamGuard stream_guard{Streams::created}; + REQUIRE(hipGetStreamDeviceId(stream_guard.stream()) == id); +} + +/** + * Test Description + * ------------------------ + * - Creates a new stream for each available device, through multiple threads + * - Verifies that the Device Stream ID is equal to the Device ID, from each thread + * Test source + * ------------------------ + * - unit/callback/hipGetStreamDeviceId.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + * - Multithreaded GPU + */ +TEST_CASE("Unit_hipGetStreamDeviceId_Positive_Multithreaded_Basic") { + const unsigned int max_threads = std::thread::hardware_concurrency(); + const int device_count = HipTest::getDeviceCount(); + + auto thread_function = [&]() { + for(unsigned int id = 0; id < device_count; ++id) { + HIP_CHECK_THREAD(hipSetDevice(id)); + + StreamGuard stream_guard{Streams::perThread}; + REQUIRE_THREAD(hipGetStreamDeviceId(stream_guard.stream()) == id); + } + }; + + std::vector thread_pool; + for(unsigned int i = 0; i < max_threads; ++i) { + thread_pool.emplace_back(thread_function); + } + + for(auto& thread: thread_pool) + { + thread.join(); + } + + HIP_CHECK_THREAD_FINALIZE(); +} + +/** + * Test Description + * ------------------------ + * - Checks that function returns valid ID if the stream is `nullptr` + * Test source + * ------------------------ + * - unit/callback/hipGetStreamDeviceId.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipGetStreamDeviceId_Negative_Parameters") { + int id = GENERATE(range(0, HipTest::getDeviceCount())); + HIP_CHECK(hipSetDevice(id)); + + StreamGuard stream_guard{Streams::nullstream}; + REQUIRE(hipGetStreamDeviceId(stream_guard.stream()) == id); +} diff --git a/catch/unit/callback/hipKernelNameRef.cc b/catch/unit/callback/hipKernelNameRef.cc new file mode 100644 index 0000000000..ff9364c3db --- /dev/null +++ b/catch/unit/callback/hipKernelNameRef.cc @@ -0,0 +1,71 @@ +/* +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 + +/** + * @addtogroup hipKernelNameRef hipKernelNameRef + * @{ + * @ingroup CallbackTest + * `hipKernelNameRef(const hipFunction_t f)` - + * returns the name of passed function object + */ + +/** + * Test Description + * ------------------------ + * - Loads the simple kernel function from the matching module + * - Checks that the valid name is returned for the loaded kernel function + * Test source + * ------------------------ + * - unit/callback/hipKernelNameRef.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipKernelNameRef_Positive_Basic") { + hipModule_t kernel_module{nullptr}; + hipFunction_t kernel_function{nullptr}; + + HIP_CHECK(hipModuleLoad(&kernel_module, "SimpleKernel.code")); + HIP_CHECK(hipModuleGetFunction(&kernel_function, kernel_module, "simple_kernel")); + REQUIRE(hipKernelNameRef(kernel_function) != nullptr); + HIP_CHECK(hipModuleUnload(kernel_module)); +} + +/** + * Test Description + * ------------------------ + * - Checks that the API returns nullptr if the passed function is not loaded + * Test source + * ------------------------ + * - unit/callback/hipKernelNameRef.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipKernelNameRef_Negative_Parameters") { + hipFunction_t kernel_function{nullptr}; + REQUIRE(hipKernelNameRef(kernel_function) == nullptr); +} diff --git a/catch/unit/callback/hipKernelNameRefByPtr.cc b/catch/unit/callback/hipKernelNameRefByPtr.cc new file mode 100644 index 0000000000..dc4d7205f4 --- /dev/null +++ b/catch/unit/callback/hipKernelNameRefByPtr.cc @@ -0,0 +1,105 @@ +/* +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 + +/** + * @addtogroup hipKernelNameRefByPtr hipKernelNameRefByPtr + * @{ + * @ingroup CallbackTest + * `hipKernelNameRefByPtr(const void* hostFunction, hipStream_t stream)` - + * returns the name of passed function pointer on desired stream + */ + +__global__ void test_kernel() { + return; +} + +/** + * Test Description + * ------------------------ + * - Creates new stream and a function pointer + * - Verifies that valid API name is returned + * Test source + * ------------------------ + * - unit/callback/hipKernelNameRefByPtr.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipKernelNameRefByPtr_Positive_Basic") { + const void* kernel_ptr{reinterpret_cast(&test_kernel)}; + + StreamGuard stream_guard{Streams::created}; + REQUIRE(hipKernelNameRefByPtr(kernel_ptr, stream_guard.stream()) != nullptr); +} + +/** + * Test Description + * ------------------------ + * - Passes `nullptr` stream while function pointer is valid + * - Verifies that the returned value is not `nullptr` + * Test source + * ------------------------ + * - unit/callback/hipKernelNameRefByPtr.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipKernelNameRefByPtr_Negative_StreamNullptr") { + const void* kernel_ptr{reinterpret_cast(&test_kernel)}; + StreamGuard stream_guard{Streams::nullstream}; + + REQUIRE(hipKernelNameRefByPtr(kernel_ptr, stream_guard.stream()) != nullptr); +} + +/** + * Test Description + * ------------------------ + * - Performs validation when the function pointer is `nullptr` + * -# When stream is `nullptr` + * - Expected output: return `nullptr` + * -# When stream is valid + * - Expected output: return `nullptr` + * Test source + * ------------------------ + * - unit/callback/hipKernelNameRefByPtr.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + * - Platform specific (AMD) + */ +TEST_CASE("Unit_hipKernelNameRefByPtr_Negative_KernelNullptr") { + const void* kernel_ptr{nullptr}; + + SECTION("stream is nullptr") { + StreamGuard stream_guard{Streams::nullstream}; + REQUIRE(hipKernelNameRefByPtr(kernel_ptr, stream_guard.stream()) == nullptr); + } + + SECTION("stream is created") { + StreamGuard stream_guard{Streams::created}; + REQUIRE(hipKernelNameRefByPtr(kernel_ptr, stream_guard.stream()) == nullptr); + } +} From f1151e3c6d035018c32a1d412b990b572456ac0a Mon Sep 17 00:00:00 2001 From: music-dino <111048524+music-dino@users.noreply.github.com> Date: Mon, 6 Mar 2023 04:30:35 +0100 Subject: [PATCH 02/13] EXSWHTEC-192 - Implement new and update existing tests for the hipGraph*MemcpyNode[From|To]Symbol family of APIs (#13) * EXSWHTEC-192 - Implement new and update existing tests for the hipGraph*MemcpyNode[From|To]Symbol family of APIs - Generalize from symbol positive test to work for different types - Implement negative parameter tests - Extract common code for to/from copy - Reorganize files --- .../config/config_amd_linux_common.json | 4 + .../config/config_amd_windows_common.json | 4 + catch/unit/graph/CMakeLists.txt | 6 + .../graph_memcpy_to_from_symbol_common.hh | 220 ++++++++ catch/unit/graph/graph_tests_common.hh | 76 +++ .../graph/hipGraphAddMemcpyNodeFromSymbol.cc | 526 ++++-------------- .../hipGraphAddMemcpyNodeFromSymbol_old.cc | 443 +++++++++++++++ .../graph/hipGraphAddMemcpyNodeToSymbol.cc | 496 ++++------------- .../hipGraphAddMemcpyNodeToSymbol_old.cc | 402 +++++++++++++ ...pGraphExecMemcpyNodeSetParamsFromSymbol.cc | 423 ++++++-------- ...phExecMemcpyNodeSetParamsFromSymbol_old.cc | 303 ++++++++++ ...hipGraphExecMemcpyNodeSetParamsToSymbol.cc | 460 ++++++--------- ...raphExecMemcpyNodeSetParamsToSymbol_old.cc | 316 +++++++++++ .../hipGraphMemcpyNodeSetParamsFromSymbol.cc | 363 +++++------- ...pGraphMemcpyNodeSetParamsFromSymbol_old.cc | 260 +++++++++ .../hipGraphMemcpyNodeSetParamsToSymbol.cc | 375 +++++-------- ...hipGraphMemcpyNodeSetParamsToSymbol_old.cc | 264 +++++++++ 17 files changed, 3139 insertions(+), 1802 deletions(-) create mode 100644 catch/unit/graph/graph_memcpy_to_from_symbol_common.hh create mode 100644 catch/unit/graph/graph_tests_common.hh create mode 100644 catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol_old.cc create mode 100644 catch/unit/graph/hipGraphAddMemcpyNodeToSymbol_old.cc create mode 100644 catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol_old.cc create mode 100644 catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol_old.cc create mode 100644 catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol_old.cc create mode 100644 catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol_old.cc diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index 5fdaefd781..f1a3283fb6 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -15,6 +15,10 @@ "Unit_hipMemset_Negative_OutOfBoundsPtr", "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", diff --git a/catch/hipTestMain/config/config_amd_windows_common.json b/catch/hipTestMain/config/config_amd_windows_common.json index 5bfc0356d7..af9e8e7ca9 100644 --- a/catch/hipTestMain/config/config_amd_windows_common.json +++ b/catch/hipTestMain/config/config_amd_windows_common.json @@ -95,6 +95,10 @@ "Unit_hipStreamSynchronize_NullStreamAndStreamPerThread", "Note: intermittent Seg fault failure ", "Unit_hipGraphAddEventRecordNode_Functional_WithoutFlags", + "Unit_hipGraphMemcpyNodeSetParamsToSymbol_Positive_Basic", + "Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Positive_Basic", + "Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Positive_Basic", + "Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Positive_Basic", "Unit_hipKernelNameRef_Negative_Parameters", "Unit_hipKernelNameRef_Positive_Basic", "Unit_hipMemAdvise_AccessedBy_All_Devices", diff --git a/catch/unit/graph/CMakeLists.txt b/catch/unit/graph/CMakeLists.txt index beea08f1aa..f7bb6f054b 100644 --- a/catch/unit/graph/CMakeLists.txt +++ b/catch/unit/graph/CMakeLists.txt @@ -30,12 +30,15 @@ set(TEST_SRC hipGraphClone.cc hipGraphInstantiateWithFlags.cc hipGraphAddHostNode.cc + hipGraphAddMemcpyNodeFromSymbol_old.cc hipGraphAddMemcpyNodeFromSymbol.cc hipGraphChildGraphNodeGetGraph.cc hipGraphNodeFindInClone.cc hipGraphExecHostNodeSetParams.cc + hipGraphAddMemcpyNodeToSymbol_old.cc hipGraphAddMemcpyNodeToSymbol.cc hipGraphExecMemsetNodeSetParams.cc + hipGraphMemcpyNodeSetParamsToSymbol_old.cc hipGraphMemcpyNodeSetParamsToSymbol.cc hipGraphDestroyNode.cc hipGraphGetNodes.cc @@ -53,6 +56,7 @@ set(TEST_SRC hipGraphEventWaitNodeSetEvent.cc hipGraphMemsetNodeGetParams.cc hipGraphMemsetNodeSetParams.cc + hipGraphExecMemcpyNodeSetParamsFromSymbol_old.cc hipGraphExecMemcpyNodeSetParamsFromSymbol.cc hipGraphEventRecordNodeGetEvent.cc hipGraphEventRecordNodeSetEvent.cc @@ -62,6 +66,7 @@ set(TEST_SRC hipStreamIsCapturing.cc hipStreamGetCaptureInfo.cc hipStreamEndCapture.cc + hipGraphMemcpyNodeSetParamsFromSymbol_old.cc hipGraphMemcpyNodeSetParamsFromSymbol.cc hipGraphExecEventWaitNodeSetEvent.cc hipGraphAddMemsetNode.cc @@ -73,6 +78,7 @@ set(TEST_SRC hipGraphExecKernelNodeSetParams.cc hipGraphLaunch.cc hipGraphMemcpyNodeSetParams1D.cc + hipGraphExecMemcpyNodeSetParamsToSymbol_old.cc hipGraphExecMemcpyNodeSetParamsToSymbol.cc hipGraphNodeGetDependentNodes.cc hipGraphNodeGetDependencies.cc diff --git a/catch/unit/graph/graph_memcpy_to_from_symbol_common.hh b/catch/unit/graph/graph_memcpy_to_from_symbol_common.hh new file mode 100644 index 0000000000..e2c183b786 --- /dev/null +++ b/catch/unit/graph/graph_memcpy_to_from_symbol_common.hh @@ -0,0 +1,220 @@ +/* +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 +#include + +#include +#include + +namespace { +constexpr size_t kArraySize = 5; +} + +#define HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(type) \ + __device__ type type##_device_var = 1; \ + __constant__ __device__ type type##_const_device_var = 1; \ + __device__ type type##_device_arr[kArraySize] = {1, 2, 3, 4, 5}; \ + __constant__ __device__ type type##_const_device_arr[kArraySize] = {1, 2, 3, 4, 5}; + +#define HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(type) \ + __device__ type type##_alt_device_var = 0; \ + __constant__ __device__ type type##_alt_const_device_var = 0; \ + __device__ type type##_alt_device_arr[kArraySize] = {0, 0, 0, 0, 0}; \ + __constant__ __device__ type type##_alt_const_device_arr[kArraySize] = {0, 0, 0, 0, 0}; + +template +void MemcpyFromSymbolShell(F f, const void* symbol, size_t offset, const std::vector expected) { + const auto alloc_type = GENERATE(LinearAllocs::hipMalloc, LinearAllocs::hipHostMalloc); + const auto size = expected.size() * sizeof(T); + LinearAllocGuard dst_alloc(alloc_type, size); + + hipMemcpyKind direction; + if (alloc_type == LinearAllocs::hipMalloc) { + direction = GENERATE(hipMemcpyDeviceToDevice, hipMemcpyDefault); + } else { + direction = GENERATE(hipMemcpyDeviceToHost, hipMemcpyDefault); + } + INFO("Memcpy direction: " << direction); + HIP_CHECK(f(dst_alloc.ptr(), symbol, size, offset * sizeof(T), direction)); + + std::vector symbol_values(expected.size()); + HIP_CHECK(hipMemcpy(symbol_values.data(), dst_alloc.ptr(), size, hipMemcpyDefault)); + REQUIRE_THAT(expected, Catch::Equals(symbol_values)); +} + +template +void MemcpyToSymbolShell(F f, const void* symbol, size_t offset, const std::vector set_values) { + const auto alloc_type = GENERATE(LinearAllocs::hipMalloc, LinearAllocs::hipHostMalloc); + const auto size = set_values.size() * sizeof(T); + LinearAllocGuard src_alloc(alloc_type, size); + HIP_CHECK(hipMemcpy(src_alloc.ptr(), set_values.data(), size, hipMemcpyDefault)); + + hipMemcpyKind direction; + if (alloc_type == LinearAllocs::hipMalloc) { + direction = GENERATE(hipMemcpyDeviceToDevice, hipMemcpyDefault); + } else { + direction = GENERATE(hipMemcpyHostToDevice, hipMemcpyDefault); + } + INFO("Memcpy direction: " << direction); + HIP_CHECK(f(symbol, src_alloc.ptr(), size, offset * sizeof(T), direction)); + + std::vector symbol_values(set_values.size()); + HIP_CHECK(hipMemcpyFromSymbol(symbol_values.data(), symbol, size, offset * sizeof(T))); + REQUIRE_THAT(set_values, Catch::Equals(symbol_values)); +} + +template +void MemcpyFromSymbolCommonNegative(F f, void* dst, const void* symbol, size_t count) { + SECTION("dst == nullptr") { + HIP_CHECK_ERROR(f(nullptr, symbol, count, 0, hipMemcpyDefault), hipErrorInvalidValue); + } + + SECTION("symbol == nullptr") { + HIP_CHECK_ERROR(f(dst, nullptr, count, 0, hipMemcpyDefault), hipErrorInvalidSymbol); + } + +// Disabled on AMD due to defect - EXSWHTEC-215 +#if HT_NVIDIA + SECTION("count == 0") { + HIP_CHECK_ERROR(f(dst, symbol, 0, 0, hipMemcpyDefault), hipErrorInvalidValue); + } +#endif + + SECTION("count > symbol size") { + HIP_CHECK_ERROR(f(dst, symbol, count + 1, 0, hipMemcpyDefault), hipErrorInvalidValue); + } + + SECTION("count + offset > symbol size") { + HIP_CHECK_ERROR(f(dst, symbol, count, 1, hipMemcpyDefault), hipErrorInvalidValue); + } + +// Disabled on AMD due to defect +#if HT_NVIDIA + SECTION("Illogical memcpy direction") { + HIP_CHECK_ERROR(f(dst, symbol, count, 0, hipMemcpyHostToDevice), + hipErrorInvalidMemcpyDirection); + } + + SECTION("Invalid memcpy direction") { + HIP_CHECK_ERROR(f(dst, symbol, count, 0, static_cast(-1)), + hipErrorInvalidMemcpyDirection); + } +#endif +} + +template +void MemcpyToSymbolCommonNegative(F f, const void* symbol, void* src, size_t count) { + SECTION("src == nullptr") { + HIP_CHECK_ERROR(f(symbol, nullptr, count, 0, hipMemcpyDefault), hipErrorInvalidValue); + } + + SECTION("symbol == nullptr") { + HIP_CHECK_ERROR(f(nullptr, src, count, 0, hipMemcpyDefault), hipErrorInvalidSymbol); + } + +// Disabled on AMD due to defect - EXSWHTEC-215 +#if HT_NVIDIA + SECTION("count == 0") { + HIP_CHECK_ERROR(f(symbol, src, 0, 0, hipMemcpyDefault), hipErrorInvalidValue); + } +#endif + + SECTION("count > symbol size") { + HIP_CHECK_ERROR(f(symbol, src, count + 1, 0, hipMemcpyDefault), hipErrorInvalidValue); + } + + SECTION("count + offset > symbol size") { + HIP_CHECK_ERROR(f(symbol, src, count, 1, hipMemcpyDefault), hipErrorInvalidValue); + } + +// Disabled on AMD due to defect +#if HT_NVIDIA + SECTION("Illogical memcpy direction") { + HIP_CHECK_ERROR(f(symbol, src, count, 0, hipMemcpyDeviceToHost), + hipErrorInvalidMemcpyDirection); + } + + SECTION("Invalid memcpy direction") { + HIP_CHECK_ERROR(f(symbol, src, count, 0, static_cast(-1)), + hipErrorInvalidMemcpyDirection); + } +#endif +} + +#if HT_AMD +#define SYMBOL(expr) &HIP_SYMBOL(expr) +#else +#define SYMBOL(expr) HIP_SYMBOL(expr) +#endif + +#define HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(f, init_val, type) \ + SECTION("Scalar variable") { f(SYMBOL(type##_device_var), 0, std::vector{init_val}); } \ + \ + SECTION("Constant scalar variable") { \ + f(SYMBOL(type##_const_device_var), 0, std::vector{init_val}); \ + } \ + \ + SECTION("Array") { \ + const auto offset = GENERATE(0, kArraySize / 2); \ + INFO("Array offset: " << offset); \ + std::vector expected(kArraySize - offset); \ + std::iota(expected.begin(), expected.end(), offset + init_val); \ + f(SYMBOL(type##_device_arr), offset, std::move(expected)); \ + } \ + \ + SECTION("Constant array") { \ + const auto offset = GENERATE(0, kArraySize / 2); \ + INFO("Array offset: " << offset); \ + std::vector expected(kArraySize - offset); \ + std::iota(expected.begin(), expected.end(), offset + init_val); \ + f(SYMBOL(type##_const_device_arr), offset, std::move(expected)); \ + } + +#define HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(f, init_val, type) \ + SECTION("Scalar variable") { \ + f(SYMBOL(type##_device_var), SYMBOL(type##_alt_device_var), 0, std::vector{init_val}); \ + } \ + \ + SECTION("Constant scalar variable") { \ + f(SYMBOL(type##_const_device_var), SYMBOL(type##_alt_const_device_var), 0, \ + std::vector{init_val}); \ + } \ + \ + SECTION("Array") { \ + const auto offset = GENERATE(0, kArraySize / 2); \ + INFO("Array offset: " << offset); \ + std::vector expected(kArraySize - offset); \ + std::iota(expected.begin(), expected.end(), offset + init_val); \ + f(SYMBOL(type##_device_arr), SYMBOL(type##_alt_device_arr), offset, std::move(expected)); \ + } \ + \ + SECTION("Constant array") { \ + const auto offset = GENERATE(0, kArraySize / 2); \ + INFO("Array offset: " << offset); \ + std::vector expected(kArraySize - offset); \ + std::iota(expected.begin(), expected.end(), offset + init_val); \ + f(SYMBOL(type##_const_device_arr), SYMBOL(type##_alt_const_device_arr), offset, \ + std::move(expected)); \ + } diff --git a/catch/unit/graph/graph_tests_common.hh b/catch/unit/graph/graph_tests_common.hh new file mode 100644 index 0000000000..bb28ec5ea5 --- /dev/null +++ b/catch/unit/graph/graph_tests_common.hh @@ -0,0 +1,76 @@ +/* +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 + +#include +#include + +template void GraphAddNodeCommonNegativeTests(F f, hipGraph_t graph) { + hipGraphNode_t node = nullptr; + SECTION("graph == nullptr") { + HIP_CHECK_ERROR(f(&node, nullptr, nullptr, 0), hipErrorInvalidValue); + } + + SECTION("node == nullptr") { + HIP_CHECK_ERROR(f(nullptr, graph, nullptr, 0), hipErrorInvalidValue); + } + + SECTION("dependencies == nullptr with size != 0") { + HIP_CHECK_ERROR(f(&node, graph, nullptr, 1), hipErrorInvalidValue); + } + +// Disabled on AMD due to defect - EXSWHTEC-202 +#if HT_NVIDIA + SECTION("Node in dependency is from different graph") { + hipGraph_t other_graph = nullptr; + HIP_CHECK(hipGraphCreate(&other_graph, 0)); + hipGraphNode_t other_node = nullptr; + HIP_CHECK(hipGraphAddEmptyNode(&other_node, other_graph, nullptr, 0)); + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddEmptyNode(&node, graph, nullptr, 0)); + HIP_CHECK_ERROR(f(&node, graph, &other_node, 1), hipErrorInvalidValue); + HIP_CHECK(hipGraphDestroy(other_graph)); + } +#endif + + SECTION("Invalid numNodes") { + hipGraphNode_t dep_node = nullptr; + HIP_CHECK(hipGraphAddEmptyNode(&dep_node, graph, nullptr, 0)); + HIP_CHECK_ERROR(f(&node, graph, &dep_node, 2), hipErrorInvalidValue); + } + +// Disabled on AMD due to defect - EXSWHTEC-201 +#if HT_NVIDIA + SECTION("Duplicate node in dependencies") { + hipGraphNode_t dep_node = nullptr; + // Need to create two nodes to avoid overlap with Invalid numNodes case + // First one is left dangling as the graph will be destroyed after the section anyway + HIP_CHECK(hipGraphAddEmptyNode(&dep_node, graph, nullptr, 0)); + HIP_CHECK(hipGraphAddEmptyNode(&dep_node, graph, nullptr, 0)); + hipGraphNode_t deps[] = {dep_node, dep_node}; + HIP_CHECK_ERROR(f(&node, graph, deps, 2), hipErrorInvalidValue); + } +#endif +} \ No newline at end of file diff --git a/catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol.cc b/catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol.cc index fa05c7fe66..4bceaa41b7 100644 --- a/catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol.cc +++ b/catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol.cc @@ -6,432 +6,144 @@ 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Testcase Scenarios of hipGraphAddMemcpyNodeFromSymbol API: - -Functional : - -1. Allocate global symbol memory, add the MemcpyNodeFromSymbol - node to the graph and verify for different memory kinds -2. Allocate const memory add the MemcpyNodeFromSymbol node to - the graph and verify for different memory kinds -3. Allocate global symbol memory and device memory in GPU-0 - and perform MemcpyToSymbol from peer GPU by adding it to the graph node. -4. Allocate const symbol memory and device memory in GPU-0 - and perform MemcpyToSymbol from peer GPU by adding it to the graph node. -5. Allocate global memory, Add MemcpyFromSymbolNode,KernelNode and memcpynode and validating - the behaviour - -Negative : - -1) Pass nullptr to graph node -2) Pass nullptr to graph -3) Pass nullptr to dependencies -4) Pass invalid numDependencies -5) Pass nullptr to dst -6) Pass nullptr to symbol -7) Pass invalid count -8) Pass offset+count greater than allocated size -9) Pass unintialized graph -*/ +#include +#include +#include #include #include -#include -#define SIZE 256 -__device__ int globalIn[SIZE]; -__device__ int globalOut[SIZE]; -__device__ __constant__ int globalConst[SIZE]; +#include "graph_memcpy_to_from_symbol_common.hh" +#include "graph_tests_common.hh" -__global__ void MemcpyFromSymbolKernel(int* B_d) { - for (int i = 0 ; i < SIZE; i++) { - globalIn[i] = B_d[i]; +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(double) + +template +void GraphMemcpyFromSymbolShell(void* symbol, size_t offset, const std::vector expected) { + const auto f = [](void* dst, const void* symbol, size_t count, size_t offset, + hipMemcpyKind direction) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&node, graph, nullptr, 0, dst, symbol, count, offset, + direction)); + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); + + return hipSuccess; + }; + + MemcpyFromSymbolShell(f, symbol, offset, std::move(expected)); +} + +/** + * @addtogroup hipGraphAddMemcpyNodeFromSymbol hipGraphAddMemcpyNodeFromSymbol + * @{ + * @ingroup GraphTest + * `hipGraphAddMemcpyNodeFromSymbol(hipGraphNode_t *pGraphNode, hipGraph_t graph, const + * hipGraphNode_t *pDependencies, size_t numDependencies, void *dst, const void *symbol, size_t + * count, size_t offset, hipMemcpyKind kind)` - + * Creates a memcpy node to copy from a symbol on the device and adds it to a graph + */ + +/** + * Test Description + * ------------------------ + * - Verify that data is correctly copied from a symbol. A graph is constructed to which a + * MemcpyFromSymbol node is added. After graph execution, values in destination memory are compared + * against values known to be in symbol memory. + * The test is run for scalar, const scalar, array, and const array symbols of types char, int, + * float and double. For array symbols, the test is repeated for zero and non-zero offset values. + * Verification is performed for destination memory allocated on host and device. + * Test source + * ------------------------ + * - unit/graph/hipGraphAddMemcpyNodeFromSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_Positive_Basic") { + SECTION("char") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolShell, 1, char); + } + + SECTION("int") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolShell, 1, int); + } + + SECTION("float") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolShell, 1, float); + } + + SECTION("double") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolShell, 1, double); } } -/* This testcase verifies negative scenarios of - hipGraphAddMemcpyNodeFromSymbol API */ -TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_Negative") { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; - std::vector dependencies; +/** + * Test Description + * ------------------------ + * - Verify API behavior with invalid arguments: + * -# pGraphNodes is nullptr + * -# graph is nullptr + * -# pDependencies is nullptr when numDependencies is non-zero + * -# A node in pDependencies belongs to a different graph + * -# numDependencies in invalid + * -# A node appears twice in pDependencies + * -# dst is nullptr + * -# symbol is nullptr + * -# count is zero + * -# count is larger than symbol size + * -# count + offset is larger than symbol size + * -# kind is illogical (hipMemcpyHostToDevice) + * -# kind is an invalid enum value + * Test source + * ------------------------ + * - unit/graph/hipGraphAddMemcpyNodeFromSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; HIP_CHECK(hipGraphCreate(&graph, 0)); - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); + int var = 0; + hipGraphNode_t node = nullptr; - // Adding MemcpyNodeToSymbol - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); + GraphAddNodeCommonNegativeTests( + std::bind(hipGraphAddMemcpyNodeFromSymbol, _1, _2, _3, _4, &var, SYMBOL(int_device_var), + sizeof(var), 0, hipMemcpyDefault), + graph); -#if HT_NVIDIA - hipGraphNode_t memcpyFromSymbolNode; - SECTION("Passing nullptr to graph") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, nullptr, - dependencies.data(), - dependencies.size(), - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } + MemcpyFromSymbolCommonNegative( + std::bind(hipGraphAddMemcpyNodeFromSymbol, &node, graph, nullptr, 0, _1, _2, _3, _4, _5), + &var, SYMBOL(int_device_var), sizeof(var)); - SECTION("Passing nullptr to graph node") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(nullptr, graph, - dependencies.data(), - dependencies.size(), - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing size > 1 and dependencies as nullptr") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - nullptr, - 1, - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing invalid dependencies size") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - 10, - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing nullptr to dst") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - nullptr, - HIP_SYMBOL(globalIn), Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing nullptr to source") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_d, - nullptr, Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidSymbol); - } - - SECTION("Passing offset+size > max size") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 10, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing Max count") { - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_d, - HIP_SYMBOL(globalIn), - std::numeric_limits::max(), 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Pass Unintialized graph") { - hipGraph_t unint_graph; - REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, unint_graph, - dependencies.data(), - dependencies.size(), - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } -#endif - - HipTest::freeArrays(A_d, B_d, nullptr, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphDestroy(graph)); -} -/* -This function is used to verify the following scenarios -1. Create global variable, allocate Memory in GPU-0 and create dependency graph of - hipGraphAddMemcpyNodeFromSymbol API in GPU-1 and validate the result -2. Allocate global memory, Create dependency graph and validate the result on GPU-0 -3. Allocate global const memory, Create dependency graph and validate the result on GPU-0 -4. Create global const variable, allocate Memory in GPU-0 and create dependency graph of - hipGraphAddMemcpyNodeFromSymbol API in GPU-1 and validate the result -*/ - -void hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(bool device_ctxchg = false, - bool const_device_var = - false) { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, nullptr, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - if (device_ctxchg) { - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipDeviceEnablePeerAccess(0, 0)); - } - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - // Adding MemcpyNodeToSymbol - if (const_device_var) { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalConst), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - - } else { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - - // Adding MemcpyNodeFromSymbol - if (const_device_var) { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_h, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToHost)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_h, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToHost)); - } - - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); - - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - - HipTest::freeArrays(A_d, nullptr, nullptr, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); -} -/* -This testcase verifies allocating global symbol memory, -add the MemcpyNodeFromSymbol node to the graph and -erifying the result -*/ -TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalMemory") { - hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(false, false); -} - -/* -This testcase verifies allocating global const symbol memory, -add the MemcpyNodeFromSymbol node to the graph and -verifying the result -*/ - -TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalConstMemory") { - hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(false, true); -} - -/* -This testcase verifies allocating global symbol memory and device variables -in GPU-0 and add the MemcpyNodeFromSymbol node to the graph and -verifying the result in GPU-1 -*/ -#if HT_NVIDIA -TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalMemoryPeerDevice") { - int numDevices = 0; - int canAccessPeer = 0; - if (numDevices > 1) { - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(true, false); - } else { - SUCCEED("Machine does not seem to have P2P"); - } - } else { - SUCCEED("skipped the testcase as no of devices is less than 2"); - } -} - -/* -This testcase verifies allocating global const symbol memory and device variables -in GPU-0 and add the MemcpyNodeFromSymbol node to the graph and -verifying the result in GPU-1 -*/ - -TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalConstMemoryPeerDevice") { - int numDevices = 0; - int canAccessPeer = 0; - if (numDevices > 1) { - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(true, true); - } else { - SUCCEED("Machine does not seem to have P2P"); - } - } else { - SUCCEED("skipped the testcase as no of devices is less than 2"); - } -} -#endif -/* -This testcaser verifies allocating global memory, -Add MemcpyFromSymbolNode,KernelNode and memcpynode and validating -the behaviour -*/ -TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalMemoryWithKernel") { - constexpr size_t Nbytes = SIZE * sizeof(int); - constexpr auto blocksPerCU = 6; // to hide latency - constexpr auto threadsPerBlock = 256; - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); - hipGraphNode_t memcpyfromsymbolkernel, memcpyD2H_B; - hipKernelNodeParams kernelNodeParams{}; - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - dependencies.clear(); - dependencies.push_back(memcpyFromSymbolNode); - - // Adding Kernel node - void* kernelArgs1[] = {&B_d}; - kernelNodeParams.func = - reinterpret_cast(MemcpyFromSymbolKernel); - kernelNodeParams.gridDim = dim3(blocks); - kernelNodeParams.blockDim = dim3(threadsPerBlock); - kernelNodeParams.sharedMemBytes = 0; - kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); - kernelNodeParams.extra = nullptr; - HIP_CHECK(hipGraphAddKernelNode(&memcpyfromsymbolkernel, graph, - dependencies.data(), dependencies.size(), - &kernelNodeParams)); - dependencies.clear(); - dependencies.push_back(memcpyfromsymbolkernel); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), - dependencies.size(), B_h, B_d, - Nbytes, hipMemcpyDeviceToHost)); - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); - - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - - HipTest::freeArrays(A_d, B_d, nullptr, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); HIP_CHECK(hipGraphDestroy(graph)); } diff --git a/catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol_old.cc b/catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol_old.cc new file mode 100644 index 0000000000..8bec35e42b --- /dev/null +++ b/catch/unit/graph/hipGraphAddMemcpyNodeFromSymbol_old.cc @@ -0,0 +1,443 @@ +/* +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. +*/ + +/** +Testcase Scenarios of hipGraphAddMemcpyNodeFromSymbol API: + +Functional : + +1. Allocate global symbol memory, add the MemcpyNodeFromSymbol + node to the graph and verify for different memory kinds +2. Allocate const memory add the MemcpyNodeFromSymbol node to + the graph and verify for different memory kinds +3. Allocate global symbol memory and device memory in GPU-0 + and perform MemcpyToSymbol from peer GPU by adding it to the graph node. +4. Allocate const symbol memory and device memory in GPU-0 + and perform MemcpyToSymbol from peer GPU by adding it to the graph node. +5. Allocate global memory, Add MemcpyFromSymbolNode,KernelNode and memcpynode and validating + the behaviour + +Negative : + +1) Pass nullptr to graph node +2) Pass nullptr to graph +3) Pass nullptr to dependencies +4) Pass invalid numDependencies +5) Pass nullptr to dst +6) Pass nullptr to symbol +7) Pass invalid count +8) Pass offset+count greater than allocated size +9) Pass unintialized graph +*/ + +#include +#include +#include + +#include +#include + +#include "graph_memcpy_to_from_symbol_common.hh" + +#define SIZE 256 + +__device__ int globalIn[SIZE]; +__device__ int globalOut[SIZE]; +__device__ __constant__ int globalConst[SIZE]; + +__global__ void MemcpyFromSymbolKernel(int* B_d) { + for (int i = 0 ; i < SIZE; i++) { + globalIn[i] = B_d[i]; + } +} + +/* This testcase verifies negative scenarios of + hipGraphAddMemcpyNodeFromSymbol API */ +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_Negative") { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + // Adding MemcpyNodeToSymbol + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + +#if HT_NVIDIA + hipGraphNode_t memcpyFromSymbolNode; + SECTION("Passing nullptr to graph") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, nullptr, + dependencies.data(), + dependencies.size(), + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing nullptr to graph node") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(nullptr, graph, + dependencies.data(), + dependencies.size(), + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing size > 1 and dependencies as nullptr") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + nullptr, + 1, + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing invalid dependencies size") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + 10, + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing nullptr to dst") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + nullptr, + HIP_SYMBOL(globalIn), Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing nullptr to source") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_d, + nullptr, Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidSymbol); + } + + SECTION("Passing offset+size > max size") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 10, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing Max count") { + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_d, + HIP_SYMBOL(globalIn), + std::numeric_limits::max(), 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Pass Unintialized graph") { + hipGraph_t unint_graph; + REQUIRE(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, unint_graph, + dependencies.data(), + dependencies.size(), + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } +#endif + + HipTest::freeArrays(A_d, B_d, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphDestroy(graph)); +} +/* +This function is used to verify the following scenarios +1. Create global variable, allocate Memory in GPU-0 and create dependency graph of + hipGraphAddMemcpyNodeFromSymbol API in GPU-1 and validate the result +2. Allocate global memory, Create dependency graph and validate the result on GPU-0 +3. Allocate global const memory, Create dependency graph and validate the result on GPU-0 +4. Create global const variable, allocate Memory in GPU-0 and create dependency graph of + hipGraphAddMemcpyNodeFromSymbol API in GPU-1 and validate the result +*/ + +void hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(bool device_ctxchg = false, + bool const_device_var = + false) { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, nullptr, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + if (device_ctxchg) { + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipDeviceEnablePeerAccess(0, 0)); + } + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + // Adding MemcpyNodeToSymbol + if (const_device_var) { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalConst), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + + } else { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + + // Adding MemcpyNodeFromSymbol + if (const_device_var) { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_h, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToHost)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_h, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToHost)); + } + + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + HIP_CHECK(hipStreamSynchronize(0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, nullptr, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} +/* +This testcase verifies allocating global symbol memory, +add the MemcpyNodeFromSymbol node to the graph and +erifying the result +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalMemory") { + hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(false, false); +} + +/* +This testcase verifies allocating global const symbol memory, +add the MemcpyNodeFromSymbol node to the graph and +verifying the result +*/ + +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalConstMemory") { + hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(false, true); +} + +/* +This testcase verifies allocating global symbol memory and device variables +in GPU-0 and add the MemcpyNodeFromSymbol node to the graph and +verifying the result in GPU-1 +*/ +#if HT_NVIDIA +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalMemoryPeerDevice") { + int numDevices = 0; + int canAccessPeer = 0; + if (numDevices > 1) { + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(true, false); + } else { + SUCCEED("Machine does not seem to have P2P"); + } + } else { + SUCCEED("skipped the testcase as no of devices is less than 2"); + } +} + +/* +This testcase verifies allocating global const symbol memory and device variables +in GPU-0 and add the MemcpyNodeFromSymbol node to the graph and +verifying the result in GPU-1 +*/ + +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalConstMemoryPeerDevice") { + int numDevices = 0; + int canAccessPeer = 0; + if (numDevices > 1) { + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + hipGraphAddMemcpyNodeFromSymbol_GlobalMemory(true, true); + } else { + SUCCEED("Machine does not seem to have P2P"); + } + } else { + SUCCEED("skipped the testcase as no of devices is less than 2"); + } +} +#endif +/* +This testcaser verifies allocating global memory, +Add MemcpyFromSymbolNode,KernelNode and memcpynode and validating +the behaviour +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNodeFromSymbol_GlobalMemoryWithKernel") { + constexpr size_t Nbytes = SIZE * sizeof(int); + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); + hipGraphNode_t memcpyfromsymbolkernel, memcpyD2H_B; + hipKernelNodeParams kernelNodeParams{}; + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + dependencies.clear(); + dependencies.push_back(memcpyFromSymbolNode); + + // Adding Kernel node + void* kernelArgs1[] = {&B_d}; + kernelNodeParams.func = + reinterpret_cast(MemcpyFromSymbolKernel); + kernelNodeParams.gridDim = dim3(blocks); + kernelNodeParams.blockDim = dim3(threadsPerBlock); + kernelNodeParams.sharedMemBytes = 0; + kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); + kernelNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&memcpyfromsymbolkernel, graph, + dependencies.data(), dependencies.size(), + &kernelNodeParams)); + dependencies.clear(); + dependencies.push_back(memcpyfromsymbolkernel); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), + dependencies.size(), B_h, B_d, + Nbytes, hipMemcpyDeviceToHost)); + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + HIP_CHECK(hipStreamSynchronize(0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, B_d, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} \ No newline at end of file diff --git a/catch/unit/graph/hipGraphAddMemcpyNodeToSymbol.cc b/catch/unit/graph/hipGraphAddMemcpyNodeToSymbol.cc index a4d0bede3e..1c8c047f9e 100644 --- a/catch/unit/graph/hipGraphAddMemcpyNodeToSymbol.cc +++ b/catch/unit/graph/hipGraphAddMemcpyNodeToSymbol.cc @@ -1,402 +1,152 @@ /* -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 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Testcase Scenarios of hipGraphAddMemcpyNodeToSymbol API: - -Functional : - -1. Allocate global symbol memory, add the MemcpyNodeToSymbol - node to the graph and verify for different memory kinds -2. Allocate const memory add the MemcpyNodeToSymbol node to - the graph and verify for different memory kinds -3. Allocate global symbol memory and device memory in GPU-0 - and perform MemcpyToSymbol from peer GPU by adding it to the graph node. -4. Allocate const symbol memory and device memory in GPU-0 - and perform MemcpyToSymbol from peer GPU by adding it to the graph node. -5. Allocate global memory, Add MemcpyToSymbolNode,KernelNode and memcpynode and validating - the behaviour - -Negative : - -1) Pass nullptr to graph node -2) Pass nullptr to graph -3) Pass nullptr to dependencies -4) Pass invalid numDependencies -5) Pass nullptr to dst -6) Pass nullptr to symbol -7) Pass invalid count -8) Pass offset+count greater than allocated size -9) Pass unintialized graph -*/ +#include +#include +#include #include #include -#include -#define SIZE 256 -__device__ int globalIn[SIZE]; -__device__ __constant__ int globalConst[SIZE]; +#include "graph_memcpy_to_from_symbol_common.hh" +#include "graph_tests_common.hh" -__global__ void MemcpyToSymbolKernel(int* B_d) { - for (int i = 0 ; i < SIZE; i++) { - B_d[i] = globalIn[i]; +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(double) + +template +void GraphMemcpyToSymbolShell(const void* symbol, size_t offset, const std::vector set_values) { + const auto f = [](const void* symbol, void* src, size_t count, size_t offset, + hipMemcpyKind direction) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&node, graph, nullptr, 0, symbol, src, count, offset, + direction)); + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); + + return hipSuccess; + }; + + MemcpyToSymbolShell(f, symbol, offset, std::move(set_values)); +} + +/** + * @addtogroup hipGraphAddMemcpyNodeToSymbol hipGraphAddMemcpyNodeToSymbol + * @{ + * @ingroup GraphTest + * `hipGraphAddMemcpyNodeToSymbol(hipGraphNode_t *pGraphNode, hipGraph_t graph, const hipGraphNode_t + * *pDependencies, size_t numDependencies, const void *symbol, const void *src, size_t count, size_t + * offset, hipMemcpyKind kind)` - + * Creates a memcpy node to copy to a symbol on the device and adds it to a graph + */ + + +/** + * Test Description + * ------------------------ + * - Verify that data is correctly copied to a symbol. A graph is constructed to which a + * MemcpyToSymbol node is added. After graph execution, a MemcpyFromSymbol is performed and + * the copied values are compared against values known to have been copied to symbol memory + * previously. + * The test is run for scalar, const scalar, array, and const array symbols of types char, int, + * float and double. For array symbols, the test is repeated for zero and non-zero offset values. + * Verification is performed for source memory allocated on host and device. + * Test source + * ------------------------ + * - unit/graph/hipGraphAddMemcpyNodeToSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_Positive_Basic") { + SECTION("char") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolShell, 10, char); + } + + SECTION("int") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolShell, 10, int); + } + + SECTION("float") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolShell, 10, float); + } + + SECTION("double") { + HIP_GRAPH_ADD_MEMCPY_NODE_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolShell, 10, double); } } -/* This testcase verifies negative scenarios of - hipGraphAddMemcpyNodeToSymbol API */ -TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_Negative") { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, nullptr, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphNode_t memcpyH2D_A; - std::vector dependencies; +/** + * Test Description + * ------------------------ + * - Verify API behavior with invalid arguments: + * -# pGraphNodes is nullptr + * -# graph is nullptr + * -# pDependencies is nullptr when numDependencies is non-zero + * -# A node in pDependencies belongs to a different graph + * -# numDependencies in invalid + * -# A node appears twice in pDependencies + * -# src is nullptr + * -# symbol is nullptr + * -# count is zero + * -# count is larger than symbol size + * -# count + offset is larger than symbol size + * -# kind is illogical (hipMemcpyDeviceToHost) + * -# kind is an invalid enum value + * Test source + * ------------------------ + * - unit/graph/hipGraphAddMemcpyNodeToSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; HIP_CHECK(hipGraphCreate(&graph, 0)); - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); -#if HT_NVIDIA - hipGraphNode_t memcpyToSymbolNode; - SECTION("Passing nullptr to graph") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, nullptr, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_h, Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } + int var = 0; + hipGraphNode_t node = nullptr; - SECTION("Passing nullptr to graph node") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(nullptr, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } + GraphAddNodeCommonNegativeTests( + std::bind(hipGraphAddMemcpyNodeToSymbol, _1, _2, _3, _4, SYMBOL(int_device_var), &var, + sizeof(var), 0, hipMemcpyDefault), + graph); - SECTION("Passing size > 1 and dependencies as nullptr") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - nullptr, - 1, - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } + MemcpyToSymbolCommonNegative( + std::bind(hipGraphAddMemcpyNodeToSymbol, &node, graph, nullptr, 0, _1, _2, _3, _4, _5), + SYMBOL(int_device_var), &var, sizeof(var)); - SECTION("Passing invalid dependencies size") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - 10, - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing nullptr to dst") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - nullptr, - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidSymbol); - } - - SECTION("Passing nullptr to source") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - nullptr, Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing offset+size > max size") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 10, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Passing Max count") { - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, - std::numeric_limits::max(), 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } - - SECTION("Pass Unintialized graph") { - hipGraph_t unint_graph; - REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, unint_graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, - Nbytes, 0, - hipMemcpyDeviceToDevice) - == hipErrorInvalidValue); - } -#endif - - HipTest::freeArrays(A_d, nullptr, nullptr, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphDestroy(graph)); -} -/* -This function is used to verify the following scenarios -1. Create global variable, allocate Memory in GPU-0 and create dependency graph of - hipGraphAddMemcpyNodeToSymbol API in GPU-1 and validate the result -2. Allocate global memory, Create dependency graph and validate the result on GPU-0 -3. Allocate global const memory, Create dependency graph and validate the result on GPU-0 -4. Create global const variable, allocate Memory in GPU-0 and create dependency graph of - hipGraphAddMemcpyNodeToSymbol API in GPU-1 and validate the result -*/ -void hipGraphAddMemcpyNodeToSymbol_GlobalMemory(bool device_ctxchg = false, - bool const_device_var = false) { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, nullptr, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - if (device_ctxchg) { - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipDeviceEnablePeerAccess(0, 0)); - } - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - // Adding MemcpyNodeToSymbol - - if (const_device_var) { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalConst), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - // Adding MemcpyNodeFromSymbol - if (const_device_var) { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_h, - HIP_SYMBOL(globalConst), - Nbytes, 0, hipMemcpyDeviceToHost)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_h, - HIP_SYMBOL(globalIn), - Nbytes, 0, hipMemcpyDeviceToHost)); - } - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); - - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - - HipTest::freeArrays(A_d, nullptr, nullptr, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); -} -/* -This testcase verifies allocating global symbol memory, -add the MemcpyNodeToSymbol node to the graph and -erifying the result -*/ -TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalMemory") { - hipGraphAddMemcpyNodeToSymbol_GlobalMemory(false, false); -} - -/* -This testcase verifies allocating global const symbol memory, -add the MemcpyNodeToSymbol node to the graph and -verifying the result -*/ -TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalConstMemory") { - hipGraphAddMemcpyNodeToSymbol_GlobalMemory(false, true); -} - -#if HT_NVIDIA -/* -This testcase verifies allocating global symbol memory and device variables -in GPU-0 and add the MemcpyNodeToSymbol node to the graph and -verifying the result in GPU-1 -*/ -TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalMemoryPeerDevice") { - int numDevices = 0; - int canAccessPeer = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - hipDeviceCanAccessPeer(&canAccessPeer, 0, 1); - if (canAccessPeer) { - hipGraphAddMemcpyNodeToSymbol_GlobalMemory(true, false); - } else { - SUCCEED("Machine does not seem to have P2P"); - } - } else { - SUCCEED("skipped the testcase as no of devices is less than 2"); - } -} -/* -This testcase verifies allocating global const symbol memory and device variables -in GPU-0 and add the MemcpyNodeToSymbol node to the graph and -verifying the result in GPU-1 -*/ -TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalConstMemoryPeerDevice") { - int numDevices = 0; - int canAccessPeer = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - hipDeviceCanAccessPeer(&canAccessPeer, 0, 1); - if (canAccessPeer) { - hipGraphAddMemcpyNodeToSymbol_GlobalMemory(true, true); - } else { - SUCCEED("Machine does not seem to have P2P"); - } - } else { - SUCCEED("skipped the testcase as no of devices is less than 2"); - } -} -#endif -/* -This testcaser verifies allocating global memory, -Add MemcpyToSymbolNode,KernelNode and memcpynode and validating -the behaviour -*/ -TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_MemcpyToSymbolNodeWithKernel") { - constexpr size_t Nbytes = SIZE * sizeof(int); - constexpr auto blocksPerCU = 6; // to hide latency - constexpr auto threadsPerBlock = 256; - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); - hipGraphNode_t memcpytosymbolkernel, memcpyD2H_B; - hipKernelNodeParams kernelNodeParams{}; - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - // Adding Kernel node - void* kernelArgs1[] = {&B_d}; - kernelNodeParams.func = - reinterpret_cast(MemcpyToSymbolKernel); - kernelNodeParams.gridDim = dim3(blocks); - kernelNodeParams.blockDim = dim3(threadsPerBlock); - kernelNodeParams.sharedMemBytes = 0; - kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); - kernelNodeParams.extra = nullptr; - HIP_CHECK(hipGraphAddKernelNode(&memcpytosymbolkernel, graph, - dependencies.data(), dependencies.size(), - &kernelNodeParams)); - dependencies.clear(); - dependencies.push_back(memcpytosymbolkernel); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), - dependencies.size(), B_h, B_d, - Nbytes, hipMemcpyDeviceToHost)); - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); - - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - - HipTest::freeArrays(A_d, B_d, nullptr, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); HIP_CHECK(hipGraphDestroy(graph)); } diff --git a/catch/unit/graph/hipGraphAddMemcpyNodeToSymbol_old.cc b/catch/unit/graph/hipGraphAddMemcpyNodeToSymbol_old.cc new file mode 100644 index 0000000000..a4d0bede3e --- /dev/null +++ b/catch/unit/graph/hipGraphAddMemcpyNodeToSymbol_old.cc @@ -0,0 +1,402 @@ +/* +Copyright (c) 2021 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. +*/ + +/** +Testcase Scenarios of hipGraphAddMemcpyNodeToSymbol API: + +Functional : + +1. Allocate global symbol memory, add the MemcpyNodeToSymbol + node to the graph and verify for different memory kinds +2. Allocate const memory add the MemcpyNodeToSymbol node to + the graph and verify for different memory kinds +3. Allocate global symbol memory and device memory in GPU-0 + and perform MemcpyToSymbol from peer GPU by adding it to the graph node. +4. Allocate const symbol memory and device memory in GPU-0 + and perform MemcpyToSymbol from peer GPU by adding it to the graph node. +5. Allocate global memory, Add MemcpyToSymbolNode,KernelNode and memcpynode and validating + the behaviour + +Negative : + +1) Pass nullptr to graph node +2) Pass nullptr to graph +3) Pass nullptr to dependencies +4) Pass invalid numDependencies +5) Pass nullptr to dst +6) Pass nullptr to symbol +7) Pass invalid count +8) Pass offset+count greater than allocated size +9) Pass unintialized graph +*/ + +#include +#include +#include +#define SIZE 256 + +__device__ int globalIn[SIZE]; +__device__ __constant__ int globalConst[SIZE]; + +__global__ void MemcpyToSymbolKernel(int* B_d) { + for (int i = 0 ; i < SIZE; i++) { + B_d[i] = globalIn[i]; + } +} + +/* This testcase verifies negative scenarios of + hipGraphAddMemcpyNodeToSymbol API */ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_Negative") { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, nullptr, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphNode_t memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); +#if HT_NVIDIA + hipGraphNode_t memcpyToSymbolNode; + SECTION("Passing nullptr to graph") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, nullptr, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_h, Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing nullptr to graph node") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(nullptr, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing size > 1 and dependencies as nullptr") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + nullptr, + 1, + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing invalid dependencies size") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + 10, + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing nullptr to dst") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + nullptr, + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidSymbol); + } + + SECTION("Passing nullptr to source") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + nullptr, Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing offset+size > max size") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 10, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Passing Max count") { + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, + std::numeric_limits::max(), 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } + + SECTION("Pass Unintialized graph") { + hipGraph_t unint_graph; + REQUIRE(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, unint_graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, + Nbytes, 0, + hipMemcpyDeviceToDevice) + == hipErrorInvalidValue); + } +#endif + + HipTest::freeArrays(A_d, nullptr, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphDestroy(graph)); +} +/* +This function is used to verify the following scenarios +1. Create global variable, allocate Memory in GPU-0 and create dependency graph of + hipGraphAddMemcpyNodeToSymbol API in GPU-1 and validate the result +2. Allocate global memory, Create dependency graph and validate the result on GPU-0 +3. Allocate global const memory, Create dependency graph and validate the result on GPU-0 +4. Create global const variable, allocate Memory in GPU-0 and create dependency graph of + hipGraphAddMemcpyNodeToSymbol API in GPU-1 and validate the result +*/ +void hipGraphAddMemcpyNodeToSymbol_GlobalMemory(bool device_ctxchg = false, + bool const_device_var = false) { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, nullptr, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + if (device_ctxchg) { + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipDeviceEnablePeerAccess(0, 0)); + } + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + // Adding MemcpyNodeToSymbol + + if (const_device_var) { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalConst), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + // Adding MemcpyNodeFromSymbol + if (const_device_var) { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_h, + HIP_SYMBOL(globalConst), + Nbytes, 0, hipMemcpyDeviceToHost)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_h, + HIP_SYMBOL(globalIn), + Nbytes, 0, hipMemcpyDeviceToHost)); + } + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + HIP_CHECK(hipStreamSynchronize(0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, nullptr, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} +/* +This testcase verifies allocating global symbol memory, +add the MemcpyNodeToSymbol node to the graph and +erifying the result +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalMemory") { + hipGraphAddMemcpyNodeToSymbol_GlobalMemory(false, false); +} + +/* +This testcase verifies allocating global const symbol memory, +add the MemcpyNodeToSymbol node to the graph and +verifying the result +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalConstMemory") { + hipGraphAddMemcpyNodeToSymbol_GlobalMemory(false, true); +} + +#if HT_NVIDIA +/* +This testcase verifies allocating global symbol memory and device variables +in GPU-0 and add the MemcpyNodeToSymbol node to the graph and +verifying the result in GPU-1 +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalMemoryPeerDevice") { + int numDevices = 0; + int canAccessPeer = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + hipDeviceCanAccessPeer(&canAccessPeer, 0, 1); + if (canAccessPeer) { + hipGraphAddMemcpyNodeToSymbol_GlobalMemory(true, false); + } else { + SUCCEED("Machine does not seem to have P2P"); + } + } else { + SUCCEED("skipped the testcase as no of devices is less than 2"); + } +} +/* +This testcase verifies allocating global const symbol memory and device variables +in GPU-0 and add the MemcpyNodeToSymbol node to the graph and +verifying the result in GPU-1 +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_GlobalConstMemoryPeerDevice") { + int numDevices = 0; + int canAccessPeer = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + hipDeviceCanAccessPeer(&canAccessPeer, 0, 1); + if (canAccessPeer) { + hipGraphAddMemcpyNodeToSymbol_GlobalMemory(true, true); + } else { + SUCCEED("Machine does not seem to have P2P"); + } + } else { + SUCCEED("skipped the testcase as no of devices is less than 2"); + } +} +#endif +/* +This testcaser verifies allocating global memory, +Add MemcpyToSymbolNode,KernelNode and memcpynode and validating +the behaviour +*/ +TEST_CASE("Unit_hipGraphAddMemcpyNodeToSymbol_MemcpyToSymbolNodeWithKernel") { + constexpr size_t Nbytes = SIZE * sizeof(int); + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); + hipGraphNode_t memcpytosymbolkernel, memcpyD2H_B; + hipKernelNodeParams kernelNodeParams{}; + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + // Adding Kernel node + void* kernelArgs1[] = {&B_d}; + kernelNodeParams.func = + reinterpret_cast(MemcpyToSymbolKernel); + kernelNodeParams.gridDim = dim3(blocks); + kernelNodeParams.blockDim = dim3(threadsPerBlock); + kernelNodeParams.sharedMemBytes = 0; + kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); + kernelNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&memcpytosymbolkernel, graph, + dependencies.data(), dependencies.size(), + &kernelNodeParams)); + dependencies.clear(); + dependencies.push_back(memcpytosymbolkernel); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), + dependencies.size(), B_h, B_d, + Nbytes, hipMemcpyDeviceToHost)); + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + HIP_CHECK(hipStreamSynchronize(0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, B_d, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} diff --git a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc index b2a92f94db..03fa33636c 100644 --- a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc +++ b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc @@ -6,298 +6,197 @@ 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Testcase Scenarios of hipGraphExecMemcpyNodeSetParamsFromSymbol API: -Functional -1) Allocate global symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. -2) Allocate const symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. -Negative -1) Pass hGraphExec as nullptr and check if api returns error. -2) Pass GraphNode as nullptr and check if api returns error. -3) Pass destination ptr as nullptr, api expected to return error code. -4) Pass symbol ptr as nullptr, api expected to return error code. -5) Pass count as zero, api expected to return error code. -6) Pass offset+count greater than allocated size, api expected to return error code. -7) Pass same symbol pointer as source ptr and destination ptr, api expected to return error code. -8) Pass Pass both dstn ptr and source ptr as 2 different symbol ptr, api expected to return error code. -9) Copy from device ptr to host ptr but pass kind as different, api expected to return error code. -10) Check with other graph node but pass same graphExec, api expected to return error code. -*/ +#include +#include #include #include -#include -#define SIZE 256 +#include -__device__ int globalIn[SIZE]; -__device__ int globalOut[SIZE]; -__device__ __constant__ int globalConst[SIZE]; +#include "graph_memcpy_to_from_symbol_common.hh" +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(double) -/* Test verifies hipGraphExecMemcpyNodeSetParamsFromSymbol API Negative scenarios. - */ -TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Negative") { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, nullptr, - &A_h, &B_h, nullptr, SIZE, false); +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(double) - hipError_t ret; - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); +template +void GraphExecMemcpyFromSymbolSetParamsShell(const void* symbol, const void* alt_symbol, + size_t offset, const std::vector expected) { + const auto f = [alt_symbol, is_arr = expected.size() > 1](void* dst, const void* symbol, + size_t count, size_t offset, + hipMemcpyKind direction) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); + hipGraphNode_t node = nullptr; - // Adding MemcpyNodeToSymbol - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol( + &node, graph, nullptr, 0, reinterpret_cast(dst) + is_arr, alt_symbol, + count - is_arr * sizeof(T), offset + is_arr * sizeof(T), direction)); - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_h, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToHost)); - HIP_CHECK(hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - // Instantiate the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); - SECTION("Pass hGraphExec as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(nullptr, - memcpyFromSymbolNode, B_d, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass GraphNode as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - nullptr, B_d, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass destination ptr as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, nullptr, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass symbol ptr as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, B_d, - nullptr, - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidSymbol == ret); - } - SECTION("Pass count as zero") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, B_d, - HIP_SYMBOL(globalConst), - 0, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass offset+count greater than allocated size") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, B_d, - HIP_SYMBOL(globalConst), - Nbytes, 10, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass same symbol pointer as source and destination ptr") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, - HIP_SYMBOL(globalIn), - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass both dstn ptr and source ptr as 2 different symbol ptr") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, - HIP_SYMBOL(globalIn), - HIP_SYMBOL(globalOut), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Copy from device ptr to host ptr but pass kind as different") { - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, - B_h, - HIP_SYMBOL(globalOut), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipSuccess != ret); - } - SECTION("Check with other graph node") { - hipGraphNode_t memcpyFromSymbolNode1{}; - ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode1, - B_d, - HIP_SYMBOL(globalOut), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - HipTest::freeArrays(A_d, B_d, nullptr, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipGraphExecMemcpyNodeSetParamsFromSymbol(graph_exec, node, dst, symbol, count, + offset, direction)); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); + + return hipSuccess; + }; + + MemcpyFromSymbolShell(f, symbol, offset, std::move(expected)); } -static -void hipGraphExecMemcpyNodeSetParamsFromSymbol_GlobalMem(bool useConstVar) { - constexpr size_t Nbytes = SIZE * sizeof(int); - hipGraphNode_t memcpyD2H_B; - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, nullptr, SIZE, false); +/** + * @addtogroup hipGraphExecMemcpyNodeSetParamsFromSymbol hipGraphExecMemcpyNodeSetParamsFromSymbol + * @{ + * @ingroup GraphTest + * `hipGraphExecMemcpyNodeSetParamsFromSymbol(hipGraphExec_t hGraphExec, hipGraphNode_t node, void + * *dst, const void *symbol, size_t count, size_t offset, hipMemcpyKind kind)` - + * Sets the parameters for a memcpy node in the given graphExec to copy from a symbol on the + */ - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; - std::vector dependencies; +/** + * Test Description + * ------------------------ + * - Verify that data is correctly copied from a symbol after node parameters are set following + * node addition. A graph is constructed to which a MemcpyFromSymbol node is added with valid but + * incorrect parameters. After the graph is instantiated the parameters are updated to correct + * values and the graph executed. Values in destination memory are compared against values known to + * be in symbol memory. + * The test is run for scalar, const scalar, array, and const array symbols of types char, int, + * float and double. For array symbols, the test is repeated for zero and non-zero offset values. + * Verification is performed for destination memory allocated on host and device. + * Test source + * ------------------------ + * - unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Positive_Basic") { + SECTION("char") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyFromSymbolSetParamsShell, 1, + char); + } + + SECTION("int") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyFromSymbolSetParamsShell, 1, + int); + } + + SECTION("float") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyFromSymbolSetParamsShell, 1, + float); + } + + SECTION("double") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyFromSymbolSetParamsShell, 1, + double); + } +} + +/** + * Test Description + * ------------------------ + * - Verify API behavior with invalid arguments: + * -# gGraphExec is nullptr + * -# node is nullptr + * -# dst is nullptr + * -# symbol is nullptr + * -# count is zero + * -# count is larger than symbol size + * -# count + offset is larger than symbol size + * -# kind is illogical (hipMemcpyHostToDevice) + * -# kind is an invalid enum value + * -# Changing memcpy direction + * -# Changing dst to memory allocated on a different device than the original dst + * Test source + * ------------------------ + * - unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; HIP_CHECK(hipGraphCreate(&graph, 0)); - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); + LinearAllocGuard var(LinearAllocs::hipMalloc, sizeof(int)); + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&node, graph, nullptr, 0, var.ptr(), + SYMBOL(int_device_var), sizeof(*var.ptr()), 0, + hipMemcpyDefault)); - if (useConstVar) { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalConst), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); - if (useConstVar) { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - C_d, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - C_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyFromSymbolNode); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), - dependencies.size(), B_h, B_d, - Nbytes, hipMemcpyDeviceToHost)); - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - - // Update the node with B_d destination pointer from C_d - if (useConstVar) { - HIP_CHECK(hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, - B_d, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, - memcpyFromSymbolNode, - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice)); + SECTION("hGraphExec == nullptr") { + HIP_CHECK_ERROR( + hipGraphExecMemcpyNodeSetParamsFromSymbol(nullptr, node, var.ptr(), SYMBOL(int_device_var), + sizeof(*var.ptr()), 0, hipMemcpyDefault), + hipErrorInvalidValue); } - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); + SECTION("node == nullptr") { + HIP_CHECK_ERROR(hipGraphExecMemcpyNodeSetParamsFromSymbol( + graph_exec, nullptr, var.ptr(), SYMBOL(int_device_var), sizeof(*var.ptr()), + 0, hipMemcpyDefault), + hipErrorInvalidValue); + } - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); + MemcpyFromSymbolCommonNegative( + std::bind(hipGraphExecMemcpyNodeSetParamsFromSymbol, graph_exec, node, _1, _2, _3, _4, _5), + var.ptr(), SYMBOL(int_device_var), sizeof(*var.ptr())); + +// Disabled on AMD due to defect +#if HT_NVIDIA + SECTION("Changing memcpy direction") { + HIP_CHECK_ERROR(hipGraphExecMemcpyNodeSetParamsFromSymbol( + graph_exec, node, var.ptr(), SYMBOL(int_device_var), sizeof(*var.ptr()), 0, + hipMemcpyDeviceToHost), + hipErrorInvalidValue); + } +#endif + + SECTION("Changing dst allocation device") { + if (HipTest::getDeviceCount() < 2) { + HipTest::HIP_SKIP_TEST("Test requires two connected GPUs"); + return; } + 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)), + hipErrorInvalidValue); } - HipTest::freeArrays(A_d, B_d, C_d, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphExecDestroy(graph_exec)); HIP_CHECK(hipGraphDestroy(graph)); } - -/* Test verifies hipGraphExecMemcpyNodeSetParamsFromSymbol Functional scenario. -1) Allocate global symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. -2) Allocate const symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. - */ -TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Functional") { - SECTION("Check and update with Global Device Symbol Memory") { - hipGraphExecMemcpyNodeSetParamsFromSymbol_GlobalMem(false); - } - SECTION("Check and update with Constant Global Device Symbol Memory") { - hipGraphExecMemcpyNodeSetParamsFromSymbol_GlobalMem(true); - } -} diff --git a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol_old.cc b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol_old.cc new file mode 100644 index 0000000000..b2a92f94db --- /dev/null +++ b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsFromSymbol_old.cc @@ -0,0 +1,303 @@ +/* +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. +*/ + +/** +Testcase Scenarios of hipGraphExecMemcpyNodeSetParamsFromSymbol API: +Functional +1) Allocate global symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. +2) Allocate const symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. +Negative +1) Pass hGraphExec as nullptr and check if api returns error. +2) Pass GraphNode as nullptr and check if api returns error. +3) Pass destination ptr as nullptr, api expected to return error code. +4) Pass symbol ptr as nullptr, api expected to return error code. +5) Pass count as zero, api expected to return error code. +6) Pass offset+count greater than allocated size, api expected to return error code. +7) Pass same symbol pointer as source ptr and destination ptr, api expected to return error code. +8) Pass Pass both dstn ptr and source ptr as 2 different symbol ptr, api expected to return error code. +9) Copy from device ptr to host ptr but pass kind as different, api expected to return error code. +10) Check with other graph node but pass same graphExec, api expected to return error code. +*/ + +#include +#include +#include +#define SIZE 256 + +__device__ int globalIn[SIZE]; +__device__ int globalOut[SIZE]; +__device__ __constant__ int globalConst[SIZE]; + + +/* Test verifies hipGraphExecMemcpyNodeSetParamsFromSymbol API Negative scenarios. + */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Negative") { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipError_t ret; + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + // Adding MemcpyNodeToSymbol + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_h, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToHost)); + HIP_CHECK(hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + // Instantiate the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + SECTION("Pass hGraphExec as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(nullptr, + memcpyFromSymbolNode, B_d, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass GraphNode as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + nullptr, B_d, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass destination ptr as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, nullptr, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass symbol ptr as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, B_d, + nullptr, + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidSymbol == ret); + } + SECTION("Pass count as zero") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, B_d, + HIP_SYMBOL(globalConst), + 0, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass offset+count greater than allocated size") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, B_d, + HIP_SYMBOL(globalConst), + Nbytes, 10, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass same symbol pointer as source and destination ptr") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, + HIP_SYMBOL(globalIn), + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass both dstn ptr and source ptr as 2 different symbol ptr") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, + HIP_SYMBOL(globalIn), + HIP_SYMBOL(globalOut), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Copy from device ptr to host ptr but pass kind as different") { + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, + B_h, + HIP_SYMBOL(globalOut), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipSuccess != ret); + } + SECTION("Check with other graph node") { + hipGraphNode_t memcpyFromSymbolNode1{}; + ret = hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode1, + B_d, + HIP_SYMBOL(globalOut), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + HipTest::freeArrays(A_d, B_d, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphDestroy(graph)); +} + +static +void hipGraphExecMemcpyNodeSetParamsFromSymbol_GlobalMem(bool useConstVar) { + constexpr size_t Nbytes = SIZE * sizeof(int); + hipGraphNode_t memcpyD2H_B; + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + if (useConstVar) { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalConst), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + if (useConstVar) { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + C_d, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + C_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyFromSymbolNode); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), + dependencies.size(), B_h, B_d, + Nbytes, hipMemcpyDeviceToHost)); + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + // Update the node with B_d destination pointer from C_d + if (useConstVar) { + HIP_CHECK(hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, + B_d, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphExecMemcpyNodeSetParamsFromSymbol(graphExec, + memcpyFromSymbolNode, + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + HIP_CHECK(hipStreamSynchronize(0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, B_d, C_d, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/* Test verifies hipGraphExecMemcpyNodeSetParamsFromSymbol Functional scenario. +1) Allocate global symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. +2) Allocate const symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. + */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Functional") { + SECTION("Check and update with Global Device Symbol Memory") { + hipGraphExecMemcpyNodeSetParamsFromSymbol_GlobalMem(false); + } + SECTION("Check and update with Constant Global Device Symbol Memory") { + hipGraphExecMemcpyNodeSetParamsFromSymbol_GlobalMem(true); + } +} diff --git a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc index 4f32551228..daa0c09b1b 100644 --- a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc +++ b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc @@ -6,312 +6,192 @@ 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Testcase Scenarios of hipGraphExecMemcpyNodeSetParamsToSymbol API: -Functional : -1) Allocate global symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. -2) Allocate const symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. -Negative : -1) Pass hGraphExec as nullptr and check if api returns error. -2) Pass GraphNode as nullptr and check if api returns error. -3) Pass symbol ptr as nullptr, api expected to return error code. -4) Pass source ptr as nullptr, api expected to return error code. -5) Pass count as zero, api expected to return error code. -6) Pass offset+count greater than allocated size, api expected to return error code. -7) Pass same symbol pointer as source ptr and destination ptr, api expected to return error code. -8) Pass Pass both dstn ptr and source ptr as 2 different symbol ptr, api expected to return error code. -9) Copy from device ptr to host ptr but pass kind as different, api expected to return error code. -10) Check with other graph node but pass same graphExec, api expected to return error code. -*/ +#include +#include +#include #include #include -#include -#define SIZE 256 -__device__ int globalIn[SIZE], globalOut[SIZE]; -__device__ __constant__ int globalConst[SIZE]; +#include "graph_memcpy_to_from_symbol_common.hh" -__global__ void MemcpyToSymbolExecKernel(int* B_d) { - for (int i = 0 ; i < SIZE; i++) { - B_d[i] = globalIn[i]; - } +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(double) + +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(double) + +template +void GraphExecMemcpyToSymbolSetParamsShell(const void* symbol, const void* alt_symbol, + size_t offset, const std::vector set_values) { + const auto f = [alt_symbol, is_arr = set_values.size() > 1](const void* symbol, void* src, + size_t count, size_t offset, + hipMemcpyKind direction) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol( + &node, graph, nullptr, 0, alt_symbol, reinterpret_cast(src) + is_arr, + count - is_arr * sizeof(T), offset + is_arr * sizeof(T), direction)); + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphExecMemcpyNodeSetParamsToSymbol(graph_exec, node, symbol, src, count, offset, + direction)); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); + + return hipSuccess; + }; + + MemcpyToSymbolShell(f, symbol, offset, std::move(set_values)); } -__global__ void MemcpyToConstSymbolExecKernel(int* B_d) { - for (int i = 0 ; i < SIZE; i++) { - B_d[i] = globalConst[i]; - } -} - -/* This testcase verifies negative scenarios of - hipGraphExecMemcpyNodeSetParamsToSymbol API */ -TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Negative") { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipError_t ret; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyH2D; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D); - - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - C_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - HIP_CHECK(hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), A_d, - Nbytes, 0, - hipMemcpyDeviceToDevice)); - - // Instantiate the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - - SECTION("Pass hGraphExec as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(nullptr, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - B_d, Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass GraphNode as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - nullptr, - HIP_SYMBOL(globalIn), - B_d, Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass symbol ptr as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - nullptr, - B_d, Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidSymbol == ret); - } - SECTION("Pass source ptr as nullptr") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - nullptr, Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass count as zero") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - B_d, 0, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass offset+count greater than allocated size") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - B_d, Nbytes, 10, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass same symbol pointer as source ptr and destination ptr") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass both dstn ptr and source ptr as 2 different symbol ptr") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - HIP_SYMBOL(globalOut), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Copy from device ptr to host ptr but pass kind as different") { - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - B_h, - Nbytes, 0, - hipMemcpyDeviceToHost); - REQUIRE(hipSuccess != ret); - } - SECTION("Check with other graph node but pass same graphExec") { - hipGraph_t graph1; - hipGraphNode_t memcpyToSymbolNode1{}; - HIP_CHECK(hipGraphCreate(&graph1, 0)); - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode1, graph1, - nullptr, - 0, - HIP_SYMBOL(globalOut), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode1, - HIP_SYMBOL(globalOut), - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - - HipTest::freeArrays(A_d, B_d, C_d, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -static -void hipGraphExecMemcpyNodeSetParamsToSymbol_GlobalMem(bool useConstVar) { - constexpr size_t Nbytes = SIZE * sizeof(int); - constexpr auto blocksPerCU = 6; // to hide latency - constexpr auto threadsPerBlock = 256; - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); - hipGraphNode_t memcpytosymbolkernel, memcpyD2H_B; - hipKernelNodeParams kernelNodeParams{}; - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - if (useConstVar) { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalConst), - C_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - C_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - // Adding Kernel node - void* kernelArgs1[] = {&B_d}; - if (useConstVar) { - kernelNodeParams.func = - reinterpret_cast(MemcpyToConstSymbolExecKernel); - } else { - kernelNodeParams.func = reinterpret_cast(MemcpyToSymbolExecKernel); - } - kernelNodeParams.gridDim = dim3(blocks); - kernelNodeParams.blockDim = dim3(threadsPerBlock); - kernelNodeParams.sharedMemBytes = 0; - kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); - kernelNodeParams.extra = nullptr; - HIP_CHECK(hipGraphAddKernelNode(&memcpytosymbolkernel, graph, - dependencies.data(), dependencies.size(), - &kernelNodeParams)); - dependencies.clear(); - dependencies.push_back(memcpytosymbolkernel); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), - dependencies.size(), B_h, B_d, - Nbytes, hipMemcpyDeviceToHost)); - - // Instantiate the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - - // Update the node with source pointer from C_d to A_d - if (useConstVar) { - HIP_CHECK(hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalConst), A_d, - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, - memcpyToSymbolNode, - HIP_SYMBOL(globalIn), A_d, - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); - - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - - HipTest::freeArrays(A_d, B_d, C_d, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -/* Test verifies hipGraphExecMemcpyNodeSetParamsToSymbol Functional scenario. -1) Allocate global symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. -2) Allocate const symbol memory, Instantiate a graph with memcpy node, - obtain executable graph and update the node params with set exec api call. - Make sure they are taking effect. +/** + * @addtogroup hipGraphExecMemcpyNodeSetParamsToSymbol hipGraphExecMemcpyNodeSetParamsToSymbol + * @{ + * @ingroup GraphTest + * `hipGraphExecMemcpyNodeSetParamsToSymbol(hipGraphExec_t hGraphExec, hipGraphNode_t node, + * const void *symbol, void *src, size_t count, size_t offset, hipMemcpyKind kind)` - + * Sets the parameters for a memcpy node in the given graphExec to copy to a symbol on the device */ -TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Functional") { - SECTION("Check and update with Global Device Symbol Memory") { - hipGraphExecMemcpyNodeSetParamsToSymbol_GlobalMem(false); + +/** + * Test Description + * ------------------------ + * - Verify that data is correctly copied to a symbol after node parameters are set following + * node addition. A graph is constructed to which a MemcpyToSymbol node is added with valid but + * incorrect parameters. After the graph is instantiated the parameters are updated to correct + * values and the graph executed. After graph execution, a MemcpyFromSymbol is performed and the + * copied values are compared against values known to have been copied to symbol memory previously. + * The test is run for scalar, const scalar, array, and const array symbols of types char, int, + * float and double. For array symbols, the test is repeated for zero and non-zero offset values. + * Verification is performed for destination memory allocated on host and device. + * Test source + * ------------------------ + * - unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Positive_Basic") { + SECTION("char") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyToSymbolSetParamsShell, 10, + char); } - SECTION("Check and update with Constant Global Device Symbol Memory") { - hipGraphExecMemcpyNodeSetParamsToSymbol_GlobalMem(true); + + SECTION("int") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyToSymbolSetParamsShell, 10, + int); + } + + SECTION("float") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyToSymbolSetParamsShell, 10, + float); + } + + SECTION("double") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphExecMemcpyToSymbolSetParamsShell, 10, + double); } } +/** + * Test Description + * ------------------------ + * - Verify API behavior with invalid arguments: + * -# gGraphExec is nullptr + * -# node is nullptr + * -# src is nullptr + * -# symbol is nullptr + * -# count is zero + * -# count is larger than symbol size + * -# count + offset is larger than symbol size + * -# kind is illogical (hipMemcpyDeviceToHost) + * -# kind is an invalid enum value + * -# Changing memcpy direction + * -# Changing src to memory allocated on a different device than the original src + * Test source + * ------------------------ + * - unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + LinearAllocGuard var(LinearAllocs::hipMalloc, sizeof(int)); + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&node, graph, nullptr, 0, SYMBOL(int_device_var), + var.ptr(), sizeof(*var.ptr()), 0, hipMemcpyDefault)); + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + SECTION("hGraphExec == nullptr") { + HIP_CHECK_ERROR( + hipGraphExecMemcpyNodeSetParamsToSymbol(nullptr, node, SYMBOL(int_device_var), var.ptr(), + sizeof(*var.ptr()), 0, hipMemcpyDefault), + hipErrorInvalidValue); + } + + SECTION("node == nullptr") { + HIP_CHECK_ERROR( + hipGraphExecMemcpyNodeSetParamsToSymbol(graph_exec, nullptr, SYMBOL(int_device_var), + var.ptr(), sizeof(*var.ptr()), 0, hipMemcpyDefault), + hipErrorInvalidValue); + } + + MemcpyToSymbolCommonNegative( + std::bind(hipGraphExecMemcpyNodeSetParamsToSymbol, graph_exec, node, _1, _2, _3, _4, _5), + SYMBOL(int_device_var), var.ptr(), sizeof(*var.ptr())); + + SECTION("Changing memcpy direction") { + HIP_CHECK_ERROR( + hipGraphExecMemcpyNodeSetParamsToSymbol(graph_exec, node, SYMBOL(int_device_var), var.ptr(), + sizeof(*var.ptr()), 0, hipMemcpyHostToDevice), + hipErrorInvalidValue); + } + + SECTION("Changing src allocation device") { + if (HipTest::getDeviceCount() < 2) { + HipTest::HIP_SKIP_TEST("Test requires two connected GPUs"); + return; + } + HIP_CHECK(hipSetDevice(1)); + LinearAllocGuard new_var(LinearAllocs::hipMalloc, sizeof(int)); + HIP_CHECK_ERROR(hipGraphExecMemcpyNodeSetParamsFromSymbol( + graph_exec, node, SYMBOL(int_device_var), new_var.ptr(), + sizeof(*new_var.ptr()), 0, static_cast(-1)), + hipErrorInvalidValue); + } + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); +} diff --git a/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol_old.cc b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol_old.cc new file mode 100644 index 0000000000..e83bfe9cea --- /dev/null +++ b/catch/unit/graph/hipGraphExecMemcpyNodeSetParamsToSymbol_old.cc @@ -0,0 +1,316 @@ +/* +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. +*/ + +/** +Testcase Scenarios of hipGraphExecMemcpyNodeSetParamsToSymbol API: +Functional : +1) Allocate global symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. +2) Allocate const symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. +Negative : +1) Pass hGraphExec as nullptr and check if api returns error. +2) Pass GraphNode as nullptr and check if api returns error. +3) Pass symbol ptr as nullptr, api expected to return error code. +4) Pass source ptr as nullptr, api expected to return error code. +5) Pass count as zero, api expected to return error code. +6) Pass offset+count greater than allocated size, api expected to return error code. +7) Pass same symbol pointer as source ptr and destination ptr, api expected to return error code. +8) Pass Pass both dstn ptr and source ptr as 2 different symbol ptr, api expected to return error code. +9) Copy from device ptr to host ptr but pass kind as different, api expected to return error code. +10) Check with other graph node but pass same graphExec, api expected to return error code. +*/ + +#include +#include +#include +#define SIZE 256 + +__device__ int globalIn[SIZE], globalOut[SIZE]; +__device__ __constant__ int globalConst[SIZE]; + +__global__ void MemcpyToSymbolExecKernel(int* B_d) { + for (int i = 0 ; i < SIZE; i++) { + B_d[i] = globalIn[i]; + } +} + +__global__ void MemcpyToConstSymbolExecKernel(int* B_d) { + for (int i = 0 ; i < SIZE; i++) { + B_d[i] = globalConst[i]; + } +} + +/* This testcase verifies negative scenarios of + hipGraphExecMemcpyNodeSetParamsToSymbol API */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Negative") { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipError_t ret; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyH2D; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D); + + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + C_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + HIP_CHECK(hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), A_d, + Nbytes, 0, + hipMemcpyDeviceToDevice)); + + // Instantiate the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + SECTION("Pass hGraphExec as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(nullptr, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + B_d, Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass GraphNode as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + nullptr, + HIP_SYMBOL(globalIn), + B_d, Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass symbol ptr as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + nullptr, + B_d, Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidSymbol == ret); + } + SECTION("Pass source ptr as nullptr") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + nullptr, Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass count as zero") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + B_d, 0, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass offset+count greater than allocated size") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + B_d, Nbytes, 10, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass same symbol pointer as source ptr and destination ptr") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass both dstn ptr and source ptr as 2 different symbol ptr") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + HIP_SYMBOL(globalOut), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Copy from device ptr to host ptr but pass kind as different") { + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + B_h, + Nbytes, 0, + hipMemcpyDeviceToHost); + REQUIRE(hipSuccess != ret); + } + SECTION("Check with other graph node but pass same graphExec") { + hipGraph_t graph1; + hipGraphNode_t memcpyToSymbolNode1{}; + HIP_CHECK(hipGraphCreate(&graph1, 0)); + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode1, graph1, + nullptr, + 0, + HIP_SYMBOL(globalOut), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + ret = hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode1, + HIP_SYMBOL(globalOut), + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + + HipTest::freeArrays(A_d, B_d, C_d, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +static +void hipGraphExecMemcpyNodeSetParamsToSymbol_GlobalMem(bool useConstVar) { + constexpr size_t Nbytes = SIZE * sizeof(int); + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); + hipGraphNode_t memcpytosymbolkernel, memcpyD2H_B; + hipKernelNodeParams kernelNodeParams{}; + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + if (useConstVar) { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalConst), + C_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + C_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + // Adding Kernel node + void* kernelArgs1[] = {&B_d}; + if (useConstVar) { + kernelNodeParams.func = + reinterpret_cast(MemcpyToConstSymbolExecKernel); + } else { + kernelNodeParams.func = reinterpret_cast(MemcpyToSymbolExecKernel); + } + kernelNodeParams.gridDim = dim3(blocks); + kernelNodeParams.blockDim = dim3(threadsPerBlock); + kernelNodeParams.sharedMemBytes = 0; + kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); + kernelNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&memcpytosymbolkernel, graph, + dependencies.data(), dependencies.size(), + &kernelNodeParams)); + dependencies.clear(); + dependencies.push_back(memcpytosymbolkernel); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), + dependencies.size(), B_h, B_d, + Nbytes, hipMemcpyDeviceToHost)); + + // Instantiate the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + // Update the node with source pointer from C_d to A_d + if (useConstVar) { + HIP_CHECK(hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalConst), A_d, + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphExecMemcpyNodeSetParamsToSymbol(graphExec, + memcpyToSymbolNode, + HIP_SYMBOL(globalIn), A_d, + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, B_d, C_d, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/* Test verifies hipGraphExecMemcpyNodeSetParamsToSymbol Functional scenario. +1) Allocate global symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. +2) Allocate const symbol memory, Instantiate a graph with memcpy node, + obtain executable graph and update the node params with set exec api call. + Make sure they are taking effect. + */ +TEST_CASE("Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Functional") { + SECTION("Check and update with Global Device Symbol Memory") { + hipGraphExecMemcpyNodeSetParamsToSymbol_GlobalMem(false); + } + SECTION("Check and update with Constant Global Device Symbol Memory") { + hipGraphExecMemcpyNodeSetParamsToSymbol_GlobalMem(true); + } +} + diff --git a/catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol.cc b/catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol.cc index 1a6da7ff19..7f1ac7fe3c 100644 --- a/catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol.cc +++ b/catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol.cc @@ -6,255 +6,156 @@ 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Testcase Scenarios of hipGraphMemcpyNodeSetParamsFromSymbol API: - -Functional : -1) Allocate global symbol memory, add node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. -2) Allocate const symbol memory, add node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. - -Negative : -1) Pass GraphNode as nullptr and check if api returns error. -2) Pass destination ptr as nullptr, api expected to return error code. -3) Pass source/symbol ptr as nullptr, api expected to return error code. -4) Pass count as zero, api expected to return error code. -5) Pass count more than allocated size for source and destination ptr, api should return error code. -6) Pass offset+count greater than allocated size, api expected to return error code. -7) Pass same symbol pointer as destination ptr and source ptr, api expected to return error code. -8) Pass both destination ptr and source ptr as 2 different symbol ptr, api expected to return error code. -*/ +#include +#include +#include #include #include -#include -#define SIZE 256 -__device__ int globalIn[SIZE]; -__device__ int globalOut[SIZE]; -__device__ __constant__ int globalConst[SIZE]; +#include "graph_memcpy_to_from_symbol_common.hh" +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(double) -/* Test verifies hipGraphMemcpyNodeSetParamsFromSymbol API Negative scenarios. +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(double) + +template +void GraphMemcpyFromSymbolSetParamsShell(const void* symbol, const void* alt_symbol, size_t offset, + const std::vector expected) { + const auto f = [alt_symbol, is_arr = expected.size() > 1](void* dst, const void* symbol, + size_t count, size_t offset, + hipMemcpyKind direction) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipGraphNode_t node = nullptr; + + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol( + &node, graph, nullptr, 0, reinterpret_cast(dst) + is_arr, alt_symbol, + count - is_arr * sizeof(T), offset + is_arr * sizeof(T), hipMemcpyDefault)); + + HIP_CHECK(hipGraphMemcpyNodeSetParamsFromSymbol(node, dst, symbol, count, offset, direction)); + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); + + return hipSuccess; + }; + + MemcpyFromSymbolShell(f, symbol, offset, std::move(expected)); +} + +/** + * @addtogroup hipGraphMemcpyNodeSetParamsFromSymbol hipGraphMemcpyNodeSetParamsFromSymbol + * @{ + * @ingroup GraphTest + * `hipGraphMemcpyNodeSetParamsFromSymbol(hipGraphNode_t node, void *dst, const void *symbol, size_t + * count, size_t offset, hipMemcpyKind kind)` - + * Sets a memcpy node's parameters to copy from a symbol on the device */ -TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Negative") { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - hipError_t ret; - hipGraph_t graph; - hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; - std::vector dependencies; +/** + * Test Description + * ------------------------ + * - Verify that data is correctly copied from a symbol after node parameters are set following + * node addition. A graph is constructed to which a MemcpyFromSymbol node is added with valid but + * incorrect parameters. The parameters are then updated to correct values and the graph executed. + * Values in destination memory are compared against values known to be in symbol memory. + * The test is run for scalar, const scalar, array, and const array symbols of types char, int, + * float and double. For array symbols, the test is repeated for zero and non-zero offset values. + * Verification is performed for destination memory allocated on host and device. + * Test source + * ------------------------ + * - unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Positive_Basic") { + SECTION("char") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolSetParamsShell, 1, + char); + } + + SECTION("int") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolSetParamsShell, 1, + int); + } + + SECTION("float") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolSetParamsShell, 1, + float); + } + + SECTION("double") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyFromSymbolSetParamsShell, 1, + double); + } +} + +/** + * Test Description + * ------------------------ + * - Verify API behavior with invalid arguments: + * -# node is nullptr + * -# dst is nullptr + * -# symbol is nullptr + * -# count is zero + * -# count is larger than symbol size + * -# count + offset is larger than symbol size + * -# kind is illogical (hipMemcpyHostToDevice) + * -# kind is an invalid enum value + * Test source + * ------------------------ + * - unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; HIP_CHECK(hipGraphCreate(&graph, 0)); - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); + int var = 0; + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&node, graph, nullptr, 0, &var, SYMBOL(int_device_var), + sizeof(var), 0, hipMemcpyDefault)); - // Adding MemcpyNodeToSymbol - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); + SECTION("node == nullptr") { + HIP_CHECK_ERROR(hipGraphMemcpyNodeSetParamsFromSymbol(nullptr, &var, SYMBOL(int_device_var), + sizeof(var), 0, hipMemcpyDefault), + hipErrorInvalidValue); + } + + MemcpyFromSymbolCommonNegative( + std::bind(hipGraphMemcpyNodeSetParamsFromSymbol, node, _1, _2, _3, _4, _5), &var, + SYMBOL(int_device_var), sizeof(var)); - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - B_h, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToHost)); - SECTION("Pass GraphNode as nullptr") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(nullptr, B_h, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToHost); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass destination ptr as nullptr") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, nullptr, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToHost); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass source/symbol ptr as nullptr") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, - nullptr, - Nbytes, 0, - hipMemcpyDeviceToHost); - REQUIRE(hipErrorInvalidSymbol == ret); - } - SECTION("Pass count as zero") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, - HIP_SYMBOL(globalConst), - 0, 0, - hipMemcpyDeviceToHost); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass count more than allocated size for source and dstn ptr") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, - HIP_SYMBOL(globalConst), - Nbytes+10, 0, - hipMemcpyDeviceToHost); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass offset non zero so that offset+count > allocated size") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, - HIP_SYMBOL(globalConst), - Nbytes, 10, - hipMemcpyDeviceToHost); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass same symbol pointer as dstn ptr and source ptr") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, - HIP_SYMBOL(globalConst), - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass both dstn ptr and source ptr as 2 different symbol ptr") { - ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, - HIP_SYMBOL(globalOut), - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - HipTest::freeArrays(A_d, B_d, nullptr, - A_h, B_h, nullptr, false); HIP_CHECK(hipGraphDestroy(graph)); } - -static -void hipGraphMemcpyNodeSetParamsFromSymbol_GlobalMem(bool useConstDeviceVar) { - constexpr size_t Nbytes = SIZE * sizeof(int); - hipGraphNode_t memcpyD2H_B; - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - if (useConstDeviceVar) { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalConst), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - if (useConstDeviceVar) { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - C_d, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, - dependencies.data(), - dependencies.size(), - C_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyFromSymbolNode); - - // Update the node with B_d destination pointer from C_d - if (useConstDeviceVar) { - HIP_CHECK(hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, - B_d, - HIP_SYMBOL(globalConst), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, - B_d, - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), - dependencies.size(), B_h, B_d, - Nbytes, hipMemcpyDeviceToHost)); - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); - - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - - HipTest::freeArrays(A_d, B_d, C_d, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -/* Test verifies hipGraphMemcpyNodeSetParamsFromSymbol API Functional scenario. - 1) Allocate global symbol memory, add node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. - 2) Allocate const symbol memory, add node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. - */ -TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Functional") { - SECTION("Check and update with Global Device Symbol Memory") { - hipGraphMemcpyNodeSetParamsFromSymbol_GlobalMem(false); - } - SECTION("Check and update with Constant Global Device Symbol Memory") { - hipGraphMemcpyNodeSetParamsFromSymbol_GlobalMem(true); - } -} diff --git a/catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol_old.cc b/catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol_old.cc new file mode 100644 index 0000000000..1a6da7ff19 --- /dev/null +++ b/catch/unit/graph/hipGraphMemcpyNodeSetParamsFromSymbol_old.cc @@ -0,0 +1,260 @@ +/* +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. +*/ + +/** +Testcase Scenarios of hipGraphMemcpyNodeSetParamsFromSymbol API: + +Functional : +1) Allocate global symbol memory, add node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. +2) Allocate const symbol memory, add node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. + +Negative : +1) Pass GraphNode as nullptr and check if api returns error. +2) Pass destination ptr as nullptr, api expected to return error code. +3) Pass source/symbol ptr as nullptr, api expected to return error code. +4) Pass count as zero, api expected to return error code. +5) Pass count more than allocated size for source and destination ptr, api should return error code. +6) Pass offset+count greater than allocated size, api expected to return error code. +7) Pass same symbol pointer as destination ptr and source ptr, api expected to return error code. +8) Pass both destination ptr and source ptr as 2 different symbol ptr, api expected to return error code. +*/ + +#include +#include +#include +#define SIZE 256 + +__device__ int globalIn[SIZE]; +__device__ int globalOut[SIZE]; +__device__ __constant__ int globalConst[SIZE]; + + +/* Test verifies hipGraphMemcpyNodeSetParamsFromSymbol API Negative scenarios. + */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Negative") { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipError_t ret; + hipGraph_t graph; + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + // Adding MemcpyNodeToSymbol + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + B_h, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToHost)); + SECTION("Pass GraphNode as nullptr") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(nullptr, B_h, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToHost); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass destination ptr as nullptr") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, nullptr, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToHost); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass source/symbol ptr as nullptr") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, + nullptr, + Nbytes, 0, + hipMemcpyDeviceToHost); + REQUIRE(hipErrorInvalidSymbol == ret); + } + SECTION("Pass count as zero") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, + HIP_SYMBOL(globalConst), + 0, 0, + hipMemcpyDeviceToHost); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass count more than allocated size for source and dstn ptr") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, + HIP_SYMBOL(globalConst), + Nbytes+10, 0, + hipMemcpyDeviceToHost); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass offset non zero so that offset+count > allocated size") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, B_h, + HIP_SYMBOL(globalConst), + Nbytes, 10, + hipMemcpyDeviceToHost); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass same symbol pointer as dstn ptr and source ptr") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, + HIP_SYMBOL(globalConst), + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass both dstn ptr and source ptr as 2 different symbol ptr") { + ret = hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, + HIP_SYMBOL(globalOut), + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + HipTest::freeArrays(A_d, B_d, nullptr, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphDestroy(graph)); +} + +static +void hipGraphMemcpyNodeSetParamsFromSymbol_GlobalMem(bool useConstDeviceVar) { + constexpr size_t Nbytes = SIZE * sizeof(int); + hipGraphNode_t memcpyD2H_B; + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyFromSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + if (useConstDeviceVar) { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalConst), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + if (useConstDeviceVar) { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + C_d, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeFromSymbol(&memcpyFromSymbolNode, graph, + dependencies.data(), + dependencies.size(), + C_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyFromSymbolNode); + + // Update the node with B_d destination pointer from C_d + if (useConstDeviceVar) { + HIP_CHECK(hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, + B_d, + HIP_SYMBOL(globalConst), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphMemcpyNodeSetParamsFromSymbol(memcpyFromSymbolNode, + B_d, + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), + dependencies.size(), B_h, B_d, + Nbytes, hipMemcpyDeviceToHost)); + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + HIP_CHECK(hipStreamSynchronize(0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, B_d, C_d, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/* Test verifies hipGraphMemcpyNodeSetParamsFromSymbol API Functional scenario. + 1) Allocate global symbol memory, add node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. + 2) Allocate const symbol memory, add node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. + */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Functional") { + SECTION("Check and update with Global Device Symbol Memory") { + hipGraphMemcpyNodeSetParamsFromSymbol_GlobalMem(false); + } + SECTION("Check and update with Constant Global Device Symbol Memory") { + hipGraphMemcpyNodeSetParamsFromSymbol_GlobalMem(true); + } +} diff --git a/catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol.cc b/catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol.cc index e1441d4de8..0f84b6b283 100644 --- a/catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol.cc +++ b/catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol.cc @@ -6,260 +6,157 @@ 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Testcase Scenarios of hipGraphMemcpyNodeSetParamsToSymbol API: -Functional : -1) Allocate global symbol memory, add the node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. -2) Allocate const symbol memory, add the node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. -Negative : -1) Pass GraphNode as nullptr and check if api returns error. -2) Pass symbol ptr as nullptr, api expected to return error code. -3) Pass src ptr as nullptr, api expected to return error code. -4) Pass count as zero, api expected to return error code. -5) Pass count more than allocated size for source and destination ptr, api should return error code. -6) Pass offset+count greater than allocated size, api expected to return error code. -7) Pass same pointer as source ptr and symbol ptr, api expected to return error code. -8) Pass both destination ptr and source ptr as 2 different symbol ptr, api expected to return error code. -9) Copy from host ptr to device ptr but pass kind as different, api expected to return error code. -*/ +#include +#include +#include #include #include -#include -#define SIZE 256 -__device__ int globalIn[SIZE], globalOut[SIZE]; -__device__ __constant__ int globalConst[SIZE]; +#include "graph_memcpy_to_from_symbol_common.hh" -__global__ void CpyToSymbolKernel(int* B_d) { - for (int i = 0 ; i < SIZE; i++) { - B_d[i] = globalIn[i]; - } +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_GLOBALS(double) + +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(char) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(int) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(float) +HIP_GRAPH_MEMCPY_FROM_SYMBOL_NODE_DEFINE_ALTERNATE_GLOBALS(double) + +template +void GraphMemcpyToSymbolSetParamsShell(const void* symbol, const void* alt_symbol, size_t offset, + const std::vector set_values) { + const auto f = [alt_symbol, is_arr = set_values.size() > 1](const void* symbol, void* src, + size_t count, size_t offset, + hipMemcpyKind direction) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol( + &node, graph, nullptr, 0, alt_symbol, reinterpret_cast(src) + is_arr, + count - is_arr * sizeof(T), offset + is_arr * sizeof(T), direction)); + + HIP_CHECK(hipGraphMemcpyNodeSetParamsToSymbol(node, symbol, src, count, offset, direction)); + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); + + return hipSuccess; + }; + + MemcpyToSymbolShell(f, symbol, offset, std::move(set_values)); } -__global__ void CpyToConstSymbolKernel(int* B_d) { - for (int i = 0 ; i < SIZE; i++) { - B_d[i] = globalConst[i]; - } -} - -/* This testcase verifies negative scenarios of - hipGraphMemcpyNodeSetParamsToSymbol API */ -TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsToSymbol_Negative") { - constexpr size_t Nbytes = SIZE * sizeof(int); - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, nullptr, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipError_t ret; - hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - A_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - SECTION("Pass GraphNode as nullptr") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(nullptr, - HIP_SYMBOL(globalIn), - B_d, Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass symbol ptr as nullptr") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - nullptr, - B_d, Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidSymbol == ret); - } - SECTION("Pass src ptr as nullptr") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - nullptr, Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass count as zero") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - B_d, 0, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass count more than allocated size for source and dstn ptr") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - B_d, Nbytes+8, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass offset+count greater than allocated size") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - B_d, Nbytes, 10, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass same symbol pointer as source ptr and destination ptr") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - HIP_SYMBOL(globalIn), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass 2 different symbol pointer as source ptr and dstn ptr") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - HIP_SYMBOL(globalOut), - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Copy from host ptr to device ptr but pass kind as different") { - ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), - A_h, - Nbytes, 0, - hipMemcpyDeviceToDevice); - REQUIRE(hipErrorInvalidValue == ret); - } - - HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphDestroy(graph)); -} - -static -void hipGraphMemcpyNodeSetParamsToSymbol_GlobalMem(bool useConstDeviceVar) { - constexpr size_t Nbytes = SIZE * sizeof(int); - constexpr auto blocksPerCU = 6; // to hide latency - constexpr auto threadsPerBlock = 256; - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); - hipGraphNode_t memcpytosymbolkernel, memcpyD2H_B; - hipKernelNodeParams kernelNodeParams{}; - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, nullptr, SIZE, false); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; - std::vector dependencies; - HIP_CHECK(hipGraphCreate(&graph, 0)); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, - Nbytes, hipMemcpyHostToDevice)); - dependencies.push_back(memcpyH2D_A); - - if (useConstDeviceVar) { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalConst), - C_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, - dependencies.data(), - dependencies.size(), - HIP_SYMBOL(globalIn), - C_d, Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - dependencies.clear(); - dependencies.push_back(memcpyToSymbolNode); - - // Update the node with source pointer from C_d to A_d - if (useConstDeviceVar) { - HIP_CHECK(hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalConst), A_d, - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } else { - HIP_CHECK(hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, - HIP_SYMBOL(globalIn), A_d, - Nbytes, 0, - hipMemcpyDeviceToDevice)); - } - - // Adding Kernel node - void* kernelArgs1[] = {&B_d}; - if (useConstDeviceVar) - kernelNodeParams.func = reinterpret_cast(CpyToConstSymbolKernel); - else - kernelNodeParams.func = reinterpret_cast(CpyToSymbolKernel); - kernelNodeParams.gridDim = dim3(blocks); - kernelNodeParams.blockDim = dim3(threadsPerBlock); - kernelNodeParams.sharedMemBytes = 0; - kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); - kernelNodeParams.extra = nullptr; - HIP_CHECK(hipGraphAddKernelNode(&memcpytosymbolkernel, graph, - dependencies.data(), dependencies.size(), - &kernelNodeParams)); - dependencies.clear(); - dependencies.push_back(memcpytosymbolkernel); - - // Adding MemcpyNode - HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), - dependencies.size(), B_h, B_d, - Nbytes, hipMemcpyDeviceToHost)); - - // Instantiate and launch the graph - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, 0)); - HIP_CHECK(hipStreamSynchronize(0)); - - // Validating the result - for (int i = 0; i < SIZE; i++) { - if (B_h[i] != A_h[i]) { - WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - - HipTest::freeArrays(A_d, B_d, C_d, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -/* Test verifies hipGraphMemcpyNodeSetParamsToSymbol API Functional scenario. - 1) Allocate global symbol memory, add node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. - 2) Allocate const symbol memory, add node to the graph. - Set/Update the new values to the node. Make sure they are taking effect. +/** + * @addtogroup hipGraphMemcpyNodeSetParamsToSymbol hipGraphMemcpyNodeSetParamsToSymbol + * @{ + * @ingroup GraphTest + * `hipGraphMemcpyNodeSetParamsToSymbol(hipGraphNode_t node, const void *symbol, const void *src, + * size_t count, size_t offset, hipMemcpyKind kind)` - + * Sets a memcpy node's parameters to copy to a symbol on the device */ -TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsToSymbol_Functional") { - SECTION("Check and update with Global Device Symbol Memory") { - hipGraphMemcpyNodeSetParamsToSymbol_GlobalMem(false); + + +/** + * Test Description + * ------------------------ + * - Verify that data is correctly copied to a symbol after node parameters are set following + * node addition. A graph is constructed to which a MemcpyToSymbol node is added with valid but + * incorrect parameters. The parameters are then updated to correct values and the graph executed. + * After graph execution, a MemcpyFromSymbol is performed and the copied values are compared against + * values known to have been copied to symbol memory previously. + * The test is run for scalar, const scalar, array, and const array symbols of types char, int, + * float and double. For array symbols, the test is repeated for zero and non-zero offset values. + * Verification is performed for destination memory allocated on host and device. + * Test source + * ------------------------ + * - unit/graph/hipGraphMemcpyNodeSetParamsToSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsToSymbol_Positive_Basic") { + SECTION("char") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolSetParamsShell, 10, + char); } - SECTION("Check and update with Constant Global Device Symbol Memory") { - hipGraphMemcpyNodeSetParamsToSymbol_GlobalMem(true); + + SECTION("int") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolSetParamsShell, 10, + int); + } + + SECTION("float") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolSetParamsShell, 10, + float); + } + + SECTION("double") { + HIP_GRAPH_MEMCPY_NODE_SET_PARAMS_TO_FROM_SYMBOL_TEST(GraphMemcpyToSymbolSetParamsShell, 10, + double); } } + +/** + * Test Description + * ------------------------ + * - Verify API behavior with invalid arguments: + * -# node is nullptr + * -# src is nullptr + * -# symbol is nullptr + * -# count is zero + * -# count is larger than symbol size + * -# count + offset is larger than symbol size + * -# kind is illogical (hipMemcpyDeviceToHost) + * -# kind is an invalid enum value + * Test source + * ------------------------ + * - unit/graph/hipGraphMemcpyNodeSetParamsToSymbol.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsToSymbol_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + int var = 0; + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&node, graph, nullptr, 0, SYMBOL(int_device_var), &var, + sizeof(var), 0, hipMemcpyDefault)); + + SECTION("node == nullptr") { + HIP_CHECK_ERROR(hipGraphMemcpyNodeSetParamsToSymbol(nullptr, SYMBOL(int_device_var), &var, + sizeof(var), 0, hipMemcpyDefault), + hipErrorInvalidValue); + } + + MemcpyToSymbolCommonNegative( + std::bind(hipGraphMemcpyNodeSetParamsToSymbol, node, _1, _2, _3, _4, _5), + SYMBOL(int_device_var), &var, sizeof(var)); + + HIP_CHECK(hipGraphDestroy(graph)); +} diff --git a/catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol_old.cc b/catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol_old.cc new file mode 100644 index 0000000000..2e30ce00f7 --- /dev/null +++ b/catch/unit/graph/hipGraphMemcpyNodeSetParamsToSymbol_old.cc @@ -0,0 +1,264 @@ +/* +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. +*/ + +/** +Testcase Scenarios of hipGraphMemcpyNodeSetParamsToSymbol API: +Functional : +1) Allocate global symbol memory, add the node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. +2) Allocate const symbol memory, add the node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. +Negative : +1) Pass GraphNode as nullptr and check if api returns error. +2) Pass symbol ptr as nullptr, api expected to return error code. +3) Pass src ptr as nullptr, api expected to return error code. +4) Pass count as zero, api expected to return error code. +5) Pass count more than allocated size for source and destination ptr, api should return error code. +6) Pass offset+count greater than allocated size, api expected to return error code. +7) Pass same pointer as source ptr and symbol ptr, api expected to return error code. +8) Pass both destination ptr and source ptr as 2 different symbol ptr, api expected to return error code. +9) Copy from host ptr to device ptr but pass kind as different, api expected to return error code. +*/ + +#include +#include +#include +#define SIZE 256 + +__device__ int globalIn[SIZE], globalOut[SIZE]; +__device__ __constant__ int globalConst[SIZE]; + +__global__ void CpyToSymbolKernel(int* B_d) { + for (int i = 0 ; i < SIZE; i++) { + B_d[i] = globalIn[i]; + } +} + +__global__ void CpyToConstSymbolKernel(int* B_d) { + for (int i = 0 ; i < SIZE; i++) { + B_d[i] = globalConst[i]; + } +} + +/* This testcase verifies negative scenarios of + hipGraphMemcpyNodeSetParamsToSymbol API */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsToSymbol_Negative") { + constexpr size_t Nbytes = SIZE * sizeof(int); + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, nullptr, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipError_t ret; + hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + A_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + SECTION("Pass GraphNode as nullptr") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(nullptr, + HIP_SYMBOL(globalIn), + B_d, Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass symbol ptr as nullptr") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + nullptr, + B_d, Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidSymbol == ret); + } + SECTION("Pass src ptr as nullptr") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + nullptr, Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass count as zero") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + B_d, 0, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass count more than allocated size for source and dstn ptr") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + B_d, Nbytes+8, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass offset+count greater than allocated size") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + B_d, Nbytes, 10, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass same symbol pointer as source ptr and destination ptr") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + HIP_SYMBOL(globalIn), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Pass 2 different symbol pointer as source ptr and dstn ptr") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + HIP_SYMBOL(globalOut), + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Copy from host ptr to device ptr but pass kind as different") { + ret = hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), + A_h, + Nbytes, 0, + hipMemcpyDeviceToDevice); + REQUIRE(hipErrorInvalidValue == ret); + } + + HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphDestroy(graph)); +} + +static +void hipGraphMemcpyNodeSetParamsToSymbol_GlobalMem(bool useConstDeviceVar) { + constexpr size_t Nbytes = SIZE * sizeof(int); + constexpr auto blocksPerCU = 6; // to hide latency + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, SIZE); + hipGraphNode_t memcpytosymbolkernel, memcpyD2H_B; + hipKernelNodeParams kernelNodeParams{}; + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, nullptr, SIZE, false); + + hipGraph_t graph; + hipGraphExec_t graphExec; + hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A; + std::vector dependencies; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, 0, A_d, A_h, + Nbytes, hipMemcpyHostToDevice)); + dependencies.push_back(memcpyH2D_A); + + if (useConstDeviceVar) { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalConst), + C_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphAddMemcpyNodeToSymbol(&memcpyToSymbolNode, graph, + dependencies.data(), + dependencies.size(), + HIP_SYMBOL(globalIn), + C_d, Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + dependencies.clear(); + dependencies.push_back(memcpyToSymbolNode); + + // Update the node with source pointer from C_d to A_d + if (useConstDeviceVar) { + HIP_CHECK(hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalConst), A_d, + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } else { + HIP_CHECK(hipGraphMemcpyNodeSetParamsToSymbol(memcpyToSymbolNode, + HIP_SYMBOL(globalIn), A_d, + Nbytes, 0, + hipMemcpyDeviceToDevice)); + } + + // Adding Kernel node + void* kernelArgs1[] = {&B_d}; + if (useConstDeviceVar) + kernelNodeParams.func = reinterpret_cast(CpyToConstSymbolKernel); + else + kernelNodeParams.func = reinterpret_cast(CpyToSymbolKernel); + kernelNodeParams.gridDim = dim3(blocks); + kernelNodeParams.blockDim = dim3(threadsPerBlock); + kernelNodeParams.sharedMemBytes = 0; + kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs1); + kernelNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&memcpytosymbolkernel, graph, + dependencies.data(), dependencies.size(), + &kernelNodeParams)); + dependencies.clear(); + dependencies.push_back(memcpytosymbolkernel); + + // Adding MemcpyNode + HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyD2H_B, graph, dependencies.data(), + dependencies.size(), B_h, B_d, + Nbytes, hipMemcpyDeviceToHost)); + + // Instantiate and launch the graph + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, 0)); + + // Validating the result + for (int i = 0; i < SIZE; i++) { + if (B_h[i] != A_h[i]) { + WARN("Validation failed B_h[i] " << B_h[i] << "A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + + HipTest::freeArrays(A_d, B_d, C_d, + A_h, B_h, nullptr, false); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/* Test verifies hipGraphMemcpyNodeSetParamsToSymbol API Functional scenario. + 1) Allocate global symbol memory, add node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. + 2) Allocate const symbol memory, add node to the graph. + Set/Update the new values to the node. Make sure they are taking effect. + */ +TEST_CASE("Unit_hipGraphMemcpyNodeSetParamsToSymbol_Functional") { + SECTION("Check and update with Global Device Symbol Memory") { + hipGraphMemcpyNodeSetParamsToSymbol_GlobalMem(false); + } + SECTION("Check and update with Constant Global Device Symbol Memory") { + hipGraphMemcpyNodeSetParamsToSymbol_GlobalMem(true); + } +} From 638e2aabeb8c900afa3422204687e86af4d32e55 Mon Sep 17 00:00:00 2001 From: nives-vukovic <110852104+nives-vukovic@users.noreply.github.com> Date: Mon, 6 Mar 2023 09:09:35 +0100 Subject: [PATCH 03/13] EXSWHTEC-198 - Implement tests for hipStreamGetCaptureInfo, hipStreamGetCaptureInfo_v2 and hipStreamIsCapturing (#195) - Refactor existing tests by including more catch2 features - Reduce code line numbers by using helper guard classes - Add some positive and negative tests - Add doxygen test descriptions --- catch/unit/graph/CMakeLists.txt | 3 + catch/unit/graph/hipStreamGetCaptureInfo.cc | 638 +++--------------- .../unit/graph/hipStreamGetCaptureInfo_old.cc | 621 +++++++++++++++++ .../unit/graph/hipStreamGetCaptureInfo_v2.cc | 323 +++++---- .../graph/hipStreamGetCaptureInfo_v2_old.cc | 280 ++++++++ catch/unit/graph/hipStreamIsCapturing.cc | 518 ++++---------- catch/unit/graph/hipStreamIsCapturing_old.cc | 436 ++++++++++++ catch/unit/graph/stream_capture_common.hh | 78 +++ 8 files changed, 1823 insertions(+), 1074 deletions(-) create mode 100644 catch/unit/graph/hipStreamGetCaptureInfo_old.cc create mode 100644 catch/unit/graph/hipStreamGetCaptureInfo_v2_old.cc create mode 100644 catch/unit/graph/hipStreamIsCapturing_old.cc create mode 100644 catch/unit/graph/stream_capture_common.hh diff --git a/catch/unit/graph/CMakeLists.txt b/catch/unit/graph/CMakeLists.txt index f7bb6f054b..1e89b09630 100644 --- a/catch/unit/graph/CMakeLists.txt +++ b/catch/unit/graph/CMakeLists.txt @@ -64,7 +64,9 @@ set(TEST_SRC hipGraphExecMemcpyNodeSetParams.cc hipStreamBeginCapture.cc hipStreamIsCapturing.cc + hipStreamIsCapturing_old.cc hipStreamGetCaptureInfo.cc + hipStreamGetCaptureInfo_old.cc hipStreamEndCapture.cc hipGraphMemcpyNodeSetParamsFromSymbol_old.cc hipGraphMemcpyNodeSetParamsFromSymbol.cc @@ -85,6 +87,7 @@ set(TEST_SRC hipGraphHostNodeGetParams.cc hipGraphExecChildGraphNodeSetParams.cc hipStreamGetCaptureInfo_v2.cc + hipStreamGetCaptureInfo_v2_old.cc hipUserObjectCreate.cc hipGraphDebugDotPrint.cc hipGraphCloneComplx.cc diff --git a/catch/unit/graph/hipStreamGetCaptureInfo.cc b/catch/unit/graph/hipStreamGetCaptureInfo.cc index 1ea025868b..77c3dac362 100644 --- a/catch/unit/graph/hipStreamGetCaptureInfo.cc +++ b/catch/unit/graph/hipStreamGetCaptureInfo.cc @@ -17,207 +17,129 @@ OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - -Testcase Scenarios ------------------- -Functional: -1) Start stream capture and get capture info. Verify api is success, capture status is hipStreamCaptureStatusActive - and identifier returned is valid/non-zero. -2) End stream capture and get capture info. Verify api is success, capture status is hipStreamCaptureStatusNone - and identifier is not returned/updated by api. -3) Begin capture on hipStreamPerThread and get capture info. Verify api is success, capture status is hipStreamCaptureStatusActive - and identifier returned is valid/non-zero. -4) End capture on hipStreamPerThread, get capture info. Verify api is success, capture status is hipStreamCaptureStatusNone - and identifier is not returned/updated by api. -5) Perform multiple captures and verify the identifier returned is unique. - -Argument Validation/Negative: -1) Pass pId as nullptr and verify api doesn’t crash and returns success. -2) Pass pCaptureStatus as nullptr and verify api doesn’t crash and returns error code. - -Extended Scenarios ------------------- -1.Create 2 streams s1 and s2. Start capturing s1. Record event e1 on s1 and wait for event e1 on s2. Queue some operations -in s1 and s2. Invoke hipStreamGetCaptureInfo on both s1 and s2. Verify that the capture info (status and id) of both s1 and s2 -are identical. Record event e2 on s2 and wait for event e2 on s1. End the capture of stream s1. Verify that the capture info -(status and id) of both s1 and s2 are identical. - -2.Create a stream s1. Start capturing s1. Get the capture info of s1. Launch a thread. In the thread get the capture info of s1 -using hipStreamGetCaptureInfo. Verify that it is in state hipStreamCaptureStatusActive and capture id inside thread is same as -capture id in main function. Exit the thread and end the capture - -3.Verify that the id remains same througout the capture. Create a stream s1. Start capturing s1. Get the capture info of s1. -Queue some oprations in s1. Again get the capture info. Queue different operations in s1. Again get the capture info. -Verify that all the capture info are identical. - -4.Create a stream with default flag (hipStreamDefault). Start capturing the stream. Invoke hipStreamGetCaptureInfo() on the null -stream. Verify hipErrorStreamCaptureImplicit is returned by hipStreamGetCaptureInfo(). Verify capture status of created stream. -Do some operatoins. End the capture on the created stream.Verify the capture status. Execute the graph and verify the output -from the operations. - -5. Test scenario 1 using hipStreamGetCaptureInfo_v2. -6. Test scenario 2 using hipStreamGetCaptureInfo_v2. -7. Test scenario 3 using hipStreamGetCaptureInfo_v2. -8. Test scenario 4 using hipStreamGetCaptureInfo_v2. -*/ - -#include #include +#include #include -constexpr size_t N = 1000000; -constexpr unsigned blocks = 512; -constexpr unsigned threadsPerBlock = 256; -size_t Nbytes = N * sizeof(float); -constexpr int LAUNCH_ITERS = 1; +#include "stream_capture_common.hh" /** - * Validates stream capture info, launches graph and verify results + * @addtogroup hipStreamGetCaptureInfo hipStreamGetCaptureInfo + * @{ + * @ingroup GraphTest + * `hipStreamGetCaptureInfo(hipStream_t stream, hipStreamCaptureStatus + * *pCaptureStatus, unsigned long long *pId)` - get capture status of a stream */ -void validateStreamCaptureInfo(hipStream_t mstream) { - hipStream_t stream1{nullptr}, stream2{nullptr}, streamForLaunch{nullptr}; - hipEvent_t memsetEvent1, memsetEvent2, forkStreamEvent; - hipGraph_t graph{nullptr}; - hipGraphExec_t graphExec{nullptr}; - float *A_d, *C_d; - float *A_h, *C_h; - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - HIP_CHECK(hipStreamCreate(&streamForLaunch)); - // Initialize input buffer - for (size_t i = 0; i < N; ++i) { - A_h[i] = 3.146f + i; // Pi - } - - // Create cross stream dependencies. - // memset operations are done on stream1 and stream2 - // and they are joined back to mainstream - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipEventCreate(&memsetEvent1)); - HIP_CHECK(hipEventCreate(&memsetEvent2)); - HIP_CHECK(hipEventCreate(&forkStreamEvent)); - - HIP_CHECK(hipStreamBeginCapture(mstream, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(forkStreamEvent, mstream)); - HIP_CHECK(hipStreamWaitEvent(stream1, forkStreamEvent, 0)); - HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); - HIP_CHECK(hipMemsetAsync(A_d, 0, Nbytes, stream1)); - HIP_CHECK(hipEventRecord(memsetEvent1, stream1)); - HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream2)); - HIP_CHECK(hipEventRecord(memsetEvent2, stream2)); - HIP_CHECK(hipStreamWaitEvent(mstream, memsetEvent1, 0)); - HIP_CHECK(hipStreamWaitEvent(mstream, memsetEvent2, 0)); - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, mstream)); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, mstream, A_d, C_d, N); +void checkStreamCaptureInfo(hipStreamCaptureMode mode, hipStream_t stream) { + constexpr size_t N = 1000000; + size_t Nbytes = N * sizeof(float); hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; unsigned long long capSequenceID = 0; // NOLINT - HIP_CHECK(hipStreamGetCaptureInfo(mstream, &captureStatus, &capSequenceID)); - // verify capture status is active and sequence id is valid + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), N, stream); + + // Capture status is active and sequence id is valid + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus, &capSequenceID)); REQUIRE(captureStatus == hipStreamCaptureStatusActive); REQUIRE(capSequenceID > 0); // End capture and verify graph is returned - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, mstream)); - HIP_CHECK(hipStreamEndCapture(mstream, &graph)); + HIP_CHECK(hipStreamEndCapture(stream, &graph)); REQUIRE(graph != nullptr); // verify capture status is inactive and sequence id is not updated capSequenceID = 0; - HIP_CHECK(hipStreamGetCaptureInfo(mstream, &captureStatus, &capSequenceID)); + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus, &capSequenceID)); REQUIRE(captureStatus == hipStreamCaptureStatusNone); REQUIRE(capSequenceID == 0); + // Verify api still returns capture status when capture ID is nullptr + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus, nullptr)); + REQUIRE(captureStatus == hipStreamCaptureStatusNone); + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); REQUIRE(graphExec != nullptr); // Replay the recorded sequence multiple times - for (int i = 0; i < LAUNCH_ITERS; i++) { - HIP_CHECK(hipGraphLaunch(graphExec, streamForLaunch)); + for (int i = 0; i < kLaunchIters; i++) { + 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), N); } - HIP_CHECK(hipStreamSynchronize(streamForLaunch)); - + HIP_CHECK(hipGraphExecDestroy(graphExec)) HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForLaunch)); - HIP_CHECK(hipStreamDestroy(stream1)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipEventDestroy(forkStreamEvent)); - HIP_CHECK(hipEventDestroy(memsetEvent1)); - HIP_CHECK(hipEventDestroy(memsetEvent2)); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); - - // Validate the computation - for (size_t i = 0; i < N; i++) { - if (C_h[i] != A_h[i] * A_h[i]) { - INFO("A and C not matching at " << i << " C_h[i] " << C_h[i] - << " A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - free(A_h); - free(C_h); } /** - * Basic Functional Test for stream capture and getting capture info. - * Regular/custom stream is used for stream capture. + * Test Description + * ------------------------ + * - Test to verify that hipStreamCaptureStatusActive is returned during + * stream capture. When capture is ended, status is changed to + * hipStreamCaptureStatusNone and error is not reported when some arguments are + * not passed + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamGetCaptureInfo.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_BasicFunctional") { - hipStream_t streamForCapture; +TEST_CASE("Unit_hipStreamGetCaptureInfo_Positive_Functional") { + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); - HIP_CHECK(hipStreamCreate(&streamForCapture)); - validateStreamCaptureInfo(streamForCapture); - HIP_CHECK(hipStreamDestroy(streamForCapture)); + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + + checkStreamCaptureInfo(captureMode, stream); } /** - * Test performs stream capture on hipStreamPerThread and validates - * capture info. + * Test Description + * ------------------------ + * - Test starts stream capture on multiple streams and verifies uniqueness + * of identifiers returned + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamGetCaptureInfo.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_hipStreamPerThread") { - validateStreamCaptureInfo(hipStreamPerThread); -} - -/** - * Test starts stream capture on multiple streams and verifies uniqueness of - * identifiers returned. - */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_UniqueID") { +TEST_CASE("Unit_hipStreamGetCaptureInfo_Positive_UniqueID") { constexpr int numStreams = 100; - hipStream_t streams[numStreams]{}; hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; std::vector idlist; - unsigned long long capSequenceID{}; //NOLINT + unsigned long long capSequenceID{}; // NOLINT hipGraph_t graph{nullptr}; + StreamsGuard streams(numStreams); + for (int i = 0; i < numStreams; i++) { - HIP_CHECK(hipStreamCreate(&streams[i])); HIP_CHECK(hipStreamBeginCapture(streams[i], hipStreamCaptureModeGlobal)); - HIP_CHECK(hipStreamGetCaptureInfo(streams[i], &captureStatus, - &capSequenceID)); + HIP_CHECK(hipStreamGetCaptureInfo(streams[i], &captureStatus, &capSequenceID)); REQUIRE(captureStatus == hipStreamCaptureStatusActive); REQUIRE(capSequenceID > 0); idlist.push_back(capSequenceID); } for (int i = 0; i < numStreams; i++) { - for (int j = i+1; j < numStreams; j++) { + for (int j = i + 1; j < numStreams; j++) { if (idlist[i] == idlist[j]) { - INFO("Same identifier returned for stream " - << i << " and stream " << j); + INFO("Same identifier returned for stream " << i << " and stream " << j); REQUIRE(false); } } @@ -226,396 +148,50 @@ TEST_CASE("Unit_hipStreamGetCaptureInfo_UniqueID") { for (int i = 0; i < numStreams; i++) { HIP_CHECK(hipStreamEndCapture(streams[i], &graph)); HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streams[i])); } } /** - * Argument validation/Negative tests for api + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# Capture status is nullptr + * -# Capture status checked on legacy/null stream + * -# Stream is uninitialized + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamGetCaptureInfo.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_ArgValidation") { - hipError_t ret; - hipStream_t stream; - hipStreamCaptureStatus captureStatus; +TEST_CASE("Unit_hipStreamGetCaptureInfo_Negative_Parameters") { + hipStreamCaptureStatus cStatus; unsigned long long capSequenceID; // NOLINT - HIP_CHECK(hipStreamCreate(&stream)); - - SECTION("Capture ID location as nullptr") { - ret = hipStreamGetCaptureInfo(stream, &captureStatus, nullptr); - // Capture ID is optional - REQUIRE(ret == hipSuccess); - } + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); SECTION("Capture Status location as nullptr") { - ret = hipStreamGetCaptureInfo(stream, nullptr, &capSequenceID); - REQUIRE(ret == hipErrorInvalidValue); + HIP_CHECK_ERROR(hipStreamGetCaptureInfo(stream, nullptr, &capSequenceID), hipErrorInvalidValue); } - - HIP_CHECK(hipStreamDestroy(stream)); -} -/* - * Create 2 streams s1 and s2. Start capturing s1. Record event e1 on s1 and - * wait for event e1 on s2. Queue some operations in s1 and s2. Invoke - * hipStreamGetCaptureInfo on both s1 and s2. Verify that the capture info - * (status and id) of both s1 and s2 are identical. Record event e2 on s2 - * and wait for event e2 on s1. End the capture of stream s1. Verify that the - * capture info (status and id) of both s1 and s2 are identical. - * The above scenario using hipStreamGetCaptureInfo_v2 API - */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_ParentAndForkedStrm_CaptureStatus") { - hipStream_t stream1{nullptr}, stream2{nullptr}; - hipEvent_t event2{nullptr}, forkStreamEvent{nullptr}; - hipGraph_t graph{nullptr}; - float *A_d, *B_d, *C_d, *D_d; - float *A_h, *B_h, *C_h, *D_h; - // Memory allocation to Host pointers - A_h = reinterpret_cast(malloc(Nbytes)); - B_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - D_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(B_h != nullptr); - REQUIRE(C_h != nullptr); - REQUIRE(D_h != nullptr); - // Memory allocation to Device pointers - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&B_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - HIP_CHECK(hipMalloc(&D_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(B_d != nullptr); - REQUIRE(C_d != nullptr); - REQUIRE(D_d != nullptr); - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipEventCreate(&event2)); - HIP_CHECK(hipEventCreate(&forkStreamEvent)); - // Start capture on stream1 - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); - // Copy data to Device - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream1)); - HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream2)); - // Kernal Operations - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream1, A_d, C_d, N); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream2, B_d, D_d, N); - // Copy data back to the Host - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream1)); - HIP_CHECK(hipMemcpyAsync(D_h, D_d, Nbytes, hipMemcpyDeviceToHost, stream2)); - - hipStreamCaptureStatus captureStatus1{hipStreamCaptureStatusNone}, - captureStatus2{hipStreamCaptureStatusNone}, - captureStatus3{hipStreamCaptureStatusNone}, - captureStatus4{hipStreamCaptureStatusNone}; - unsigned long long capSequenceID1, capSequenceID2, capSequenceID3, //NOLINT - capSequenceID4; - SECTION("hipStreamGetCaptureInfo verification before End capture") { - // Capture info - HIP_CHECK(hipStreamGetCaptureInfo(stream1, &captureStatus1, - &capSequenceID1)); - HIP_CHECK(hipStreamGetCaptureInfo(stream2, &captureStatus2, - &capSequenceID2)); - // Verfication of results - REQUIRE(capSequenceID1 == capSequenceID2); - REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); - REQUIRE(captureStatus2 == hipStreamCaptureStatusActive); +#if HT_NVIDIA // EXSWHTEC-216, EXSWHTEC-228 + SECTION("Capture status when checked on null stream") { + hipGraph_t graph{nullptr}; + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + HIP_CHECK_ERROR(hipStreamGetCaptureInfo(nullptr, &cStatus, &capSequenceID), + hipErrorStreamCaptureImplicit); + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + HIP_CHECK(hipGraphDestroy(graph)); } - SECTION("hipStreamGetCaptureInfo_v2 verification before End capture") { - // Capture info - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream1, &captureStatus1, - &capSequenceID1, nullptr, nullptr, nullptr)); - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream2, &captureStatus2, - &capSequenceID2, nullptr, nullptr, nullptr)); - // Verfication of results - REQUIRE(capSequenceID1 == capSequenceID2); - REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); - REQUIRE(captureStatus2 == hipStreamCaptureStatusActive); + SECTION("Capture status when stream is uninitialized") { + constexpr auto InvalidStream = [] { + StreamGuard sg(Streams::created); + return sg.stream(); + }; + + HIP_CHECK_ERROR(hipStreamGetCaptureInfo(InvalidStream(), &cStatus, &capSequenceID), + hipErrorContextIsDestroyed); } - - - HIP_CHECK(hipEventRecord(event2, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream1, event2, 0)); - // End the capture - HIP_CHECK(hipStreamEndCapture(stream1, &graph)); - REQUIRE(graph != nullptr); - SECTION("hipStreamGetCaptureInfo verification after End capture") { - // Capture Info - HIP_CHECK(hipStreamGetCaptureInfo(stream1, &captureStatus3, - &capSequenceID3)); - HIP_CHECK(hipStreamGetCaptureInfo(stream2, &captureStatus4, - &capSequenceID4)); - // Verification of results - REQUIRE(captureStatus3 == hipStreamCaptureStatusNone); - REQUIRE(captureStatus4 == hipStreamCaptureStatusNone); - } - SECTION("hipStreamGetCaptureInfo_v2 verification after End capture") { - // Capture Info - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream1, &captureStatus3, - &capSequenceID3, nullptr, nullptr, nullptr)); - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream2, &captureStatus4, - &capSequenceID4, nullptr, nullptr, nullptr)); - // Verification of results - REQUIRE(captureStatus3 == hipStreamCaptureStatusNone); - REQUIRE(captureStatus4 == hipStreamCaptureStatusNone); - } - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream1)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipEventDestroy(forkStreamEvent)); - HIP_CHECK(hipEventDestroy(event2)); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(B_d)); - HIP_CHECK(hipFree(C_d)); - HIP_CHECK(hipFree(D_d)); - free(A_h); - free(B_h); - free(C_h); - free(D_h); -} -// Thread Function -static void thread_func(hipStream_t stream, unsigned long long capSequenceID1, //NOLINT - unsigned long long capSequenceID2) { //NOLINT - hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; - unsigned long long capSequenceID3, capSequenceID4; //NOLINT - SECTION("hipStreamGetCaptureInfo CaptureStatus in Thread") { - HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus, &capSequenceID3)); - REQUIRE(capSequenceID1 == capSequenceID3); - REQUIRE(captureStatus == hipStreamCaptureStatusActive); - } - SECTION("hipStreamGetCaptureInfo_v2 CaptureStatus in Thread") { - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, - &capSequenceID4, nullptr, nullptr, nullptr)); - REQUIRE(capSequenceID2 == capSequenceID4); - REQUIRE(captureStatus == hipStreamCaptureStatusActive); - } -} -/* - * Create a stream s1. Start capturing s1. Get the capture info of s1. Launch - * a thread. In the thread get the capture info of s1 using hipStreamGetCaptureInfo. - * Verify that it is in state hipStreamCaptureStatusActive and capture id inside - * thread is same as capture id in main function. Exit the thread and end the capture - * The above scenario using hipStreamGetCaptureInfo_v2 API - */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_CaptureStatus_InThread") { - hipStream_t stream{nullptr}; - hipGraph_t graph{nullptr}; - - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - // Capture info - hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; - unsigned long long capSequenceID1, capSequenceID2; //NOLINT - // hipStreamGetCaptureInfo Capture status - HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus, &capSequenceID1)); - // hipStreamGetCaptureInfo_v2 Capture status - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, - &capSequenceID2, nullptr, nullptr, nullptr)); - // Thread launch - std::thread t(thread_func, stream, capSequenceID1, capSequenceID2); - t.join(); - - HIP_CHECK(hipStreamEndCapture(stream, &graph)); - REQUIRE(graph != nullptr); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream)); -} -/* - * Verify that the id remains same througout the capture. Create a stream s1. - * Start capturing s1. Get the capture info of s1. Queue some oprations in s1. - * Again get the capture info. Queue different operations in s1. Again get the - * capture info. Verify that all the capture info are identical. - * The above scenario using hipStreamGetCaptureInfo_v2 API -*/ -TEST_CASE("Unit_hipStreamGetCaptureInfo_CaptureStatus_Througout_Capture") { - hipStream_t stream{nullptr}; - hipGraph_t graph{nullptr}; - float *A_d, *B_d, *C_d, *D_d; - float *A_h, *B_h, *C_h, *D_h; - // Memory allocation to Host pointers - A_h = reinterpret_cast(malloc(Nbytes)); - B_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - D_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(B_h != nullptr); - REQUIRE(C_h != nullptr); - REQUIRE(D_h != nullptr); - // Memory allocation to Device pointers - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&B_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - HIP_CHECK(hipMalloc(&D_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(B_d != nullptr); - REQUIRE(C_d != nullptr); - REQUIRE(D_d != nullptr); - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - // Capture Info - hipStreamCaptureStatus captureStatus1{hipStreamCaptureStatusNone}, - captureStatus2{hipStreamCaptureStatusNone}, - captureStatus3{hipStreamCaptureStatusNone}, - captureStatus4{hipStreamCaptureStatusNone}, - captureStatus5{hipStreamCaptureStatusNone}, - captureStatus6{hipStreamCaptureStatusNone}; - - unsigned long long capSequenceID1, capSequenceID2, capSequenceID3, //NOLINT - capSequenceID4, capSequenceID5, capSequenceID6; - - // hipStreamGetCaptureInfo Capture status - HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus1, &capSequenceID1)); - // hipStreamGetCaptureInfo_v2 Capture status - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus2, - &capSequenceID2, nullptr, nullptr, nullptr)); - // Copy data to Device - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); - // Kernal Operations - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); - - // hipStreamGetCaptureInfo Capture status - HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus3, &capSequenceID3)); - REQUIRE(captureStatus1 == captureStatus3); - REQUIRE(capSequenceID1 == capSequenceID3); - // hipStreamGetCaptureInfo_v2 Capture status - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus4, - &capSequenceID4, nullptr, nullptr, nullptr)); - REQUIRE(captureStatus2 == captureStatus4); - REQUIRE(capSequenceID2 == capSequenceID4); - - // Kernal Operations - HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, B_d, D_d, N); - HIP_CHECK(hipMemcpyAsync(D_h, D_d, Nbytes, hipMemcpyDeviceToHost, stream)); - - // hipStreamGetCaptureInfo Capture status - HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus5, &capSequenceID5)); - REQUIRE(captureStatus3 == captureStatus5); - REQUIRE(capSequenceID3 == capSequenceID5); - // hipStreamGetCaptureInfo_v2 Capture status - HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus6, - &capSequenceID6, nullptr, nullptr, nullptr)); - REQUIRE(captureStatus4 == captureStatus6); - REQUIRE(capSequenceID4 == capSequenceID6); - - HIP_CHECK(hipStreamEndCapture(stream, &graph)); - REQUIRE(graph != nullptr); - - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream)); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(B_d)); - HIP_CHECK(hipFree(C_d)); - HIP_CHECK(hipFree(D_d)); - free(A_h); - free(B_h); - free(C_h); - free(D_h); -} -/* - * Create a stream with default flag (hipStreamDefault). Start capturing the stream. - * Invoke hipStreamGetCaptureInfo() on the null stream. Verify hipErrorStreamCaptureImplicit - * is returned by hipStreamGetCaptureInfo(). Verify capture status of created stream. Do some - * operatoins. End the capture on the created stream.Verify the capture status. Execute the - * graph and verify the output from the operations. - * The above scenario using hipStreamGetCaptureInfo_v2 API -*/ -TEST_CASE("Unit_hipStreamGetCaptureInfo_Nullstream_CaptureInfo") { - hipStream_t stream{nullptr}, streamForGraph{nullptr}; - hipGraph_t graph{nullptr}; - hipError_t ret; - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - float *A_d, *C_d; - float *A_h, *C_h, *D_h; - // Memory allocation to Host pointers - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - D_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - REQUIRE(D_h != nullptr); - - // Memory allocation to Device pointers - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - - // Initialize input buffer - for (size_t i = 0; i < N; ++i) { - A_h[i] = 1.0f + i; - D_h[i] = 0.0f; - } - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - - hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}, - captureStatus1{hipStreamCaptureStatusNone}, - captureStatus2{hipStreamCaptureStatusNone}; - unsigned long long capSequenceID = 0, // NOLINT - capSequenceID1 = 0; - - // Verify the Error returned with null stream. - SECTION("hipStreamGetCaptureInfo with null stream") { - ret = hipStreamGetCaptureInfo(0, &captureStatus, &capSequenceID); - REQUIRE(ret == hipErrorStreamCaptureImplicit); - } - SECTION("hipStreamGetCaptureInfo_v2 with null stream") { - ret = hipStreamGetCaptureInfo_v2(0, &captureStatus, &capSequenceID, - nullptr, nullptr, nullptr); - REQUIRE(ret == hipErrorStreamCaptureImplicit); - } - - - // Check the capture status of the stream - HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus1)); - REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); - - // Copy data to Device - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); - - // Kernal Operation - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); - - // End the capture - HIP_CHECK(hipStreamEndCapture(stream, &graph)); - REQUIRE(graph != nullptr); - - // Capture Status - SECTION("hipStreamGetCaptureInfo with null stream after End capture") { - ret = hipStreamGetCaptureInfo(0, &captureStatus2, &capSequenceID1); - REQUIRE(ret == hipSuccess); - } - SECTION("hipStreamGetCaptureInfo_v2 with null stream after End capture") { - ret = hipStreamGetCaptureInfo_v2(0, &captureStatus2, &capSequenceID1, - nullptr, nullptr, nullptr); - REQUIRE(ret == hipSuccess); - } - // Launch graph - hipGraphExec_t graphExec; - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); - - // Verify Output - for (size_t i = 0; i < N; i++) { - D_h[i] = A_h[i] * A_h[i]; - REQUIRE(C_h[i] == D_h[i]); - } - - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); - free(A_h); - free(C_h); - free(D_h); -} +#endif +} \ No newline at end of file diff --git a/catch/unit/graph/hipStreamGetCaptureInfo_old.cc b/catch/unit/graph/hipStreamGetCaptureInfo_old.cc new file mode 100644 index 0000000000..c4b0b4b5b1 --- /dev/null +++ b/catch/unit/graph/hipStreamGetCaptureInfo_old.cc @@ -0,0 +1,621 @@ +/* +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. +*/ + +/** + +Testcase Scenarios +------------------ +Functional: +1) Start stream capture and get capture info. Verify api is success, capture status is hipStreamCaptureStatusActive + and identifier returned is valid/non-zero. +2) End stream capture and get capture info. Verify api is success, capture status is hipStreamCaptureStatusNone + and identifier is not returned/updated by api. +3) Begin capture on hipStreamPerThread and get capture info. Verify api is success, capture status is hipStreamCaptureStatusActive + and identifier returned is valid/non-zero. +4) End capture on hipStreamPerThread, get capture info. Verify api is success, capture status is hipStreamCaptureStatusNone + and identifier is not returned/updated by api. +5) Perform multiple captures and verify the identifier returned is unique. + +Argument Validation/Negative: +1) Pass pId as nullptr and verify api doesn’t crash and returns success. +2) Pass pCaptureStatus as nullptr and verify api doesn’t crash and returns error code. + +Extended Scenarios +------------------ +1.Create 2 streams s1 and s2. Start capturing s1. Record event e1 on s1 and wait for event e1 on s2. Queue some operations +in s1 and s2. Invoke hipStreamGetCaptureInfo on both s1 and s2. Verify that the capture info (status and id) of both s1 and s2 +are identical. Record event e2 on s2 and wait for event e2 on s1. End the capture of stream s1. Verify that the capture info +(status and id) of both s1 and s2 are identical. + +2.Create a stream s1. Start capturing s1. Get the capture info of s1. Launch a thread. In the thread get the capture info of s1 +using hipStreamGetCaptureInfo. Verify that it is in state hipStreamCaptureStatusActive and capture id inside thread is same as +capture id in main function. Exit the thread and end the capture + +3.Verify that the id remains same througout the capture. Create a stream s1. Start capturing s1. Get the capture info of s1. +Queue some oprations in s1. Again get the capture info. Queue different operations in s1. Again get the capture info. +Verify that all the capture info are identical. + +4.Create a stream with default flag (hipStreamDefault). Start capturing the stream. Invoke hipStreamGetCaptureInfo() on the null +stream. Verify hipErrorStreamCaptureImplicit is returned by hipStreamGetCaptureInfo(). Verify capture status of created stream. +Do some operatoins. End the capture on the created stream.Verify the capture status. Execute the graph and verify the output +from the operations. + +5. Test scenario 1 using hipStreamGetCaptureInfo_v2. +6. Test scenario 2 using hipStreamGetCaptureInfo_v2. +7. Test scenario 3 using hipStreamGetCaptureInfo_v2. +8. Test scenario 4 using hipStreamGetCaptureInfo_v2. +*/ + +#include +#include +#include + +constexpr size_t N = 1000000; +constexpr unsigned blocks = 512; +constexpr unsigned threadsPerBlock = 256; +size_t Nbytes = N * sizeof(float); +constexpr int LAUNCH_ITERS = 1; + +/** + * Validates stream capture info, launches graph and verify results + */ +void validateStreamCaptureInfo(hipStream_t mstream) { + hipStream_t stream1{nullptr}, stream2{nullptr}, streamForLaunch{nullptr}; + hipEvent_t memsetEvent1, memsetEvent2, forkStreamEvent; + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + float *A_d, *C_d; + float *A_h, *C_h; + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + HIP_CHECK(hipStreamCreate(&streamForLaunch)); + + // Initialize input buffer + for (size_t i = 0; i < N; ++i) { + A_h[i] = 3.146f + i; // Pi + } + + // Create cross stream dependencies. + // memset operations are done on stream1 and stream2 + // and they are joined back to mainstream + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipEventCreate(&memsetEvent1)); + HIP_CHECK(hipEventCreate(&memsetEvent2)); + HIP_CHECK(hipEventCreate(&forkStreamEvent)); + + HIP_CHECK(hipStreamBeginCapture(mstream, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(forkStreamEvent, mstream)); + HIP_CHECK(hipStreamWaitEvent(stream1, forkStreamEvent, 0)); + HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); + HIP_CHECK(hipMemsetAsync(A_d, 0, Nbytes, stream1)); + HIP_CHECK(hipEventRecord(memsetEvent1, stream1)); + HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream2)); + HIP_CHECK(hipEventRecord(memsetEvent2, stream2)); + HIP_CHECK(hipStreamWaitEvent(mstream, memsetEvent1, 0)); + HIP_CHECK(hipStreamWaitEvent(mstream, memsetEvent2, 0)); + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, mstream)); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, mstream, A_d, C_d, N); + + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + unsigned long long capSequenceID = 0; // NOLINT + HIP_CHECK(hipStreamGetCaptureInfo(mstream, &captureStatus, &capSequenceID)); + + // verify capture status is active and sequence id is valid + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + REQUIRE(capSequenceID > 0); + + // End capture and verify graph is returned + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, mstream)); + HIP_CHECK(hipStreamEndCapture(mstream, &graph)); + REQUIRE(graph != nullptr); + + // verify capture status is inactive and sequence id is not updated + capSequenceID = 0; + HIP_CHECK(hipStreamGetCaptureInfo(mstream, &captureStatus, &capSequenceID)); + REQUIRE(captureStatus == hipStreamCaptureStatusNone); + REQUIRE(capSequenceID == 0); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + REQUIRE(graphExec != nullptr); + + // Replay the recorded sequence multiple times + for (int i = 0; i < LAUNCH_ITERS; i++) { + HIP_CHECK(hipGraphLaunch(graphExec, streamForLaunch)); + } + + HIP_CHECK(hipStreamSynchronize(streamForLaunch)); + + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(streamForLaunch)); + HIP_CHECK(hipStreamDestroy(stream1)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipEventDestroy(forkStreamEvent)); + HIP_CHECK(hipEventDestroy(memsetEvent1)); + HIP_CHECK(hipEventDestroy(memsetEvent2)); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); + + // Validate the computation + for (size_t i = 0; i < N; i++) { + if (C_h[i] != A_h[i] * A_h[i]) { + INFO("A and C not matching at " << i << " C_h[i] " << C_h[i] + << " A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + free(A_h); + free(C_h); +} + +/** + * Basic Functional Test for stream capture and getting capture info. + * Regular/custom stream is used for stream capture. + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_BasicFunctional") { + hipStream_t streamForCapture; + + HIP_CHECK(hipStreamCreate(&streamForCapture)); + validateStreamCaptureInfo(streamForCapture); + HIP_CHECK(hipStreamDestroy(streamForCapture)); +} + +/** + * Test performs stream capture on hipStreamPerThread and validates + * capture info. + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_hipStreamPerThread") { + validateStreamCaptureInfo(hipStreamPerThread); +} + +/** + * Test starts stream capture on multiple streams and verifies uniqueness of + * identifiers returned. + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_UniqueID") { + constexpr int numStreams = 100; + hipStream_t streams[numStreams]{}; + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + std::vector idlist; + unsigned long long capSequenceID{}; //NOLINT + hipGraph_t graph{nullptr}; + + for (int i = 0; i < numStreams; i++) { + HIP_CHECK(hipStreamCreate(&streams[i])); + HIP_CHECK(hipStreamBeginCapture(streams[i], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamGetCaptureInfo(streams[i], &captureStatus, + &capSequenceID)); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + REQUIRE(capSequenceID > 0); + idlist.push_back(capSequenceID); + } + + for (int i = 0; i < numStreams; i++) { + for (int j = i+1; j < numStreams; j++) { + if (idlist[i] == idlist[j]) { + INFO("Same identifier returned for stream " + << i << " and stream " << j); + REQUIRE(false); + } + } + } + + for (int i = 0; i < numStreams; i++) { + HIP_CHECK(hipStreamEndCapture(streams[i], &graph)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(streams[i])); + } +} + +/** + * Argument validation/Negative tests for api + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_ArgValidation") { + hipError_t ret; + hipStream_t stream; + hipStreamCaptureStatus captureStatus; + unsigned long long capSequenceID; // NOLINT + HIP_CHECK(hipStreamCreate(&stream)); + + SECTION("Capture ID location as nullptr") { + ret = hipStreamGetCaptureInfo(stream, &captureStatus, nullptr); + // Capture ID is optional + REQUIRE(ret == hipSuccess); + } + + SECTION("Capture Status location as nullptr") { + ret = hipStreamGetCaptureInfo(stream, nullptr, &capSequenceID); + REQUIRE(ret == hipErrorInvalidValue); + } + + HIP_CHECK(hipStreamDestroy(stream)); +} +/* + * Create 2 streams s1 and s2. Start capturing s1. Record event e1 on s1 and + * wait for event e1 on s2. Queue some operations in s1 and s2. Invoke + * hipStreamGetCaptureInfo on both s1 and s2. Verify that the capture info + * (status and id) of both s1 and s2 are identical. Record event e2 on s2 + * and wait for event e2 on s1. End the capture of stream s1. Verify that the + * capture info (status and id) of both s1 and s2 are identical. + * The above scenario using hipStreamGetCaptureInfo_v2 API + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_ParentAndForkedStrm_CaptureStatus") { + hipStream_t stream1{nullptr}, stream2{nullptr}; + hipEvent_t event2{nullptr}, forkStreamEvent{nullptr}; + hipGraph_t graph{nullptr}; + float *A_d, *B_d, *C_d, *D_d; + float *A_h, *B_h, *C_h, *D_h; + // Memory allocation to Host pointers + A_h = reinterpret_cast(malloc(Nbytes)); + B_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + D_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(B_h != nullptr); + REQUIRE(C_h != nullptr); + REQUIRE(D_h != nullptr); + // Memory allocation to Device pointers + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&B_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + HIP_CHECK(hipMalloc(&D_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(B_d != nullptr); + REQUIRE(C_d != nullptr); + REQUIRE(D_d != nullptr); + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipEventCreate(&event2)); + HIP_CHECK(hipEventCreate(&forkStreamEvent)); + // Start capture on stream1 + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); + // Copy data to Device + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream1)); + HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream2)); + // Kernal Operations + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream1, A_d, C_d, N); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream2, B_d, D_d, N); + // Copy data back to the Host + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream1)); + HIP_CHECK(hipMemcpyAsync(D_h, D_d, Nbytes, hipMemcpyDeviceToHost, stream2)); + + hipStreamCaptureStatus captureStatus1{hipStreamCaptureStatusNone}, + captureStatus2{hipStreamCaptureStatusNone}, + captureStatus3{hipStreamCaptureStatusNone}, + captureStatus4{hipStreamCaptureStatusNone}; + unsigned long long capSequenceID1, capSequenceID2, capSequenceID3, //NOLINT + capSequenceID4; + SECTION("hipStreamGetCaptureInfo verification before End capture") { + // Capture info + HIP_CHECK(hipStreamGetCaptureInfo(stream1, &captureStatus1, + &capSequenceID1)); + HIP_CHECK(hipStreamGetCaptureInfo(stream2, &captureStatus2, + &capSequenceID2)); + // Verfication of results + REQUIRE(capSequenceID1 == capSequenceID2); + REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); + REQUIRE(captureStatus2 == hipStreamCaptureStatusActive); + } + SECTION("hipStreamGetCaptureInfo_v2 verification before End capture") { + // Capture info + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream1, &captureStatus1, + &capSequenceID1, nullptr, nullptr, nullptr)); + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream2, &captureStatus2, + &capSequenceID2, nullptr, nullptr, nullptr)); + // Verfication of results + REQUIRE(capSequenceID1 == capSequenceID2); + REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); + REQUIRE(captureStatus2 == hipStreamCaptureStatusActive); + } + + + HIP_CHECK(hipEventRecord(event2, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream1, event2, 0)); + // End the capture + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); + REQUIRE(graph != nullptr); + SECTION("hipStreamGetCaptureInfo verification after End capture") { + // Capture Info + HIP_CHECK(hipStreamGetCaptureInfo(stream1, &captureStatus3, + &capSequenceID3)); + HIP_CHECK(hipStreamGetCaptureInfo(stream2, &captureStatus4, + &capSequenceID4)); + // Verification of results + REQUIRE(captureStatus3 == hipStreamCaptureStatusNone); + REQUIRE(captureStatus4 == hipStreamCaptureStatusNone); + } + SECTION("hipStreamGetCaptureInfo_v2 verification after End capture") { + // Capture Info + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream1, &captureStatus3, + &capSequenceID3, nullptr, nullptr, nullptr)); + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream2, &captureStatus4, + &capSequenceID4, nullptr, nullptr, nullptr)); + // Verification of results + REQUIRE(captureStatus3 == hipStreamCaptureStatusNone); + REQUIRE(captureStatus4 == hipStreamCaptureStatusNone); + } + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream1)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipEventDestroy(forkStreamEvent)); + HIP_CHECK(hipEventDestroy(event2)); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(B_d)); + HIP_CHECK(hipFree(C_d)); + HIP_CHECK(hipFree(D_d)); + free(A_h); + free(B_h); + free(C_h); + free(D_h); +} +// Thread Function +static void thread_func(hipStream_t stream, unsigned long long capSequenceID1, //NOLINT + unsigned long long capSequenceID2) { //NOLINT + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + unsigned long long capSequenceID3, capSequenceID4; //NOLINT + SECTION("hipStreamGetCaptureInfo CaptureStatus in Thread") { + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus, &capSequenceID3)); + REQUIRE(capSequenceID1 == capSequenceID3); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + } + SECTION("hipStreamGetCaptureInfo_v2 CaptureStatus in Thread") { + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, + &capSequenceID4, nullptr, nullptr, nullptr)); + REQUIRE(capSequenceID2 == capSequenceID4); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + } +} +/* + * Create a stream s1. Start capturing s1. Get the capture info of s1. Launch + * a thread. In the thread get the capture info of s1 using hipStreamGetCaptureInfo. + * Verify that it is in state hipStreamCaptureStatusActive and capture id inside + * thread is same as capture id in main function. Exit the thread and end the capture + * The above scenario using hipStreamGetCaptureInfo_v2 API + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_CaptureStatus_InThread") { + hipStream_t stream{nullptr}; + hipGraph_t graph{nullptr}; + + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + // Capture info + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + unsigned long long capSequenceID1, capSequenceID2; //NOLINT + // hipStreamGetCaptureInfo Capture status + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus, &capSequenceID1)); + // hipStreamGetCaptureInfo_v2 Capture status + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, + &capSequenceID2, nullptr, nullptr, nullptr)); + // Thread launch + std::thread t(thread_func, stream, capSequenceID1, capSequenceID2); + t.join(); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); +} +/* + * Verify that the id remains same througout the capture. Create a stream s1. + * Start capturing s1. Get the capture info of s1. Queue some oprations in s1. + * Again get the capture info. Queue different operations in s1. Again get the + * capture info. Verify that all the capture info are identical. + * The above scenario using hipStreamGetCaptureInfo_v2 API +*/ +TEST_CASE("Unit_hipStreamGetCaptureInfo_CaptureStatus_Througout_Capture") { + hipStream_t stream{nullptr}; + hipGraph_t graph{nullptr}; + float *A_d, *B_d, *C_d, *D_d; + float *A_h, *B_h, *C_h, *D_h; + // Memory allocation to Host pointers + A_h = reinterpret_cast(malloc(Nbytes)); + B_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + D_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(B_h != nullptr); + REQUIRE(C_h != nullptr); + REQUIRE(D_h != nullptr); + // Memory allocation to Device pointers + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&B_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + HIP_CHECK(hipMalloc(&D_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(B_d != nullptr); + REQUIRE(C_d != nullptr); + REQUIRE(D_d != nullptr); + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + // Capture Info + hipStreamCaptureStatus captureStatus1{hipStreamCaptureStatusNone}, + captureStatus2{hipStreamCaptureStatusNone}, + captureStatus3{hipStreamCaptureStatusNone}, + captureStatus4{hipStreamCaptureStatusNone}, + captureStatus5{hipStreamCaptureStatusNone}, + captureStatus6{hipStreamCaptureStatusNone}; + + unsigned long long capSequenceID1, capSequenceID2, capSequenceID3, //NOLINT + capSequenceID4, capSequenceID5, capSequenceID6; + + // hipStreamGetCaptureInfo Capture status + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus1, &capSequenceID1)); + // hipStreamGetCaptureInfo_v2 Capture status + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus2, + &capSequenceID2, nullptr, nullptr, nullptr)); + // Copy data to Device + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + // Kernal Operations + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + + // hipStreamGetCaptureInfo Capture status + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus3, &capSequenceID3)); + REQUIRE(captureStatus1 == captureStatus3); + REQUIRE(capSequenceID1 == capSequenceID3); + // hipStreamGetCaptureInfo_v2 Capture status + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus4, + &capSequenceID4, nullptr, nullptr, nullptr)); + REQUIRE(captureStatus2 == captureStatus4); + REQUIRE(capSequenceID2 == capSequenceID4); + + // Kernal Operations + HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, B_d, D_d, N); + HIP_CHECK(hipMemcpyAsync(D_h, D_d, Nbytes, hipMemcpyDeviceToHost, stream)); + + // hipStreamGetCaptureInfo Capture status + HIP_CHECK(hipStreamGetCaptureInfo(stream, &captureStatus5, &capSequenceID5)); + REQUIRE(captureStatus3 == captureStatus5); + REQUIRE(capSequenceID3 == capSequenceID5); + // hipStreamGetCaptureInfo_v2 Capture status + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus6, + &capSequenceID6, nullptr, nullptr, nullptr)); + REQUIRE(captureStatus4 == captureStatus6); + REQUIRE(capSequenceID4 == capSequenceID6); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(B_d)); + HIP_CHECK(hipFree(C_d)); + HIP_CHECK(hipFree(D_d)); + free(A_h); + free(B_h); + free(C_h); + free(D_h); +} +/* + * Create a stream with default flag (hipStreamDefault). Start capturing the stream. + * Invoke hipStreamGetCaptureInfo() on the null stream. Verify hipErrorStreamCaptureImplicit + * is returned by hipStreamGetCaptureInfo(). Verify capture status of created stream. Do some + * operatoins. End the capture on the created stream.Verify the capture status. Execute the + * graph and verify the output from the operations. + * The above scenario using hipStreamGetCaptureInfo_v2 API +*/ +TEST_CASE("Unit_hipStreamGetCaptureInfo_Nullstream_CaptureInfo") { + hipStream_t stream{nullptr}, streamForGraph{nullptr}; + hipGraph_t graph{nullptr}; + hipError_t ret; + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamCreate(&streamForGraph)); + float *A_d, *C_d; + float *A_h, *C_h, *D_h; + // Memory allocation to Host pointers + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + D_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + REQUIRE(D_h != nullptr); + + // Memory allocation to Device pointers + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + + // Initialize input buffer + for (size_t i = 0; i < N; ++i) { + A_h[i] = 1.0f + i; + D_h[i] = 0.0f; + } + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}, + captureStatus1{hipStreamCaptureStatusNone}, + captureStatus2{hipStreamCaptureStatusNone}; + unsigned long long capSequenceID = 0, // NOLINT + capSequenceID1 = 0; + + // Verify the Error returned with null stream. + SECTION("hipStreamGetCaptureInfo with null stream") { + ret = hipStreamGetCaptureInfo(0, &captureStatus, &capSequenceID); + REQUIRE(ret == hipErrorStreamCaptureImplicit); + } + SECTION("hipStreamGetCaptureInfo_v2 with null stream") { + ret = hipStreamGetCaptureInfo_v2(0, &captureStatus, &capSequenceID, + nullptr, nullptr, nullptr); + REQUIRE(ret == hipErrorStreamCaptureImplicit); + } + + + // Check the capture status of the stream + HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus1)); + REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); + + // Copy data to Device + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + + // Kernal Operation + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + + // End the capture + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); + + // Capture Status + SECTION("hipStreamGetCaptureInfo with null stream after End capture") { + ret = hipStreamGetCaptureInfo(0, &captureStatus2, &capSequenceID1); + REQUIRE(ret == hipSuccess); + } + SECTION("hipStreamGetCaptureInfo_v2 with null stream after End capture") { + ret = hipStreamGetCaptureInfo_v2(0, &captureStatus2, &capSequenceID1, + nullptr, nullptr, nullptr); + REQUIRE(ret == hipSuccess); + } + // Launch graph + hipGraphExec_t graphExec; + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + + // Verify Output + for (size_t i = 0; i < N; i++) { + D_h[i] = A_h[i] * A_h[i]; + REQUIRE(C_h[i] == D_h[i]); + } + + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); + free(A_h); + free(C_h); + free(D_h); +} \ No newline at end of file diff --git a/catch/unit/graph/hipStreamGetCaptureInfo_v2.cc b/catch/unit/graph/hipStreamGetCaptureInfo_v2.cc index d19e059083..5eddd04655 100644 --- a/catch/unit/graph/hipStreamGetCaptureInfo_v2.cc +++ b/catch/unit/graph/hipStreamGetCaptureInfo_v2.cc @@ -17,90 +17,74 @@ OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - -Testcase Scenarios ------------------- -Functional: -1) Start stream capture and get capture info v2. Verify api is success, capture status is hipStreamCaptureStatusActive, - identifier returned is valid/non-zero, graph object is returned. -2) When stream capture is in progress, create dependent nodes by creating multistream dependencies and verify the api returns - valid dependent nodes. -3) End stream capture and get capture info. Verify api is success, capture status is hipStreamCaptureStatusNone and - identifier/graph/nodes are not returned by api. -4) When optional parameters are not passed, make sure api still returns capture status of stream. -5) Begin capture on hipStreamPerThread, get capture info v2 and validate results. -6) Perform multiple captures and verify the identifier returned is unique. - -Parameter Validation/Negative: -1) Capture Status location as nullptr and verify api returns error code. -2) Stream as nullptr and verify api returns error code. - -*/ - -#include #include #include +#include -constexpr size_t N = 1000000; -constexpr int LAUNCH_ITERS = 1; +#include "stream_capture_common.hh" /** - * Validates stream capture infov2, launches graph and verifies results + * @addtogroup hipStreamGetCaptureInfo_v2 hipStreamGetCaptureInfo_v2 + * @{ + * @ingroup GraphTest + * `hipStreamGetCaptureInfo_v2(hipStream_t stream, hipStreamCaptureStatus + * *captureStatus_out, unsigned long long *id_out __dparm(0), hipGraph_t + * *graph_out __dparm(0), const hipGraphNode_t **dependencies_out __dparm(0), + * size_t *numDependencies_out __dparm(0)))` - Get stream's capture state */ -void validateStreamCaptureInfoV2(hipStream_t mstream) { - hipStream_t stream1{nullptr}, stream2{nullptr}, streamForLaunch{nullptr}; - hipEvent_t memcpyEvent1, memsetEvent2, forkStreamEvent; + +void checkStreamCaptureInfo_v2(hipStreamCaptureMode mode, hipStream_t stream) { + constexpr size_t N = 1000000; + size_t Nbytes = N * sizeof(float); + hipGraph_t graph{nullptr}, capInfoGraph{nullptr}; hipGraphExec_t graphExec{nullptr}; - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - const hipGraphNode_t *nodelist{}; - size_t Nbytes = N * sizeof(float), numDependencies; - float *A_d, *C_d; - float *A_h, *C_h; - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - HIP_CHECK(hipStreamCreate(&streamForLaunch)); + const hipGraphNode_t* nodelist{}; + int numDepsCreated = 0; + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + hipGraphNodeType type(hipGraphNodeTypeEmpty); + unsigned long long capSequenceID = 0; // NOLINT + size_t numDependencies; - // Initialize input buffer - for (size_t i = 0; i < N; ++i) { - A_h[i] = 3.146f + i; // Pi + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard B_d(LinearAllocs::hipMalloc, Nbytes); + + EventsGuard events_guard(3); + StreamsGuard streams_guard(2); + + SECTION("Linear sequence graph") { + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + captureSequenceLinear(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream); + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, &capSequenceID, &capInfoGraph, + &nodelist, &numDependencies)); + numDepsCreated = 1; + HIP_CHECK(hipGraphNodeGetType(nodelist[0], &type)); + if ((type != hipGraphNodeTypeMemset) && (type != hipGraphNodeTypeMemcpy)) { + INFO("Type0 returned as " << type); + REQUIRE(false); + } } - // Create cross stream dependencies. - // memset/memcpy operations are done on stream1 and stream2 - // and they are joined back to mainstream - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipEventCreate(&memcpyEvent1)); - HIP_CHECK(hipEventCreate(&memsetEvent2)); - HIP_CHECK(hipEventCreate(&forkStreamEvent)); - - HIP_CHECK(hipStreamBeginCapture(mstream, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(forkStreamEvent, mstream)); - HIP_CHECK(hipStreamWaitEvent(stream1, forkStreamEvent, 0)); - HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); - HIP_CHECK(hipMemsetAsync(A_d, 0, Nbytes, stream1)); - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream1)); - HIP_CHECK(hipEventRecord(memcpyEvent1, stream1)); - HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream2)); - HIP_CHECK(hipEventRecord(memsetEvent2, stream2)); - HIP_CHECK(hipStreamWaitEvent(mstream, memcpyEvent1, 0)); - HIP_CHECK(hipStreamWaitEvent(mstream, memsetEvent2, 0)); - - hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; - unsigned long long capSequenceID = 0; // NOLINT - constexpr int numDepsCreated = 2; // Num of dependencies created - - HIP_CHECK(hipStreamGetCaptureInfo_v2(mstream, &captureStatus, - &capSequenceID, &capInfoGraph, &nodelist, &numDependencies)); + SECTION("Branched sequence graph") { + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + captureSequenceBranched(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream, + streams_guard.stream_list(), events_guard.event_list()); + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, &capSequenceID, &capInfoGraph, + &nodelist, &numDependencies)); + numDepsCreated = 2; + HIP_CHECK(hipGraphNodeGetType(nodelist[0], &type)); + if ((type != hipGraphNodeTypeMemset) && (type != hipGraphNodeTypeMemcpy)) { + INFO("Type0 returned as " << type); + REQUIRE(false); + } + HIP_CHECK(hipGraphNodeGetType(nodelist[1], &type)); + if ((type != hipGraphNodeTypeMemset) && (type != hipGraphNodeTypeMemcpy)) { + INFO("Type1 returned as " << type); + REQUIRE(false); + } + } // verify capture status is active, sequence id is valid, graph is returned, REQUIRE(captureStatus == hipStreamCaptureStatusActive); @@ -108,27 +92,10 @@ void validateStreamCaptureInfoV2(hipStream_t mstream) { REQUIRE(capInfoGraph != nullptr); REQUIRE(numDependencies == numDepsCreated); - // verify dependency nodes list returned is the one we created. - hipGraphNodeType type(hipGraphNodeTypeEmpty); - - HIP_CHECK(hipGraphNodeGetType(nodelist[0], &type)); - if ((type != hipGraphNodeTypeMemset) && (type != hipGraphNodeTypeMemcpy)) { - INFO("Type0 returned as " << type); - REQUIRE(false); - } - - HIP_CHECK(hipGraphNodeGetType(nodelist[1], &type)); - if ((type != hipGraphNodeTypeMemset) && (type != hipGraphNodeTypeMemcpy)) { - INFO("Type1 returned as " << type); - REQUIRE(false); - } - - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, mstream, A_d, C_d, N); + captureSequenceCompute(A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream); // End capture and verify graph is returned - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, mstream)); - HIP_CHECK(hipStreamEndCapture(mstream, &graph)); + HIP_CHECK(hipStreamEndCapture(stream, &graph)); REQUIRE(graph != nullptr); // verify capture status is inactive and other params are not updated @@ -136,8 +103,8 @@ void validateStreamCaptureInfoV2(hipStream_t mstream) { capInfoGraph = nullptr; numDependencies = 0; nodelist = nullptr; - HIP_CHECK(hipStreamGetCaptureInfo_v2(mstream, &captureStatus, - &capSequenceID, &capInfoGraph, &nodelist, &numDependencies)); + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, &capSequenceID, &capInfoGraph, + &nodelist, &numDependencies)); REQUIRE(captureStatus == hipStreamCaptureStatusNone); REQUIRE(capSequenceID == 0); REQUIRE(capInfoGraph == nullptr); @@ -145,88 +112,88 @@ void validateStreamCaptureInfoV2(hipStream_t mstream) { REQUIRE(numDependencies == 0); // Verify api still returns capture status when optional args are not passed - HIP_CHECK(hipStreamGetCaptureInfo_v2(mstream, &captureStatus)); + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus)); REQUIRE(captureStatus == hipStreamCaptureStatusNone); HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); REQUIRE(graphExec != nullptr); // Replay the recorded sequence multiple times - for (int i = 0; i < LAUNCH_ITERS; i++) { - HIP_CHECK(hipGraphLaunch(graphExec, streamForLaunch)); + for (int i = 0; i < kLaunchIters; i++) { + 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); } - HIP_CHECK(hipStreamSynchronize(streamForLaunch)); - + HIP_CHECK(hipGraphExecDestroy(graphExec)) HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForLaunch)); - HIP_CHECK(hipStreamDestroy(stream1)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipEventDestroy(forkStreamEvent)); - HIP_CHECK(hipEventDestroy(memcpyEvent1)); - HIP_CHECK(hipEventDestroy(memsetEvent2)); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); - - // Validate the computation - for (size_t i = 0; i < N; i++) { - if (C_h[i] != A_h[i] * A_h[i]) { - INFO("A and C not matching at " << i << " C_h[i] " << C_h[i] - << " A_h[i] " << A_h[i]); - REQUIRE(false); - } - } - free(A_h); - free(C_h); } /** - * Basic Functional Test for stream capture and getting capture info V2. - * Regular/custom stream is used for stream capture. + * Test Description + * ------------------------ + * - Test to verify that hipStreamCaptureStatusActive is returned during + * stream capture, correct number of created dependencies is returned and + * sequence ID is valid. When capture is ended, status is changed to + * hipStreamCaptureStatusNone and error is not reported when some arguments are + * not passed. + * -# Sequence graph is linear, number of created dependencies is 1, node + * type is correct + * -# Sequence graph is branched, number of created dependencies is 2, + * node types are correct + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamGetCaptureInfo_v2.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_BasicFunctional") { - hipStream_t streamForCapture; +TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_Positive_Functional") { + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); - HIP_CHECK(hipStreamCreate(&streamForCapture)); - validateStreamCaptureInfoV2(streamForCapture); - HIP_CHECK(hipStreamDestroy(streamForCapture)); + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + + checkStreamCaptureInfo_v2(captureMode, stream); } /** - * Test performs stream capture on hipStreamPerThread and validates - * capture info V2. + * Test Description + * ------------------------ + * - Test to verify stream capture on multiple streams and verifies + * uniqueness of identifiers returned from capture Info V2: + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamGetCaptureInfo_v2.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_hipStreamPerThread") { - validateStreamCaptureInfoV2(hipStreamPerThread); -} - -/** - * Test starts stream capture on multiple streams and verifies uniqueness of - * identifiers returned from capture Info V2. - */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_UniqueID") { +TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_Positive_UniqueID") { constexpr int numStreams = 100; - hipStream_t streams[numStreams]{}; hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; std::vector idlist; unsigned long long capSequenceID{}; // NOLINT hipGraph_t graph{nullptr}; + StreamsGuard streams(numStreams); + for (int i = 0; i < numStreams; i++) { - HIP_CHECK(hipStreamCreate(&streams[i])); HIP_CHECK(hipStreamBeginCapture(streams[i], hipStreamCaptureModeGlobal)); - HIP_CHECK(hipStreamGetCaptureInfo_v2(streams[i], &captureStatus, - &capSequenceID, nullptr, nullptr, nullptr)); + HIP_CHECK(hipStreamGetCaptureInfo_v2(streams[i], &captureStatus, &capSequenceID, nullptr, + nullptr, nullptr)); REQUIRE(captureStatus == hipStreamCaptureStatusActive); REQUIRE(capSequenceID > 0); idlist.push_back(capSequenceID); } for (int i = 0; i < numStreams; i++) { - for (int j = i+1; j < numStreams; j++) { + for (int j = i + 1; j < numStreams; j++) { if (idlist[i] == idlist[j]) { - INFO("Same identifier returned for stream " - << i << " and stream " << j); + INFO("Same identifier returned for stream " << i << " and stream " << j); REQUIRE(false); } } @@ -235,46 +202,58 @@ TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_UniqueID") { for (int i = 0; i < numStreams; i++) { HIP_CHECK(hipStreamEndCapture(streams[i], &graph)); HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streams[i])); } } /** - * Parameter validation/Negative tests for api + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# Capture status is nullptr + * -# Capture status checked on legacy/null stream + * -# Capture status when stream is uninitialized + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamGetCaptureInfo_v2.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_ParamValidation") { - hipError_t ret; - hipStream_t stream; - float *A_d; - hipGraph_t graph{}, capInfoGraph{}; +TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_Negative_Parameters") { + hipGraph_t capInfoGraph{}; hipStreamCaptureStatus captureStatus; unsigned long long capSequenceID; // NOLINT size_t numDependencies; - const hipGraphNode_t *nodelist{}; - constexpr int numBytes{100}; + const hipGraphNode_t* nodelist{}; - HIP_CHECK(hipMalloc(&A_d, numBytes)); - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipMemsetAsync(A_d, 0, numBytes, stream)); + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); SECTION("Capture Status location as nullptr") { - ret = hipStreamGetCaptureInfo_v2(stream, nullptr, - &capSequenceID, &capInfoGraph, &nodelist, &numDependencies); - REQUIRE(ret == hipErrorInvalidValue); + HIP_CHECK_ERROR(hipStreamGetCaptureInfo_v2(stream, nullptr, &capSequenceID, &capInfoGraph, + &nodelist, &numDependencies), + hipErrorInvalidValue); } - - SECTION("Stream as nullptr") { - ret = hipStreamGetCaptureInfo_v2(nullptr, &captureStatus, - &capSequenceID, &capInfoGraph, &nodelist, &numDependencies); - if ((ret != hipErrorUnknown) && (ret != hipErrorStreamCaptureImplicit)) { - INFO("Ret : " << ret); - REQUIRE(false); - } +#if HT_NVIDIA // EXSWHTEC-216, EXSWHTEC-228 + SECTION("Capture status when checked on null stream") { + hipGraph_t graph{nullptr}; + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + HIP_CHECK_ERROR(hipStreamGetCaptureInfo_v2(nullptr, &captureStatus, &capSequenceID, + &capInfoGraph, &nodelist, &numDependencies), + hipErrorStreamCaptureImplicit); + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + HIP_CHECK(hipGraphDestroy(graph)); } + SECTION("Capture status when stream is uninitialized") { + constexpr auto InvalidStream = [] { + StreamGuard sg(Streams::created); + return sg.stream(); + }; - HIP_CHECK(hipStreamEndCapture(stream, &graph)); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream)); - HIP_CHECK(hipFree(A_d)); -} + HIP_CHECK_ERROR(hipStreamGetCaptureInfo_v2(InvalidStream(), &captureStatus, &capSequenceID, + &capInfoGraph, &nodelist, &numDependencies), + hipErrorContextIsDestroyed); + } +#endif +} \ No newline at end of file diff --git a/catch/unit/graph/hipStreamGetCaptureInfo_v2_old.cc b/catch/unit/graph/hipStreamGetCaptureInfo_v2_old.cc new file mode 100644 index 0000000000..47a168b02c --- /dev/null +++ b/catch/unit/graph/hipStreamGetCaptureInfo_v2_old.cc @@ -0,0 +1,280 @@ +/* +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. +*/ + +/** + +Testcase Scenarios +------------------ +Functional: +1) Start stream capture and get capture info v2. Verify api is success, capture status is hipStreamCaptureStatusActive, + identifier returned is valid/non-zero, graph object is returned. +2) When stream capture is in progress, create dependent nodes by creating multistream dependencies and verify the api returns + valid dependent nodes. +3) End stream capture and get capture info. Verify api is success, capture status is hipStreamCaptureStatusNone and + identifier/graph/nodes are not returned by api. +4) When optional parameters are not passed, make sure api still returns capture status of stream. +5) Begin capture on hipStreamPerThread, get capture info v2 and validate results. +6) Perform multiple captures and verify the identifier returned is unique. + +Parameter Validation/Negative: +1) Capture Status location as nullptr and verify api returns error code. +2) Stream as nullptr and verify api returns error code. + +*/ + +#include +#include +#include + +constexpr size_t N = 1000000; +constexpr int LAUNCH_ITERS = 1; + +/** + * Validates stream capture infov2, launches graph and verifies results + */ +void validateStreamCaptureInfoV2(hipStream_t mstream) { + hipStream_t stream1{nullptr}, stream2{nullptr}, streamForLaunch{nullptr}; + hipEvent_t memcpyEvent1, memsetEvent2, forkStreamEvent; + hipGraph_t graph{nullptr}, capInfoGraph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + const hipGraphNode_t *nodelist{}; + size_t Nbytes = N * sizeof(float), numDependencies; + float *A_d, *C_d; + float *A_h, *C_h; + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + HIP_CHECK(hipStreamCreate(&streamForLaunch)); + + // Initialize input buffer + for (size_t i = 0; i < N; ++i) { + A_h[i] = 3.146f + i; // Pi + } + + // Create cross stream dependencies. + // memset/memcpy operations are done on stream1 and stream2 + // and they are joined back to mainstream + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipEventCreate(&memcpyEvent1)); + HIP_CHECK(hipEventCreate(&memsetEvent2)); + HIP_CHECK(hipEventCreate(&forkStreamEvent)); + + HIP_CHECK(hipStreamBeginCapture(mstream, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(forkStreamEvent, mstream)); + HIP_CHECK(hipStreamWaitEvent(stream1, forkStreamEvent, 0)); + HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); + HIP_CHECK(hipMemsetAsync(A_d, 0, Nbytes, stream1)); + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream1)); + HIP_CHECK(hipEventRecord(memcpyEvent1, stream1)); + HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream2)); + HIP_CHECK(hipEventRecord(memsetEvent2, stream2)); + HIP_CHECK(hipStreamWaitEvent(mstream, memcpyEvent1, 0)); + HIP_CHECK(hipStreamWaitEvent(mstream, memsetEvent2, 0)); + + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + unsigned long long capSequenceID = 0; // NOLINT + constexpr int numDepsCreated = 2; // Num of dependencies created + + HIP_CHECK(hipStreamGetCaptureInfo_v2(mstream, &captureStatus, + &capSequenceID, &capInfoGraph, &nodelist, &numDependencies)); + + // verify capture status is active, sequence id is valid, graph is returned, + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + REQUIRE(capSequenceID > 0); + REQUIRE(capInfoGraph != nullptr); + REQUIRE(numDependencies == numDepsCreated); + + // verify dependency nodes list returned is the one we created. + hipGraphNodeType type(hipGraphNodeTypeEmpty); + + HIP_CHECK(hipGraphNodeGetType(nodelist[0], &type)); + if ((type != hipGraphNodeTypeMemset) && (type != hipGraphNodeTypeMemcpy)) { + INFO("Type0 returned as " << type); + REQUIRE(false); + } + + HIP_CHECK(hipGraphNodeGetType(nodelist[1], &type)); + if ((type != hipGraphNodeTypeMemset) && (type != hipGraphNodeTypeMemcpy)) { + INFO("Type1 returned as " << type); + REQUIRE(false); + } + + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, mstream, A_d, C_d, N); + + // End capture and verify graph is returned + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, mstream)); + HIP_CHECK(hipStreamEndCapture(mstream, &graph)); + REQUIRE(graph != nullptr); + + // verify capture status is inactive and other params are not updated + capSequenceID = 0; + capInfoGraph = nullptr; + numDependencies = 0; + nodelist = nullptr; + HIP_CHECK(hipStreamGetCaptureInfo_v2(mstream, &captureStatus, + &capSequenceID, &capInfoGraph, &nodelist, &numDependencies)); + REQUIRE(captureStatus == hipStreamCaptureStatusNone); + REQUIRE(capSequenceID == 0); + REQUIRE(capInfoGraph == nullptr); + REQUIRE(nodelist == nullptr); + REQUIRE(numDependencies == 0); + + // Verify api still returns capture status when optional args are not passed + HIP_CHECK(hipStreamGetCaptureInfo_v2(mstream, &captureStatus)); + REQUIRE(captureStatus == hipStreamCaptureStatusNone); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + REQUIRE(graphExec != nullptr); + + // Replay the recorded sequence multiple times + for (int i = 0; i < LAUNCH_ITERS; i++) { + HIP_CHECK(hipGraphLaunch(graphExec, streamForLaunch)); + } + + HIP_CHECK(hipStreamSynchronize(streamForLaunch)); + + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(streamForLaunch)); + HIP_CHECK(hipStreamDestroy(stream1)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipEventDestroy(forkStreamEvent)); + HIP_CHECK(hipEventDestroy(memcpyEvent1)); + HIP_CHECK(hipEventDestroy(memsetEvent2)); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); + + // Validate the computation + for (size_t i = 0; i < N; i++) { + if (C_h[i] != A_h[i] * A_h[i]) { + INFO("A and C not matching at " << i << " C_h[i] " << C_h[i] + << " A_h[i] " << A_h[i]); + REQUIRE(false); + } + } + free(A_h); + free(C_h); +} + +/** + * Basic Functional Test for stream capture and getting capture info V2. + * Regular/custom stream is used for stream capture. + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_BasicFunctional") { + hipStream_t streamForCapture; + + HIP_CHECK(hipStreamCreate(&streamForCapture)); + validateStreamCaptureInfoV2(streamForCapture); + HIP_CHECK(hipStreamDestroy(streamForCapture)); +} + +/** + * Test performs stream capture on hipStreamPerThread and validates + * capture info V2. + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_hipStreamPerThread") { + validateStreamCaptureInfoV2(hipStreamPerThread); +} + +/** + * Test starts stream capture on multiple streams and verifies uniqueness of + * identifiers returned from capture Info V2. + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_UniqueID") { + constexpr int numStreams = 100; + hipStream_t streams[numStreams]{}; + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + std::vector idlist; + unsigned long long capSequenceID{}; // NOLINT + hipGraph_t graph{nullptr}; + + for (int i = 0; i < numStreams; i++) { + HIP_CHECK(hipStreamCreate(&streams[i])); + HIP_CHECK(hipStreamBeginCapture(streams[i], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamGetCaptureInfo_v2(streams[i], &captureStatus, + &capSequenceID, nullptr, nullptr, nullptr)); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + REQUIRE(capSequenceID > 0); + idlist.push_back(capSequenceID); + } + + for (int i = 0; i < numStreams; i++) { + for (int j = i+1; j < numStreams; j++) { + if (idlist[i] == idlist[j]) { + INFO("Same identifier returned for stream " + << i << " and stream " << j); + REQUIRE(false); + } + } + } + + for (int i = 0; i < numStreams; i++) { + HIP_CHECK(hipStreamEndCapture(streams[i], &graph)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(streams[i])); + } +} + +/** + * Parameter validation/Negative tests for api + */ +TEST_CASE("Unit_hipStreamGetCaptureInfo_v2_ParamValidation") { + hipError_t ret; + hipStream_t stream; + float *A_d; + hipGraph_t graph{}, capInfoGraph{}; + hipStreamCaptureStatus captureStatus; + unsigned long long capSequenceID; // NOLINT + size_t numDependencies; + const hipGraphNode_t *nodelist{}; + constexpr int numBytes{100}; + + HIP_CHECK(hipMalloc(&A_d, numBytes)); + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipMemsetAsync(A_d, 0, numBytes, stream)); + + SECTION("Capture Status location as nullptr") { + ret = hipStreamGetCaptureInfo_v2(stream, nullptr, + &capSequenceID, &capInfoGraph, &nodelist, &numDependencies); + REQUIRE(ret == hipErrorInvalidValue); + } + + SECTION("Stream as nullptr") { + ret = hipStreamGetCaptureInfo_v2(nullptr, &captureStatus, + &capSequenceID, &capInfoGraph, &nodelist, &numDependencies); + if ((ret != hipErrorUnknown) && (ret != hipErrorStreamCaptureImplicit)) { + INFO("Ret : " << ret); + REQUIRE(false); + } + } + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK(hipFree(A_d)); +} \ No newline at end of file diff --git a/catch/unit/graph/hipStreamIsCapturing.cc b/catch/unit/graph/hipStreamIsCapturing.cc index 08f59284c8..1c681b4243 100644 --- a/catch/unit/graph/hipStreamIsCapturing.cc +++ b/catch/unit/graph/hipStreamIsCapturing.cc @@ -18,419 +18,195 @@ THE SOFTWARE. */ #include +#include #include -constexpr unsigned blocks = 512; -constexpr unsigned threadsPerBlock = 256; -constexpr size_t N = 100000; -constexpr size_t Nbytes = N * sizeof(float); +#include "stream_capture_common.hh" /** -API - hipStreamIsCapturing -Negative Testcase Scenarios : Negative - 1) Check capture status with null pCaptureStatus. - 2) Check capture status with hipStreamPerThread and null pCaptureStatus. -Functional Testcase Scenarios : - 1) Check capture status with null stream. - 2) Check capture status with hipStreamPerThread. - 3) Functional : Create a stream, call api and check - capture status is hipStreamCaptureStatusNone. - 4) Functional : Start capturing a stream and check - capture status returned as hipStreamCaptureStatusActive. - 5) Functional : Stop capturing a stream and check - status is returned as hipStreamCaptureStatusNone. - 6) Functional : Use hipStreamPerThread, call api and check - capture status is hipStreamCaptureStatusNone. - 7) Functional : Start capturing using hipStreamPerThread and check - capture status returned as hipStreamCaptureStatusActive. - 8) Functional : Stop capturing using hipStreamPerThread and check - status is returned as hipStreamCaptureStatusNone. - 9) Functional : Create 2 streams s1 and s2. Start capturing s1. Record event e1 - on s1 and wait for event e1 on s2. Queue some operations in s1 and s2. Invoke - hipStreamIsCapturing on both s1 and s2. Verify that the capture info (status) - of both s1 and s2 are identical. Record event e2 on s2 and wait for event e2 - on s1. End the capture of stream s1. Invoke hipStreamIsCapturing on both streams. - Verify that the capture info(status)of both s1 and s2 are identical - 10)Functional : Create a stream s1. Start capturing s1. Get the capture info using - hipStreamIsCapturing of s1. Launch a thread. In the thread get the capture info - of s1 using hipStreamIsCapturing. Verify that it is in state hipStreamCaptureStatusActive - in thread. Exit the thread and end the capture. - 11)Functional : Create a stream with default flag (hipStreamDefault). Start capturing - the stream. Invoke hipStreamIsCapturing() on the null stream. Verify hipErrorStreamCaptureImplicit - is returned by hipStreamIsCapturing(). Verify capture status of created stream. Do some operatoins. - End the capture on the created stream. Execute the graph and verify the output from the operations. -*/ + * @addtogroup hipStreamIsCapturing hipStreamIsCapturing + * @{ + * @ingroup GraphTest + * `hipStreamIsCapturing(hipStream_t stream, hipStreamCaptureStatus + * *pCaptureStatus)` - get stream's capture state + */ -TEST_CASE("Unit_hipStreamIsCapturing_Negative") { - hipError_t ret; - hipStream_t stream{}; +/** + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# Capture status is nullptr + * -# Capture status is checked on null stream + * -# Stream is uninitialized + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamIsCapturing.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamIsCapturing_Negative_Parameters") { + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); SECTION("Check capture status with null pCaptureStatus.") { - ret = hipStreamIsCapturing(stream, nullptr); - REQUIRE(hipErrorInvalidValue == ret); + HIP_CHECK_ERROR(hipStreamIsCapturing(stream, nullptr), hipErrorInvalidValue); } - SECTION("Check capture status with hipStreamPerThread and" - " nullptr as pCaptureStatus.") { - ret = hipStreamIsCapturing(hipStreamPerThread, nullptr); - REQUIRE(hipErrorInvalidValue == ret); - } -} -TEST_CASE("Unit_hipStreamIsCapturing_Functional_Basic") { - hipStreamCaptureStatus cStatus; + SECTION("Check capture status when checked on null stream") { + hipStreamCaptureStatus cStatus; + hipGraph_t graph{nullptr}; - SECTION("Check capture status with null stream.") { - HIP_CHECK(hipStreamIsCapturing(nullptr, &cStatus)); - REQUIRE(hipStreamCaptureStatusNone == cStatus); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + HIP_CHECK_ERROR(hipStreamIsCapturing(nullptr, &cStatus), hipErrorStreamCaptureImplicit); + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + HIP_CHECK(hipGraphDestroy(graph)); } - SECTION("Check capture status with hipStreamPerThread.") { - HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); - REQUIRE(hipStreamCaptureStatusNone == cStatus); +#if HT_NVIDIA // EXSWHTEC-216 + SECTION("Check capture status when stream is uninitialized") { + hipStreamCaptureStatus cStatus; + + constexpr auto InvalidStream = [] { + StreamGuard sg(Streams::created); + return sg.stream(); + }; + + HIP_CHECK_ERROR(hipStreamIsCapturing(InvalidStream(), &cStatus), hipErrorContextIsDestroyed); } +#endif } /** -Testcase Scenarios : - 1) Functional : Create a stream, call api and check - capture status is hipStreamCaptureStatusNone. - 2) Functional : Start capturing a stream and check - capture status returned as hipStreamCaptureStatusActive. - 3) Functional : Stop capturing a stream and check - status is returned as hipStreamCaptureStatusNone. -*/ - -TEST_CASE("Unit_hipStreamIsCapturing_Functional") { - float *A_d, *C_d; - float *A_h, *C_h; - hipStream_t stream{nullptr}; - hipGraph_t graph{nullptr}; + * Test Description + * ------------------------ + * - Initiate simple API call for stream capture status on custom + * stream/hipStreamPerThread + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamIsCapturing.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamIsCapturing_Positive_Basic") { hipStreamCaptureStatus cStatus; + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); +} - // Fill with Phi + i - for (size_t i = 0; i < N; i++) { - A_h[i] = 1.618f + i; - } +void checkStreamCaptureStatus(hipStreamCaptureMode mode, hipStream_t stream) { + constexpr size_t N = 1000000; - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - HIP_CHECK(hipStreamCreate(&stream)); + hipStreamCaptureStatus cStatus; + size_t Nbytes = N * sizeof(float); + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; - SECTION("Check the stream capture status before start capturing.") { - HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); - REQUIRE(hipStreamCaptureStatusNone == cStatus); - } + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + // Status is none before capture begins + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); - SECTION("Start capturing a stream and check the status.") { - HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); - REQUIRE(hipStreamCaptureStatusActive == cStatus); - } + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), N, stream); - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); - - HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + // Status is active during stream capture + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusActive == cStatus); HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); - SECTION("Stop capturing a stream and check the status.") { - HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); - REQUIRE(hipStreamCaptureStatusNone == cStatus); + // Status is none after capture ends + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + REQUIRE(graphExec != nullptr); + + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + 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), N); } - HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipGraphExecDestroy(graphExec)) HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream)); - - free(A_h); - free(C_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); } /** -Testcase Scenarios : - 1) Functional : Use hipStreamPerThread, call api and check - capture status is hipStreamCaptureStatusNone. - 2) Functional : Start capturing using hipStreamPerThread and check - capture status returned as hipStreamCaptureStatusActive. - 3) Functional : Stop capturing using hipStreamPerThread and check - status is returned as hipStreamCaptureStatusNone. -*/ + * Test Description + * ------------------------ + * - Initiate stream capture with different modes on custom + * stream/hipStreamPerThread. Check that capture status is correct in different + * capturing phases + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamIsCapturing.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamIsCapturing_Positive_Functional") { + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); -TEST_CASE("Unit_hipStreamIsCapturing_hipStreamPerThread") { - float *A_d, *C_d; - float *A_h, *C_h; - hipGraph_t graph{nullptr}; - hipStreamCaptureStatus cStatus; + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - - // Fill with Phi + i - for (size_t i = 0; i < N; i++) { - A_h[i] = 1.618f + i; - } - - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - - SECTION("Check the stream capture status before start capturing.") { - HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); - REQUIRE(hipStreamCaptureStatusNone == cStatus); - } - - HIP_CHECK(hipStreamBeginCapture(hipStreamPerThread, - hipStreamCaptureModeGlobal)); - - SECTION("Start capturing a stream and check the status.") { - HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); - REQUIRE(hipStreamCaptureStatusActive == cStatus); - } - - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, - hipStreamPerThread)); - - HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, hipStreamPerThread)); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, hipStreamPerThread, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, - hipStreamPerThread)); - - HIP_CHECK(hipStreamEndCapture(hipStreamPerThread, &graph)); - - SECTION("Stop capturing a stream and check the status.") { - HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); - REQUIRE(hipStreamCaptureStatusNone == cStatus); - } - - HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); - HIP_CHECK(hipGraphDestroy(graph)); - - free(A_h); - free(C_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); + checkStreamCaptureStatus(captureMode, stream); } -/* -* Create 2 streams s1 and s2. Start capturing s1. Record event e1 on s1 and wait -* for event e1 on s2. Queue some operations in s1 and s2. Invoke hipStreamIsCapturing -* on both s1 and s2. Verify that the capture info (status) of both s1 and s2 are identical. -* Record event e2 on s2 and wait for event e2 on s1. End the capture of stream s1. -* Invoke hipStreamIsCapturing on both streams. Verify that the capture info(status) -* of both s1 and s2 are identical. -*/ -TEST_CASE("Unit_hipStreamIsCapturing_ParentAndForkedStream") { - hipStream_t stream1{nullptr}, stream2{nullptr}; - hipEvent_t event2{nullptr}, forkStreamEvent{nullptr}; - hipGraph_t graph{nullptr}; - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - size_t Nbytes = N * sizeof(float); - float *A_d, *B_d, *C_d, *D_d; - float *A_h, *B_h, *C_h, *D_h; - // Memory allocation to Host pointers - A_h = reinterpret_cast(malloc(Nbytes)); - B_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - D_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(B_h != nullptr); - REQUIRE(C_h != nullptr); - REQUIRE(D_h != nullptr); - // Memory allocation to Device pointers - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&B_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - HIP_CHECK(hipMalloc(&D_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(B_d != nullptr); - REQUIRE(C_d != nullptr); - REQUIRE(D_d != nullptr); - // Initialize input buffer - for (size_t i = 0; i < N; ++i) { - A_h[i] = 3.146f + i; // Pi - B_h[i] = A_h[i]; - } - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipEventCreate(&event2)); - HIP_CHECK(hipEventCreate(&forkStreamEvent)); - // Start capture on stream1 - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); - // Copy data to Device - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream1)); - HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream2)); - // Kernal Operations - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream1, A_d, C_d, N); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream2, B_d, D_d, N); - // Copy data back to the Host - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream1)); - HIP_CHECK(hipMemcpyAsync(D_h, D_d, Nbytes, hipMemcpyDeviceToHost, stream2)); - - hipStreamCaptureStatus captureStatus1{hipStreamCaptureStatusNone}, - captureStatus2{hipStreamCaptureStatusNone}, - captureStatus3{hipStreamCaptureStatusNone}, - captureStatus4{hipStreamCaptureStatusNone}; - // Capturing info - HIP_CHECK(hipStreamIsCapturing(stream1, &captureStatus1)); - HIP_CHECK(hipStreamIsCapturing(stream2, &captureStatus2)); - // Verfication of results - REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); - REQUIRE(captureStatus2 == hipStreamCaptureStatusActive); - - HIP_CHECK(hipEventRecord(event2, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream1, event2, 0)); - // End the capture - HIP_CHECK(hipStreamEndCapture(stream1, &graph)); - REQUIRE(graph != nullptr); - - // Capture Info - HIP_CHECK(hipStreamIsCapturing(stream1, &captureStatus3)); - HIP_CHECK(hipStreamIsCapturing(stream2, &captureStatus4)); - // Verification of results - REQUIRE(captureStatus3 == hipStreamCaptureStatusNone); - REQUIRE(captureStatus4 == hipStreamCaptureStatusNone); - - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream1)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipEventDestroy(forkStreamEvent)); - HIP_CHECK(hipEventDestroy(event2)); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(B_d)); - HIP_CHECK(hipFree(C_d)); - HIP_CHECK(hipFree(D_d)); - free(A_h); - free(B_h); - free(C_h); - free(D_h); -} -/* -* Create a stream s1. Start capturing s1. Get the capture info using hipStreamIsCapturing -* of s1. Launch a thread. In the thread get the capture info of s1 using hipStreamIsCapturing. -* Verify that it is in state hipStreamCaptureStatusActive in thread. Exit the thread and end -* the capture. -*/ -// Thread Function static void thread_func(hipStream_t stream) { - hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; - HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus)); - REQUIRE(captureStatus == hipStreamCaptureStatusActive); + hipStreamCaptureStatus cStatus; + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusActive == cStatus); } -TEST_CASE("Unit_hipStreamIsCapturing_CheckCaptureStatus_FromThread") { - hipStream_t stream{nullptr}; - hipGraph_t graph{nullptr}; +/** + * Test Description + * ------------------------ + * - Initiate stream capture with different modes on custom + * stream/hipStreamPerThread. Check that capture status is correct when status + * is checked in a separate thread + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamIsCapturing.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamIsCapturing_Positive_Thread") { + constexpr size_t N = 1000000; + size_t Nbytes = N * sizeof(float); + + hipGraph_t graph{nullptr}; + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + + const hipStreamCaptureMode captureMode = hipStreamCaptureModeGlobal; + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), N, stream); - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - // Capture info - hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; - HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus)); - REQUIRE(captureStatus == hipStreamCaptureStatusActive); - // Thread launch std::thread t(thread_func, stream); t.join(); HIP_CHECK(hipStreamEndCapture(stream, &graph)); - REQUIRE(graph != nullptr); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream)); -} - -/* -* Create a stream with default flag (hipStreamDefault). Start capturing the stream. -* Invoke hipStreamIsCapturing() on the null stream. Verify hipErrorStreamCaptureImplicit -* is returned by hipStreamIsCapturing(). Verify capture status of created stream. Do some operatoins. -* End the capture on the created stream. Execute the graph and verify the output from the operations. -*/ -TEST_CASE("Unit_hipStreamIsCapturing_ChkNullStrmStatus") { - hipStream_t stream{nullptr}, streamForGraph{nullptr}; - hipGraph_t graph{nullptr}; - hipError_t ret; - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - float *A_d, *C_d; - float *A_h, *C_h, *D_h; - // Memory allocation to Host pointers - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - D_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - REQUIRE(D_h != nullptr); - - // Memory allocation to Device pointers - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - - // Initialize input buffer - for (size_t i = 0; i < N; ++i) { - A_h[i] = 1.0f + i; - D_h[i] = 0.0f; - } - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}, - captureStatus1{hipStreamCaptureStatusNone}, - captureStatus2{hipStreamCaptureStatusNone}; - // Verify the Error returned if null stream is passed. - ret = hipStreamIsCapturing(0, &captureStatus); - REQUIRE(ret == hipErrorStreamCaptureImplicit); - // Check the capture status of the stream - HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus1)); - REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); - // Copy data to Device - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); - // Kernal Operations - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); - // End the capture - HIP_CHECK(hipStreamEndCapture(stream, &graph)); - REQUIRE(graph != nullptr); - - ret = hipStreamIsCapturing(0, &captureStatus2); - REQUIRE(ret == hipSuccess); - - // Launch graph - hipGraphExec_t graphExec; - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); - // Verify Output - for (size_t i = 0; i < N; i++) { - D_h[i] = A_h[i] * A_h[i]; - REQUIRE(C_h[i] == D_h[i]); - } - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); - free(A_h); - free(C_h); - free(D_h); -} +} \ No newline at end of file diff --git a/catch/unit/graph/hipStreamIsCapturing_old.cc b/catch/unit/graph/hipStreamIsCapturing_old.cc new file mode 100644 index 0000000000..ba4634a394 --- /dev/null +++ b/catch/unit/graph/hipStreamIsCapturing_old.cc @@ -0,0 +1,436 @@ +/* +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 + +constexpr unsigned blocks = 512; +constexpr unsigned threadsPerBlock = 256; +constexpr size_t N = 100000; +constexpr size_t Nbytes = N * sizeof(float); + +/** +API - hipStreamIsCapturing +Negative Testcase Scenarios : Negative + 1) Check capture status with null pCaptureStatus. + 2) Check capture status with hipStreamPerThread and null pCaptureStatus. +Functional Testcase Scenarios : + 1) Check capture status with null stream. + 2) Check capture status with hipStreamPerThread. + 3) Functional : Create a stream, call api and check + capture status is hipStreamCaptureStatusNone. + 4) Functional : Start capturing a stream and check + capture status returned as hipStreamCaptureStatusActive. + 5) Functional : Stop capturing a stream and check + status is returned as hipStreamCaptureStatusNone. + 6) Functional : Use hipStreamPerThread, call api and check + capture status is hipStreamCaptureStatusNone. + 7) Functional : Start capturing using hipStreamPerThread and check + capture status returned as hipStreamCaptureStatusActive. + 8) Functional : Stop capturing using hipStreamPerThread and check + status is returned as hipStreamCaptureStatusNone. + 9) Functional : Create 2 streams s1 and s2. Start capturing s1. Record event e1 + on s1 and wait for event e1 on s2. Queue some operations in s1 and s2. Invoke + hipStreamIsCapturing on both s1 and s2. Verify that the capture info (status) + of both s1 and s2 are identical. Record event e2 on s2 and wait for event e2 + on s1. End the capture of stream s1. Invoke hipStreamIsCapturing on both streams. + Verify that the capture info(status)of both s1 and s2 are identical + 10)Functional : Create a stream s1. Start capturing s1. Get the capture info using + hipStreamIsCapturing of s1. Launch a thread. In the thread get the capture info + of s1 using hipStreamIsCapturing. Verify that it is in state hipStreamCaptureStatusActive + in thread. Exit the thread and end the capture. + 11)Functional : Create a stream with default flag (hipStreamDefault). Start capturing + the stream. Invoke hipStreamIsCapturing() on the null stream. Verify hipErrorStreamCaptureImplicit + is returned by hipStreamIsCapturing(). Verify capture status of created stream. Do some operatoins. + End the capture on the created stream. Execute the graph and verify the output from the operations. +*/ + +TEST_CASE("Unit_hipStreamIsCapturing_Negative") { + hipError_t ret; + hipStream_t stream{}; + + SECTION("Check capture status with null pCaptureStatus.") { + ret = hipStreamIsCapturing(stream, nullptr); + REQUIRE(hipErrorInvalidValue == ret); + } + SECTION("Check capture status with hipStreamPerThread and" + " nullptr as pCaptureStatus.") { + ret = hipStreamIsCapturing(hipStreamPerThread, nullptr); + REQUIRE(hipErrorInvalidValue == ret); + } +} + +TEST_CASE("Unit_hipStreamIsCapturing_Functional_Basic") { + hipStreamCaptureStatus cStatus; + + SECTION("Check capture status with null stream.") { + HIP_CHECK(hipStreamIsCapturing(nullptr, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); + } + SECTION("Check capture status with hipStreamPerThread.") { + HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); + } +} + +/** +Testcase Scenarios : + 1) Functional : Create a stream, call api and check + capture status is hipStreamCaptureStatusNone. + 2) Functional : Start capturing a stream and check + capture status returned as hipStreamCaptureStatusActive. + 3) Functional : Stop capturing a stream and check + status is returned as hipStreamCaptureStatusNone. +*/ + +TEST_CASE("Unit_hipStreamIsCapturing_Functional") { + float *A_d, *C_d; + float *A_h, *C_h; + hipStream_t stream{nullptr}; + hipGraph_t graph{nullptr}; + hipStreamCaptureStatus cStatus; + + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + + // Fill with Phi + i + for (size_t i = 0; i < N; i++) { + A_h[i] = 1.618f + i; + } + + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + HIP_CHECK(hipStreamCreate(&stream)); + + SECTION("Check the stream capture status before start capturing.") { + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); + } + + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + + SECTION("Start capturing a stream and check the status.") { + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusActive == cStatus); + } + + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + + HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + + SECTION("Stop capturing a stream and check the status.") { + HIP_CHECK(hipStreamIsCapturing(stream, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); + } + + HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); + + free(A_h); + free(C_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); +} + +/** +Testcase Scenarios : + 1) Functional : Use hipStreamPerThread, call api and check + capture status is hipStreamCaptureStatusNone. + 2) Functional : Start capturing using hipStreamPerThread and check + capture status returned as hipStreamCaptureStatusActive. + 3) Functional : Stop capturing using hipStreamPerThread and check + status is returned as hipStreamCaptureStatusNone. +*/ + +TEST_CASE("Unit_hipStreamIsCapturing_hipStreamPerThread") { + float *A_d, *C_d; + float *A_h, *C_h; + hipGraph_t graph{nullptr}; + hipStreamCaptureStatus cStatus; + + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + + // Fill with Phi + i + for (size_t i = 0; i < N; i++) { + A_h[i] = 1.618f + i; + } + + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + + SECTION("Check the stream capture status before start capturing.") { + HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); + } + + HIP_CHECK(hipStreamBeginCapture(hipStreamPerThread, + hipStreamCaptureModeGlobal)); + + SECTION("Start capturing a stream and check the status.") { + HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); + REQUIRE(hipStreamCaptureStatusActive == cStatus); + } + + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, + hipStreamPerThread)); + + HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, hipStreamPerThread)); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, hipStreamPerThread, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, + hipStreamPerThread)); + + HIP_CHECK(hipStreamEndCapture(hipStreamPerThread, &graph)); + + SECTION("Stop capturing a stream and check the status.") { + HIP_CHECK(hipStreamIsCapturing(hipStreamPerThread, &cStatus)); + REQUIRE(hipStreamCaptureStatusNone == cStatus); + } + + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + HIP_CHECK(hipGraphDestroy(graph)); + + free(A_h); + free(C_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); +} +/* +* Create 2 streams s1 and s2. Start capturing s1. Record event e1 on s1 and wait +* for event e1 on s2. Queue some operations in s1 and s2. Invoke hipStreamIsCapturing +* on both s1 and s2. Verify that the capture info (status) of both s1 and s2 are identical. +* Record event e2 on s2 and wait for event e2 on s1. End the capture of stream s1. +* Invoke hipStreamIsCapturing on both streams. Verify that the capture info(status) +* of both s1 and s2 are identical. +*/ +TEST_CASE("Unit_hipStreamIsCapturing_ParentAndForkedStream") { + hipStream_t stream1{nullptr}, stream2{nullptr}; + hipEvent_t event2{nullptr}, forkStreamEvent{nullptr}; + hipGraph_t graph{nullptr}; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + size_t Nbytes = N * sizeof(float); + float *A_d, *B_d, *C_d, *D_d; + float *A_h, *B_h, *C_h, *D_h; + // Memory allocation to Host pointers + A_h = reinterpret_cast(malloc(Nbytes)); + B_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + D_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(B_h != nullptr); + REQUIRE(C_h != nullptr); + REQUIRE(D_h != nullptr); + // Memory allocation to Device pointers + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&B_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + HIP_CHECK(hipMalloc(&D_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(B_d != nullptr); + REQUIRE(C_d != nullptr); + REQUIRE(D_d != nullptr); + + // Initialize input buffer + for (size_t i = 0; i < N; ++i) { + A_h[i] = 3.146f + i; // Pi + B_h[i] = A_h[i]; + } + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipEventCreate(&event2)); + HIP_CHECK(hipEventCreate(&forkStreamEvent)); + // Start capture on stream1 + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); + // Copy data to Device + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream1)); + HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream2)); + // Kernal Operations + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream1, A_d, C_d, N); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream2, B_d, D_d, N); + // Copy data back to the Host + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream1)); + HIP_CHECK(hipMemcpyAsync(D_h, D_d, Nbytes, hipMemcpyDeviceToHost, stream2)); + + hipStreamCaptureStatus captureStatus1{hipStreamCaptureStatusNone}, + captureStatus2{hipStreamCaptureStatusNone}, + captureStatus3{hipStreamCaptureStatusNone}, + captureStatus4{hipStreamCaptureStatusNone}; + // Capturing info + HIP_CHECK(hipStreamIsCapturing(stream1, &captureStatus1)); + HIP_CHECK(hipStreamIsCapturing(stream2, &captureStatus2)); + // Verfication of results + REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); + REQUIRE(captureStatus2 == hipStreamCaptureStatusActive); + + HIP_CHECK(hipEventRecord(event2, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream1, event2, 0)); + // End the capture + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); + REQUIRE(graph != nullptr); + + // Capture Info + HIP_CHECK(hipStreamIsCapturing(stream1, &captureStatus3)); + HIP_CHECK(hipStreamIsCapturing(stream2, &captureStatus4)); + // Verification of results + REQUIRE(captureStatus3 == hipStreamCaptureStatusNone); + REQUIRE(captureStatus4 == hipStreamCaptureStatusNone); + + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream1)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipEventDestroy(forkStreamEvent)); + HIP_CHECK(hipEventDestroy(event2)); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(B_d)); + HIP_CHECK(hipFree(C_d)); + HIP_CHECK(hipFree(D_d)); + free(A_h); + free(B_h); + free(C_h); + free(D_h); +} +/* +* Create a stream s1. Start capturing s1. Get the capture info using hipStreamIsCapturing +* of s1. Launch a thread. In the thread get the capture info of s1 using hipStreamIsCapturing. +* Verify that it is in state hipStreamCaptureStatusActive in thread. Exit the thread and end +* the capture. +*/ +// Thread Function +static void thread_func(hipStream_t stream) { + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus)); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); +} + +TEST_CASE("Unit_hipStreamIsCapturing_CheckCaptureStatus_FromThread") { + hipStream_t stream{nullptr}; + hipGraph_t graph{nullptr}; + + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + // Capture info + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus)); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + // Thread launch + std::thread t(thread_func, stream); + t.join(); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); +} + +/* +* Create a stream with default flag (hipStreamDefault). Start capturing the stream. +* Invoke hipStreamIsCapturing() on the null stream. Verify hipErrorStreamCaptureImplicit +* is returned by hipStreamIsCapturing(). Verify capture status of created stream. Do some operatoins. +* End the capture on the created stream. Execute the graph and verify the output from the operations. +*/ +TEST_CASE("Unit_hipStreamIsCapturing_ChkNullStrmStatus") { + hipStream_t stream{nullptr}, streamForGraph{nullptr}; + hipGraph_t graph{nullptr}; + hipError_t ret; + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamCreate(&streamForGraph)); + float *A_d, *C_d; + float *A_h, *C_h, *D_h; + // Memory allocation to Host pointers + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + D_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + REQUIRE(D_h != nullptr); + + // Memory allocation to Device pointers + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + + // Initialize input buffer + for (size_t i = 0; i < N; ++i) { + A_h[i] = 1.0f + i; + D_h[i] = 0.0f; + } + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}, + captureStatus1{hipStreamCaptureStatusNone}, + captureStatus2{hipStreamCaptureStatusNone}; + // Verify the Error returned if null stream is passed. + ret = hipStreamIsCapturing(0, &captureStatus); + REQUIRE(ret == hipErrorStreamCaptureImplicit); + // Check the capture status of the stream + HIP_CHECK(hipStreamIsCapturing(stream, &captureStatus1)); + REQUIRE(captureStatus1 == hipStreamCaptureStatusActive); + // Copy data to Device + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + // Kernal Operations + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + // End the capture + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); + + ret = hipStreamIsCapturing(0, &captureStatus2); + REQUIRE(ret == hipSuccess); + + // Launch graph + hipGraphExec_t graphExec; + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + // Verify Output + for (size_t i = 0; i < N; i++) { + D_h[i] = A_h[i] * A_h[i]; + REQUIRE(C_h[i] == D_h[i]); + } + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); + free(A_h); + free(C_h); + free(D_h); +} \ No newline at end of file diff --git a/catch/unit/graph/stream_capture_common.hh b/catch/unit/graph/stream_capture_common.hh new file mode 100644 index 0000000000..2e1fe9bfaa --- /dev/null +++ b/catch/unit/graph/stream_capture_common.hh @@ -0,0 +1,78 @@ +/* +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 +#include +#include +#include + +namespace { +inline constexpr size_t kLaunchIters = 10; +} // anonymous namespace + +template +void captureSequenceSimple(T* hostMem1, T* devMem1, T* hostMem2, size_t N, + hipStream_t captureStream) { + size_t Nbytes = N * sizeof(T); + + HIP_CHECK(hipMemsetAsync(devMem1, 0, Nbytes, captureStream)); + HIP_CHECK(hipMemcpyAsync(devMem1, hostMem1, Nbytes, hipMemcpyHostToDevice, captureStream)); + HIP_CHECK(hipMemcpyAsync(hostMem2, devMem1, Nbytes, hipMemcpyDeviceToHost, captureStream)); +} + +template +void captureSequenceLinear(T* hostMem1, T* devMem1, T* hostMem2, T* devMem2, size_t N, + hipStream_t captureStream) { + size_t Nbytes = N * sizeof(T); + + HIP_CHECK(hipMemcpyAsync(devMem1, hostMem1, Nbytes, hipMemcpyHostToDevice, captureStream)); + + HIP_CHECK(hipMemsetAsync(devMem2, 0, Nbytes, captureStream)); +} + +template +void captureSequenceBranched(T* hostMem1, T* devMem1, T* hostMem2, T* devMem2, size_t N, + hipStream_t captureStream, std::vector& streams, + std::vector& events) { + size_t Nbytes = N * sizeof(T); + + HIP_CHECK(hipEventRecord(events[0], captureStream)); + HIP_CHECK(hipStreamWaitEvent(streams[0], events[0], 0)); + HIP_CHECK(hipStreamWaitEvent(streams[1], events[0], 0)); + HIP_CHECK(hipMemsetAsync(devMem1, 0, Nbytes, streams[0])); + HIP_CHECK(hipMemcpyAsync(devMem1, hostMem1, Nbytes, hipMemcpyHostToDevice, streams[0])); + HIP_CHECK(hipEventRecord(events[1], streams[0])); + HIP_CHECK(hipMemsetAsync(devMem2, 0, Nbytes, streams[1])); + HIP_CHECK(hipEventRecord(events[2], streams[1])); + HIP_CHECK(hipStreamWaitEvent(captureStream, events[1], 0)); + HIP_CHECK(hipStreamWaitEvent(captureStream, events[2], 0)); +} + +template +void captureSequenceCompute(T* devMem1, T* hostMem2, T* devMem2, size_t N, hipStream_t stream) { + size_t Nbytes = N * sizeof(T); + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), dim3(threadsPerBlock), 0, stream, + devMem1, devMem2, N); + + HIP_CHECK(hipMemcpyAsync(hostMem2, devMem2, Nbytes, hipMemcpyDeviceToHost, stream)); +} From 4580864fa1dd7cd265c65db93e78550b258981db Mon Sep 17 00:00:00 2001 From: nives-vukovic <110852104+nives-vukovic@users.noreply.github.com> Date: Mon, 6 Mar 2023 09:11:54 +0100 Subject: [PATCH 04/13] EXSWHTEC-145 - Implement tests for Stream Capture APIs (#11) Implement tests for hipStreamUpdateCaptureDependencies, hipThreadExchangeStreamCaptureMode and hipLaunchHostFunc - Refactor existing tests by including more catch2 features - Reduce code line numbers by using helper guard classes - Add some positive and negative tests - Add doxygen test descriptions --- .../config/config_amd_windows_common.json | 3 +- catch/unit/graph/CMakeLists.txt | 5 + catch/unit/graph/hipLaunchHostFunc.cc | 183 ++ catch/unit/graph/hipStreamBeginCapture.cc | 1837 ++++++++--------- catch/unit/graph/hipStreamBeginCapture_old.cc | 1294 ++++++++++++ catch/unit/graph/hipStreamEndCapture.cc | 511 ++--- catch/unit/graph/hipStreamEndCapture_old.cc | 437 ++++ .../hipStreamUpdateCaptureDependencies.cc | 472 +++++ .../hipThreadExchangeStreamCaptureMode.cc | 151 ++ 9 files changed, 3590 insertions(+), 1303 deletions(-) create mode 100644 catch/unit/graph/hipLaunchHostFunc.cc create mode 100644 catch/unit/graph/hipStreamBeginCapture_old.cc create mode 100644 catch/unit/graph/hipStreamEndCapture_old.cc create mode 100644 catch/unit/graph/hipStreamUpdateCaptureDependencies.cc create mode 100644 catch/unit/graph/hipThreadExchangeStreamCaptureMode.cc diff --git a/catch/hipTestMain/config/config_amd_windows_common.json b/catch/hipTestMain/config/config_amd_windows_common.json index af9e8e7ca9..09eadeb673 100644 --- a/catch/hipTestMain/config/config_amd_windows_common.json +++ b/catch/hipTestMain/config/config_amd_windows_common.json @@ -114,6 +114,7 @@ "Unit_hipStreamValue_Wait64_Blocking_NoMask_Nor", "Unit_hipStreamQuery_WithFinishedWork", "Unit_hipLaunchHostFunc_Graph", - "Unit_hipLaunchHostFunc_KernelHost" + "Unit_hipLaunchHostFunc_KernelHost", + "Unit_hipStreamSetCaptureDependencies_Positive_Functional" ] } diff --git a/catch/unit/graph/CMakeLists.txt b/catch/unit/graph/CMakeLists.txt index 1e89b09630..9c06e091b0 100644 --- a/catch/unit/graph/CMakeLists.txt +++ b/catch/unit/graph/CMakeLists.txt @@ -63,11 +63,13 @@ set(TEST_SRC hipGraphEventWaitNodeGetEvent.cc hipGraphExecMemcpyNodeSetParams.cc hipStreamBeginCapture.cc + hipStreamBeginCapture_old.cc hipStreamIsCapturing.cc hipStreamIsCapturing_old.cc hipStreamGetCaptureInfo.cc hipStreamGetCaptureInfo_old.cc hipStreamEndCapture.cc + hipStreamEndCapture_old.cc hipGraphMemcpyNodeSetParamsFromSymbol_old.cc hipGraphMemcpyNodeSetParamsFromSymbol.cc hipGraphExecEventWaitNodeSetEvent.cc @@ -87,6 +89,9 @@ set(TEST_SRC hipGraphHostNodeGetParams.cc hipGraphExecChildGraphNodeSetParams.cc hipStreamGetCaptureInfo_v2.cc + hipStreamUpdateCaptureDependencies.cc + hipThreadExchangeStreamCaptureMode.cc + hipLaunchHostFunc.cc hipStreamGetCaptureInfo_v2_old.cc hipUserObjectCreate.cc hipGraphDebugDotPrint.cc diff --git a/catch/unit/graph/hipLaunchHostFunc.cc b/catch/unit/graph/hipLaunchHostFunc.cc new file mode 100644 index 0000000000..005bb6b736 --- /dev/null +++ b/catch/unit/graph/hipLaunchHostFunc.cc @@ -0,0 +1,183 @@ +/* +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 "stream_capture_common.hh" + +/** + * @addtogroup hipLaunchHostFunc hipLaunchHostFunc + * @{ + * @ingroup GraphTest + * `hipLaunchHostFunc(hipStream_t stream, hipHostFn_t fn, void *userData)` - + * enqueues a host function call in a stream + */ + +static void hostNodeCallbackDummy(void* data) { REQUIRE(data == nullptr); } + +static void hostNodeCallback(void* data) { + float** userData = static_cast(data); + + float input_data = *(userData[0]); + float output_data = *(userData[1]); + REQUIRE(input_data == output_data); +} + +/** + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# Stream is legacy/nullptr stream + * -# Function is nullptr + * -# Stream is uninitialized + * Test source + * ------------------------ + * - catch\unit\graph\hipLaunchHostFunc.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipLaunchHostFunc_Negative_Parameters") { + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); +#if HT_NVIDIA // EXSWHTEC-228 + SECTION("Pass stream as nullptr") { + hipHostFn_t fn = hostNodeCallbackDummy; + HIP_CHECK_ERROR(hipLaunchHostFunc(nullptr, fn, nullptr), hipErrorStreamCaptureImplicit); + } +#endif + SECTION("Pass functions as nullptr") { + HIP_CHECK_ERROR(hipLaunchHostFunc(stream, nullptr, nullptr), hipErrorInvalidValue); + } + + SECTION("Pass uninitialized stream") { + hipHostFn_t fn = hostNodeCallbackDummy; + constexpr auto InvalidStream = [] { + StreamGuard sg(Streams::created); + return sg.stream(); + }; + HIP_CHECK_ERROR(hipLaunchHostFunc(InvalidStream(), fn, nullptr), hipErrorContextIsDestroyed); + } +} + +/** + * Test Description + * ------------------------ + * - Test to verify enquing a host function into a stream, which checks if + * the captured computation result is correct + * Test source + * ------------------------ + * - catch\unit\graph\hipLaunchHostFunc.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipLaunchHostFunc_Positive_Functional") { + LinearAllocGuard A_h(LinearAllocs::malloc, sizeof(float)); + LinearAllocGuard B_h(LinearAllocs::malloc, sizeof(float)); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, sizeof(float)); + + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = hipStreamCaptureModeGlobal; + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), 1, stream); + + hipHostFn_t fn = hostNodeCallback; + float* data[2] = {A_h.host_ptr(), B_h.host_ptr()}; + HIP_CHECK(hipLaunchHostFunc(stream, fn, static_cast(data))); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + // Validate end capture is successful + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + std::fill_n(A_h.host_ptr(), 1, static_cast(i)); + HIP_CHECK(hipGraphLaunch(graphExec, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + ArrayFindIfNot(B_h.host_ptr(), static_cast(i), 1); + } + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +static void thread_func_pos(hipStream_t* stream, hipHostFn_t fn, float** data){ + + HIP_CHECK(hipLaunchHostFunc(*stream, fn, static_cast(data)))} + +/** + * Test Description + * ------------------------ + * - Test to verify enquing a host function into a stream on a different + * thread, which checks if the captured computation result is correct + * Test source + * ------------------------ + * - catch\unit\graph\hipLaunchHostFunc.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipLaunchHostFunc_Positive_Thread") { + LinearAllocGuard A_h(LinearAllocs::malloc, sizeof(float)); + LinearAllocGuard B_h(LinearAllocs::malloc, sizeof(float)); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, sizeof(float)); + + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = hipStreamCaptureModeGlobal; + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), 1, stream); + + hipHostFn_t fn = hostNodeCallback; + float* data[2] = {A_h.host_ptr(), B_h.host_ptr()}; + std::thread t(thread_func_pos, &stream, fn, data); + t.join(); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + // Validate end capture is successful + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + std::fill_n(A_h.host_ptr(), 1, static_cast(i)); + HIP_CHECK(hipGraphLaunch(graphExec, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + ArrayFindIfNot(B_h.host_ptr(), static_cast(i), 1); + } + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} diff --git a/catch/unit/graph/hipStreamBeginCapture.cc b/catch/unit/graph/hipStreamBeginCapture.cc index 80be624e8a..e7e8b72761 100644 --- a/catch/unit/graph/hipStreamBeginCapture.cc +++ b/catch/unit/graph/hipStreamBeginCapture.cc @@ -17,103 +17,25 @@ OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** -Testcase Scenarios : Functional - 1) Initiate stream capture with different modes on custom stream. - Capture stream sequence and replay the sequence in multiple iterations. - 2) End capture and validate that API returns captured graph for - all possible modes on custom stream. - 3) Initiate stream capture with different modes on hipStreamPerThread. - Capture stream sequence and replay the sequence in multiple iterations. - 4) End capture and validate that API returns captured graph for - all possible modes on hipStreamPerThread. - 5) Waiting on an event recorded on a captured stream. Initiate capture - on stream1, record an event on stream1, wait for the event on stream2, - end the stream1 capture and Initiate stream capture on stream2 - 5.1) Both streams are created with default flags. - 5.2) Both streams are created with flag = hipStreamCaptureModeGlobal. - 5.3) Both streams are created with different flags. - 5.4) Both streams are created with different priorities. - 5.5) Validate the number of nodes in both the captured graphs. - 6) Colligated Streams capture. Capture operation sequences queued in - 2 streams by overlapping the 2 captures. - 6.1) Both streams are created with default flags. - 6.2) Both streams are created with flag = hipStreamCaptureModeGlobal. - 6.3) Both streams are created with different flags. - 6.4) Both streams are created with different priorities. - 7) Extend the scenario 5.1 for 3 streamsss. - 8) Create 2 streams. Start capturing both stream1 and stream2 at the same - time. On stream1 queue memcpy, kernel and memcpy operations and on stream2 - queue memcpy, kernel and memcpy operations. Execute both the captured - graphs and validate the results. - 9) Capture 2 streams in parallel using threads. Execute the graphs in - sequence in main thread and validate the results. - 9.1) mode = hipStreamCaptureModeGlobal - 9.2) mode = hipStreamCaptureModeThreadLocal - 9.3) mode = hipStreamCaptureModeRelaxed - 10) Queue operations (increment kernels) in 3 streams. Start capturing - the streams after some operations have been queued. This scenario validates - that only operations queued after hipStreamBeginCapture are captured in - the graph. - 11) Detecting invalid capture. Create 2 streams s1 and s2. Start capturing - s1. Create event dependency between s1 and s2 using event record and event - wait. Try capturing s2. hipStreamBeginCapture must return error. - 12) Stream reuse. Capture multiple graphs from the same stream. Validate - graphs are captured correctly. - 13) Test different synchronization during stream capture. - 13.1) Test hipStreamSynchronize. Must return - hipErrorStreamCaptureUnsupported. - 13.2) Test hipDeviceSynchronize. Must return - hipErrorStreamCaptureUnsupported. - 13.3) Test hipDeviceSynchronize. Must return - hipEventSynchronize. - 13.4) Test hipStreamWaitEvent. Must return - hipErrorStreamCaptureIsolation. - 14) End Stream Capture when the stream capture is still in progress. - 14.1) Abruptly end stream capture when stream capture is in progress in - forked stream. hipStreamEndCapture must return - hipErrorStreamCaptureUnjoined. - 14.2) Abruptly end stream capture when operations in forked stream - are still waiting to be captured. hipStreamEndCapture must return - hipErrorStreamCaptureUnjoined. - 15) Testing independent stream capture using multiple GPUs. Capture - a stream in each device context and execute the captured graph in the - context GPU. - 16) Test Nested Stream Capture Functionality: Create 3 streams s1, s2 & s3. - Capture s1, record event e1 on s1, wait for event e1 on s2 and queue - operations in s1. Record event e2 on s2 and wait for it on s3. Queue - operations on both s2 and s3. Record event e4 on s3 and wait for it in s1. - Record event e3 on s2 and wait for it in s1. End stream capture on s1. - Execute the graph and verify the result. - 17) Forked Stream Reuse: In scenario 16, after end capture on s1, queue - operations on both s2 and s3, and capture their graphs. Execute both the - graphs and validate the functionality. - 18) Capture a complex graph containing multiple independent memcpy, kernel - and host nodes. Launch the graph on random input data and validate the - output. - 19) Capture empty streams (parent + forked streams) and validate the - functionality. -*/ - -#include #include #include +#include + +#include "stream_capture_common.hh" + +/** + * @addtogroup hipStreamBeginCapture hipStreamBeginCapture + * @{ + * @ingroup GraphTest + * `hipStreamBeginCapture(hipStream_t stream, hipStreamCaptureMode mode)` - + * begins graph capture on a stream + */ -#define INCREMENT_KERNEL_FINALEXP_VAL 7 -constexpr size_t N = 1000000; -constexpr int LAUNCH_ITERS = 50; static int gCbackIter = 0; -#define GRIDSIZE 256 -#define BLOCKSIZE 256 -#define CONST_KER1_VAL 3 -#define CONST_KER2_VAL 2 -#define CONST_KER3_VAL 5 -static __global__ void dummyKernel() { - return; -} +static __global__ void dummyKernel() { return; } -static __global__ void incrementKernel(int *data) { +static __global__ void incrementKernel(int* data) { atomicAdd(data, 1); return; } @@ -133,21 +55,25 @@ static void hostNodeCallback(void* data) { gCbackIter++; } -bool CaptureStreamAndLaunchGraph(float *A_d, float *C_d, float *A_h, - float *C_h, hipStreamCaptureMode mode, hipStream_t stream) { +template +void captureStreamAndLaunchGraph(F graphFunc, hipStreamCaptureMode mode, hipStream_t stream) { + constexpr size_t N = 1000000; + size_t Nbytes = N * sizeof(T); + hipGraph_t graph{nullptr}; hipGraphExec_t graphExec{nullptr}; - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - size_t Nbytes = N * sizeof(float); + // Host and Device allocation + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard B_d(LinearAllocs::hipMalloc, Nbytes); + + // Capture stream sequence HIP_CHECK(hipStreamBeginCapture(stream, mode)); - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + graphFunc(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream); - HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + captureSequenceCompute(A_d.ptr(), B_h.ptr(), B_d.ptr(), N, stream); HIP_CHECK(hipStreamEndCapture(stream, &graph)); @@ -158,176 +84,142 @@ bool CaptureStreamAndLaunchGraph(float *A_d, float *C_d, float *A_h, REQUIRE(graphExec != nullptr); // Replay the recorded sequence multiple times - for (int i = 0; i < LAUNCH_ITERS; i++) { + for (int i = 0; i < kLaunchIters; i++) { + 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); } - HIP_CHECK(hipStreamSynchronize(stream)); - - HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphExecDestroy(graphExec)) HIP_CHECK(hipGraphDestroy(graph)); - - // Validate the computation - for (size_t i = 0; i < N; i++) { - if (C_h[i] != A_h[i] * A_h[i]) { - UNSCOPED_INFO("A and C not matching at " << i); - return false; - } - } - return true; } /** - * Basic Functional Test for API capturing custom stream and replaying sequence. - * Test exercises the API on available/possible modes. - * Stream capture with different modes behave the same when supported/ - * safe apis are used in sequence. + * Test Description + * ------------------------ + * - Basic Functional Test for capturing created/hipStreamPerThread stream + * and replaying sequence. Test exercises the API on all available modes: + * -# Linear sequence capture - each graph node has only one dependency + * -# Branched sequence capture - some graph nodes have more than one + * dependency + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_BasicFunctional") { - float *A_d, *C_d; - float *A_h, *C_h; - size_t Nbytes = N * sizeof(float); - hipStream_t stream; - bool ret; +TEST_CASE("Unit_hipStreamBeginCapture_Positive_Functional") { + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); - // Fill with Phi + i - for (size_t i = 0; i < N; i++) { - A_h[i] = 1.618f + i; + 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) { + return captureSequenceLinear(A_h, A_d, B_h, B_d, N, stream); + }, + captureMode, stream); } - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - - SECTION("Capture stream and launch graph when mode is global") { - ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, - hipStreamCaptureModeGlobal, stream); - REQUIRE(ret == true); + 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()); + }, + captureMode, stream); } - - SECTION("Capture stream and launch graph when mode is local") { - ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, - hipStreamCaptureModeThreadLocal, stream); - REQUIRE(ret == true); - } - - SECTION("Capture stream and launch graph when mode is relaxed") { - ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, - hipStreamCaptureModeRelaxed, stream); - REQUIRE(ret == true); - } - - HIP_CHECK(hipStreamDestroy(stream)); - free(A_h); - free(C_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); } /** - * Perform capture on hipStreamPerThread, launch the graph and verify results. + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# Begin capture on legacy/null stream + * -# Begin capture on the already captured stream + * -# Begin capture with invalid mode + * -# Begin capture on uninitialized stream + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_hipStreamPerThread") { - float *A_d, *C_d; - float *A_h, *C_h; - size_t Nbytes = N * sizeof(float); - hipStream_t stream{hipStreamPerThread}; - bool ret; - - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - - // Fill with Phi + i - for (size_t i = 0; i < N; i++) { - A_h[i] = 1.618f + i; - } - - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - - SECTION("Capture hipStreamPerThread and launch graph when mode is global") { - ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, - hipStreamCaptureModeGlobal, stream); - REQUIRE(ret == true); - } - - SECTION("Capture hipStreamPerThread and launch graph when mode is local") { - ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, - hipStreamCaptureModeThreadLocal, stream); - REQUIRE(ret == true); - } - - SECTION("Capture hipStreamPerThread and launch graph when mode is relaxed") { - ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, - hipStreamCaptureModeRelaxed, stream); - REQUIRE(ret == true); - } - - free(A_h); - free(C_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); -} - - -/* Test verifies hipStreamBeginCapture API Negative scenarios. - */ - -TEST_CASE("Unit_hipStreamBeginCapture_Negative") { - hipError_t ret; - hipStream_t stream{}; - HIP_CHECK(hipStreamCreate(&stream)); +TEST_CASE("Unit_hipStreamBeginCapture_Negative_Parameters") { + const auto stream_type = GENERATE(Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); SECTION("Stream capture on legacy/null stream returns error code.") { - ret = hipStreamBeginCapture(nullptr, hipStreamCaptureModeGlobal); - REQUIRE(hipErrorStreamCaptureUnsupported == ret); + HIP_CHECK_ERROR(hipStreamBeginCapture(nullptr, hipStreamCaptureModeGlobal), + hipErrorStreamCaptureUnsupported); } SECTION("Capturing hipStream status with same stream again") { HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - ret = hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal); - REQUIRE(hipErrorIllegalState == ret); + HIP_CHECK_ERROR(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal), + hipErrorIllegalState); } SECTION("Creating hipStream with invalid mode") { - ret = hipStreamBeginCapture(stream, hipStreamCaptureMode(-1)); - REQUIRE(hipErrorInvalidValue == ret); + HIP_CHECK_ERROR(hipStreamBeginCapture(stream, hipStreamCaptureMode(-1)), hipErrorInvalidValue); } - HIP_CHECK(hipStreamDestroy(stream)); +#if HT_NVIDIA // EXSWHTEC-216 + SECTION("Stream capture on uninitialized stream returns error code.") { + constexpr auto InvalidStream = [] { + StreamGuard sg(Streams::created); + return sg.stream(); + }; + HIP_CHECK_ERROR(hipStreamBeginCapture(InvalidStream(), hipStreamCaptureModeGlobal), + hipErrorContextIsDestroyed); + } +#endif } -TEST_CASE("Unit_hipStreamBeginCapture_Basic") { - hipStream_t s1, s2, s3; - - HIP_CHECK(hipStreamCreate(&s1)); - HIP_CHECK(hipStreamBeginCapture(s1, hipStreamCaptureModeGlobal)); - - HIP_CHECK(hipStreamCreate(&s2)); - HIP_CHECK(hipStreamBeginCapture(s2, hipStreamCaptureModeThreadLocal)); - - HIP_CHECK(hipStreamCreate(&s3)); - HIP_CHECK(hipStreamBeginCapture(s3, hipStreamCaptureModeRelaxed)); - - HIP_CHECK(hipStreamDestroy(s1)); - HIP_CHECK(hipStreamDestroy(s2)); - HIP_CHECK(hipStreamDestroy(s3)); -} -/* Local Function +/** + * Test Description + * ------------------------ + * - Basic Test to verify basic API functionality with + * created/hipStreamPerThread stream for available modes + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -static void interStrmEventSyncCapture(const hipStream_t &stream1, - const hipStream_t &stream2) { - hipGraph_t graph1, graph2; - hipEvent_t event; +TEST_CASE("Unit_hipStreamBeginCapture_Positive_Basic") { + hipGraph_t graph{nullptr}; + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t s = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + + HIP_CHECK(hipStreamBeginCapture(s, captureMode)); + + HIP_CHECK(hipStreamEndCapture(s, &graph)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/* Local function for inter stream event synchronization + */ +static void interStrmEventSyncCapture(const hipStream_t& stream1, const hipStream_t& stream2) { + hipGraph_t graph1{nullptr}, graph2{nullptr}; hipGraphExec_t graphExec1{nullptr}, graphExec2{nullptr}; + + EventsGuard events_guard(1); + hipEvent_t event = events_guard[0]; + HIP_CHECK(hipEventCreate(&event)); HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); HIP_CHECK(hipEventRecord(event, stream1)); @@ -338,35 +230,43 @@ static void interStrmEventSyncCapture(const hipStream_t &stream1, dummyKernel<<<1, 1, 0, stream2>>>(); dummyKernel<<<1, 1, 0, stream2>>>(); HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); - // Create Executable Graphs - HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); - REQUIRE(graphExec1 != nullptr); - HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); - REQUIRE(graphExec2 != nullptr); + size_t numNodes1 = 0, numNodes2 = 0; HIP_CHECK(hipGraphGetNodes(graph1, nullptr, &numNodes1)); HIP_CHECK(hipGraphGetNodes(graph2, nullptr, &numNodes2)); REQUIRE(numNodes1 == 1); REQUIRE(numNodes2 == 2); - // Execute the Graphs - HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); - HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); - HIP_CHECK(hipStreamSynchronize(stream1)); - HIP_CHECK(hipStreamSynchronize(stream2)); + + HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); + REQUIRE(graphExec1 != nullptr); + HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); + REQUIRE(graphExec2 != nullptr); + + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + // Execute the Graphs + HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); + HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipStreamSynchronize(stream2)); + } + // Free HIP_CHECK(hipGraphExecDestroy(graphExec2)); HIP_CHECK(hipGraphExecDestroy(graphExec1)); HIP_CHECK(hipGraphDestroy(graph2)); HIP_CHECK(hipGraphDestroy(graph1)); - HIP_CHECK(hipEventDestroy(event)); } -/* Local Function + +/* Local function for colligated stream capture */ -static void colligatedStrmCapture(const hipStream_t &stream1, - const hipStream_t &stream2) { - hipGraph_t graph1, graph2; - hipEvent_t event; +static void colligatedStrmCapture(const hipStream_t& stream1, const hipStream_t& stream2) { + hipGraph_t graph1{nullptr}, graph2{nullptr}; hipGraphExec_t graphExec1{nullptr}, graphExec2{nullptr}; + + EventsGuard events_guard(1); + hipEvent_t event = events_guard[0]; + HIP_CHECK(hipEventCreate(&event)); HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); HIP_CHECK(hipEventRecord(event, stream1)); @@ -379,550 +279,638 @@ static void colligatedStrmCapture(const hipStream_t &stream1, // Validate end capture is successful REQUIRE(graph2 != nullptr); REQUIRE(graph1 != nullptr); - // Create Executable Graphs + HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); REQUIRE(graphExec1 != nullptr); HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); REQUIRE(graphExec2 != nullptr); - // Execute the Graphs - HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); - HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); - HIP_CHECK(hipStreamSynchronize(stream1)); - HIP_CHECK(hipStreamSynchronize(stream2)); + + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + // Execute the Graphs + HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); + HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipStreamSynchronize(stream2)); + } + // Free HIP_CHECK(hipGraphExecDestroy(graphExec2)); HIP_CHECK(hipGraphExecDestroy(graphExec1)); HIP_CHECK(hipGraphDestroy(graph2)); HIP_CHECK(hipGraphDestroy(graph1)); - HIP_CHECK(hipEventDestroy(event)); } -/* Fill input Data + +/* Local function for colligated stream capture functionality */ -static void init_input(int* a, size_t size) { - unsigned int seed = time(nullptr); - for (size_t i = 0; i < size; i++) { - a[i] = (HipTest::RAND_R(&seed) & 0xFF); - } -} -/* Validate Output - */ -static void validate_output(int* a, int *b, size_t size) { - for (size_t i = 0; i < size; i++) { - REQUIRE(a[i] == (b[i]*b[i])); - } -} -/* Local Function - */ -static void colligatedStrmCaptureFunc(const hipStream_t &stream1, - const hipStream_t &stream2) { - constexpr size_t size = 1024; - constexpr auto blocksPerCU = 6; - constexpr auto threadsPerBlock = 256; - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, - threadsPerBlock, size); - hipGraph_t graph1, graph2; - 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}; +static void colligatedStrmCaptureFunc(const hipStream_t& stream1, const hipStream_t& stream2) { + constexpr size_t N = 1000000; + size_t Nbytes = N * sizeof(int); + + hipGraph_t graph1{nullptr}, graph2{nullptr}; hipGraphExec_t graphExec1{nullptr}, graphExec2{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); + + // Host and device allocation + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard B_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard C_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard C_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard D_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard D_d(LinearAllocs::hipMalloc, Nbytes); + // Capture 2 streams HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipMemcpyAsync(inputVec_d1, inputVec_h1, sizeof(int) * size, - hipMemcpyDefault, stream1)); - HIP_CHECK(hipMemcpyAsync(inputVec_d2, inputVec_h2, sizeof(int) * size, - hipMemcpyDefault, stream2)); - HipTest::vector_square<<>>( - inputVec_d1, outputVec_d1, size); - HipTest::vector_square<<>>( - inputVec_d2, outputVec_d2, size); - HIP_CHECK(hipMemcpyAsync(outputVec_h1, outputVec_d1, sizeof(int) * size, - hipMemcpyDefault, stream1)); - HIP_CHECK(hipMemcpyAsync(outputVec_h2, outputVec_d2, sizeof(int) * size, - hipMemcpyDefault, 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)); HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); // Validate end capture is successful REQUIRE(graph2 != nullptr); REQUIRE(graph1 != nullptr); + // Create Executable Graphs HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); REQUIRE(graphExec1 != nullptr); HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); REQUIRE(graphExec2 != nullptr); + // Execute the Graphs - for (int iter = 0; iter < LAUNCH_ITERS; iter++) { - init_input(inputVec_h1, size); - init_input(inputVec_h2, size); + for (int iter = 0; iter < kLaunchIters; iter++) { + std::fill_n(A_h.host_ptr(), N, iter); + std::fill_n(C_h.host_ptr(), N, iter); HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); HIP_CHECK(hipStreamSynchronize(stream1)); HIP_CHECK(hipStreamSynchronize(stream2)); - validate_output(outputVec_h1, inputVec_h1, size); - validate_output(outputVec_h2, inputVec_h2, size); + ArrayFindIfNot(B_h.host_ptr(), iter * iter, N); + ArrayFindIfNot(D_h.host_ptr(), iter * iter, N); } + // 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); HIP_CHECK(hipGraphExecDestroy(graphExec2)); HIP_CHECK(hipGraphExecDestroy(graphExec1)); HIP_CHECK(hipGraphDestroy(graph2)); HIP_CHECK(hipGraphDestroy(graph1)); } + /* Stream Capture thread function */ -static void threadStrmCaptureFunc(hipStream_t stream, int *inputVec_d, -int *outputVec_d, int *inputVec_h, int *outputVec_h, hipGraph_t *graph, -size_t size, hipStreamCaptureMode mode) { - constexpr auto blocksPerCU = 6; - constexpr auto threadsPerBlock = 256; - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, - threadsPerBlock, size); +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)); - HIP_CHECK(hipMemcpyAsync(inputVec_d, inputVec_h, sizeof(int) * size, - hipMemcpyDefault, stream)); - HipTest::vector_square<<>>( - inputVec_d, outputVec_d, size); - HIP_CHECK(hipMemcpyAsync(outputVec_h, outputVec_d, sizeof(int) * size, - hipMemcpyDefault, stream)); + captureSequenceLinear(A_h, A_d, B_h, B_d, N, stream); + captureSequenceCompute(A_d, B_h, B_d, N, stream); HIP_CHECK(hipStreamEndCapture(stream, graph)); } + /* Local Function for multithreaded tests */ static void multithreadedTest(hipStreamCaptureMode mode) { - hipStream_t stream1, stream2; - constexpr size_t size = 1024; - hipGraph_t graph1, graph2; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - 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}; + constexpr size_t N = 1000000; + size_t Nbytes = N * sizeof(int); + + hipGraph_t graph1{nullptr}, graph2{nullptr}; hipGraphExec_t graphExec1{nullptr}, graphExec2{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); + StreamGuard stream_guard1(Streams::created); + hipStream_t stream1 = stream_guard1.stream(); + StreamGuard stream_guard2(Streams::created); + hipStream_t stream2 = stream_guard2.stream(); + + // Host and device allocation + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard B_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard C_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard D_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard C_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard D_d(LinearAllocs::hipMalloc, Nbytes); + // Launch 2 threads to capture the 2 streams into graphs - std::thread t1(threadStrmCaptureFunc, stream1, inputVec_d1, - outputVec_d1, inputVec_h1, outputVec_h1, &graph1, size, mode); - std::thread t2(threadStrmCaptureFunc, stream2, inputVec_d2, - outputVec_d2, inputVec_h2, outputVec_h2, &graph2, size, 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(); + // Create Executable Graphs HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); + REQUIRE(graphExec1 != nullptr); HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); + REQUIRE(graphExec2 != nullptr); + // Execute the Graphs - for (int iter = 0; iter < LAUNCH_ITERS; iter++) { - init_input(inputVec_h1, size); - init_input(inputVec_h2, size); + for (int iter = 0; iter < kLaunchIters; iter++) { + std::fill_n(A_h.host_ptr(), N, iter); + std::fill_n(C_h.host_ptr(), N, iter); HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); HIP_CHECK(hipStreamSynchronize(stream1)); HIP_CHECK(hipStreamSynchronize(stream2)); - validate_output(outputVec_h1, inputVec_h1, size); - validate_output(outputVec_h2, inputVec_h2, size); + ArrayFindIfNot(B_h.host_ptr(), iter * iter, N); + ArrayFindIfNot(D_h.host_ptr(), iter * iter, N); } + // 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); HIP_CHECK(hipGraphExecDestroy(graphExec2)); HIP_CHECK(hipGraphExecDestroy(graphExec1)); - HIP_CHECK(hipGraphDestroy(graph1)); HIP_CHECK(hipGraphDestroy(graph2)); - HIP_CHECK(hipStreamDestroy(stream1)); - HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipGraphDestroy(graph1)); } -/* Test scenario 5.1 + +/** + * Test Description + * ------------------------ + * - Test to verify inter stream event synchronization- Waiting on an event + recorded on a captured stream. Initiate capture on stream1, record an event on + stream1, wait for the event on stream2, end the stream1 capture and initiate + stream capture on stream2 + * -# Streams are created with hipStreamDefault/hipStreamNonBlocking flag + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_defaultflag") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); +TEST_CASE("Unit_hipStreamBeginCapture_Positive_InterStrmEventSync_Flags") { + const auto stream_flags1 = GENERATE(hipStreamDefault, hipStreamNonBlocking); + const auto stream_flags2 = GENERATE(hipStreamDefault, hipStreamNonBlocking); + StreamGuard stream_guard1(Streams::withFlags, stream_flags1); + hipStream_t stream1 = stream_guard1.stream(); + StreamGuard stream_guard2(Streams::withFlags, stream_flags2); + hipStream_t stream2 = stream_guard2.stream(); interStrmEventSyncCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } -/* Test scenario 5.2 + +/** + * Test Description + * ------------------------ + * - Test to verify inter stream event synchronization- Waiting on an event + * recorded on a captured stream. Initiate capture on stream1, record an event + * on stream1, wait for the event on stream2, end the stream1 capture and + * initiate stream capture on stream2 + * -# Stream1 is created with minimal priority, stream 2 is created with + * maximal priority + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_blockingflag") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); - HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamNonBlocking)); - interStrmEventSyncCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); -} -/* Test scenario 5.3 - */ -TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_diffflags") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); - HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamDefault)); - interStrmEventSyncCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); -} -/* Test scenario 5.4 - */ -TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_diffprio") { - hipStream_t stream1, stream2; +TEST_CASE("Unit_hipStreamBeginCapture_Positive_InterStrmEventSync_Priority") { int minPriority = 0, maxPriority = 0; HIP_CHECK(hipDeviceGetStreamPriorityRange(&minPriority, &maxPriority)); - HIP_CHECK(hipStreamCreateWithPriority(&stream1, hipStreamDefault, - minPriority)); - HIP_CHECK(hipStreamCreateWithPriority(&stream2, hipStreamDefault, - maxPriority)); + StreamGuard stream_guard1(Streams::withPriority, hipStreamDefault, minPriority); + hipStream_t stream1 = stream_guard1.stream(); + StreamGuard stream_guard2(Streams::withPriority, hipStreamDefault, maxPriority); + hipStream_t stream2 = stream_guard2.stream(); interStrmEventSyncCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } -/* Test scenario 6.1 + +/** + * Test Description + * ------------------------ + * - Test to verify colligated streams capture. Capture operation sequences + * queued in 2 streams by overlapping the 2 captures. Initiate capture on + * stream1, record an event on stream1, initiate capture on stream 2, end both + * stream captures + * -# Streams are created with hipStreamDefault/hipStreamNonBlocking flag + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_defaultflag") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); +TEST_CASE("Unit_hipStreamBeginCapture_Positive_ColligatedStrmCapture_Flags") { + const auto stream_flags1 = GENERATE(hipStreamDefault, hipStreamNonBlocking); + const auto stream_flags2 = GENERATE(hipStreamDefault, hipStreamNonBlocking); + StreamGuard stream_guard1(Streams::withFlags, stream_flags1); + hipStream_t stream1 = stream_guard1.stream(); + StreamGuard stream_guard2(Streams::withFlags, stream_flags2); + hipStream_t stream2 = stream_guard2.stream(); colligatedStrmCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } -/* Test scenario 6.2 + +/** + * Test Description + * ------------------------ + * - Test to verify colligated streams capture. Capture operation sequences + * queued in 2 streams by overlapping the 2 captures. Initiate capture on + * stream1, record an event on stream1, initiate capture on stream 2, end both + * stream captures + * -# Stream1 is created with minimal priority, stream 2 is created with + * maximal priority + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_blockingflag") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); - HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamNonBlocking)); - colligatedStrmCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); -} -/* Test scenario 6.3 - */ -TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_diffflags") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); - HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamDefault)); - colligatedStrmCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); -} -/* Test scenario 6.4 - */ -TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_diffprio") { - hipStream_t stream1, stream2; +TEST_CASE("Unit_hipStreamBeginCapture_Positive_ColligatedStrmCapture_Priority") { int minPriority = 0, maxPriority = 0; HIP_CHECK(hipDeviceGetStreamPriorityRange(&minPriority, &maxPriority)); - HIP_CHECK(hipStreamCreateWithPriority(&stream1, hipStreamDefault, - minPriority)); - HIP_CHECK(hipStreamCreateWithPriority(&stream2, hipStreamDefault, - maxPriority)); + StreamGuard stream_guard1(Streams::withPriority, hipStreamDefault, minPriority); + hipStream_t stream1 = stream_guard1.stream(); + StreamGuard stream_guard2(Streams::withPriority, hipStreamDefault, maxPriority); + hipStream_t stream2 = stream_guard2.stream(); colligatedStrmCapture(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } -/* Test scenario 7 + +/** + * Test Description + * ------------------------ + * - Create 2 streams. Start capturing both stream1 and stream2 at the same + * time. On stream1 queue memcpy, kernel and memcpy operations and on stream2 + * queue memcpy, kernel and memcpy operations. Execute both the captured graphs + * and validate the results + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_multiplestrms") { - hipStream_t stream1, stream2, stream3; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipStreamCreate(&stream3)); - hipGraph_t graph1, graph2, graph3; +TEST_CASE("Unit_hipStreamBeginCapture_Positive_ColligatedStrmCaptureFunc") { + StreamGuard stream_guard1(Streams::created); + hipStream_t stream1 = stream_guard1.stream(); + StreamGuard stream_guard2(Streams::created); + hipStream_t stream2 = stream_guard2.stream(); + colligatedStrmCaptureFunc(stream1, stream2); +} + +/** + * Test Description + * ------------------------ + * - Capture 2 streams in parallel using threads. Execute the graphs in + * sequence in main thread and validate the results for all available capture + * modes + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamBeginCapture_Positive_Multithreaded") { + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + multithreadedTest(captureMode); +} + +/** + * Test Description + * ------------------------ + * - Test to verify inter stream event synchronization- Waiting on an event + * recorded on a captured stream. + * -# Initiate capture on stream1, record an event on stream1, wait for + * the event on stream2, end the stream1 capture and initiate stream capture on + * stream2. Repeat the same sequence between stream2 and stream3 + * -# Initiate capture on stream1, record an event on stream1, wait for + * the event on stream2 and stream3, end the stream1 capture and initiate stream + * capture on stream2 and stream3 + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamBeginCapture_Positive_Multiplestrms") { + StreamsGuard streams(3); + hipGraph_t graphs[3]; + size_t numNodes1 = 0, numNodes2 = 0, numNodes3 = 0; SECTION("Capture Multiple stream with interdependent events") { - hipEvent_t event1, event2; - HIP_CHECK(hipEventCreate(&event1)); - HIP_CHECK(hipEventCreate(&event2)); - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(event1, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, event1, 0)); - dummyKernel<<<1, 1, 0, stream1>>>(); - HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); - HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(event2, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream3, event2, 0)); - dummyKernel<<<1, 1, 0, stream2>>>(); - HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); - HIP_CHECK(hipStreamBeginCapture(stream3, hipStreamCaptureModeGlobal)); - dummyKernel<<<1, 1, 0, stream3>>>(); - HIP_CHECK(hipStreamEndCapture(stream3, &graph3)); - HIP_CHECK(hipGraphGetNodes(graph1, nullptr, &numNodes1)); - HIP_CHECK(hipGraphGetNodes(graph2, nullptr, &numNodes2)); - HIP_CHECK(hipGraphGetNodes(graph3, nullptr, &numNodes3)); + EventsGuard events(2); + + HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(events[0], streams[0])); + HIP_CHECK(hipStreamWaitEvent(streams[1], events[0], 0)); + dummyKernel<<<1, 1, 0, streams[0]>>>(); + HIP_CHECK(hipStreamEndCapture(streams[0], &graphs[0])); + HIP_CHECK(hipStreamBeginCapture(streams[1], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(events[1], streams[1])); + HIP_CHECK(hipStreamWaitEvent(streams[2], events[1], 0)); + dummyKernel<<<1, 1, 0, streams[1]>>>(); + HIP_CHECK(hipStreamEndCapture(streams[1], &graphs[1])); + HIP_CHECK(hipStreamBeginCapture(streams[2], hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, streams[2]>>>(); + HIP_CHECK(hipStreamEndCapture(streams[2], &graphs[2])); + HIP_CHECK(hipGraphGetNodes(graphs[0], nullptr, &numNodes1)); + HIP_CHECK(hipGraphGetNodes(graphs[1], nullptr, &numNodes2)); + HIP_CHECK(hipGraphGetNodes(graphs[2], nullptr, &numNodes3)); REQUIRE(numNodes1 == 1); REQUIRE(numNodes2 == 1); REQUIRE(numNodes3 == 1); - HIP_CHECK(hipEventDestroy(event2)); - HIP_CHECK(hipEventDestroy(event1)); } SECTION("Capture Multiple stream with single event") { - hipEvent_t event1; - HIP_CHECK(hipEventCreate(&event1)); - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(event1, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, event1, 0)); - HIP_CHECK(hipStreamWaitEvent(stream3, event1, 0)); - dummyKernel<<<1, 1, 0, stream1>>>(); - HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); - HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); - dummyKernel<<<1, 1, 0, stream2>>>(); - HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); - HIP_CHECK(hipStreamBeginCapture(stream3, hipStreamCaptureModeGlobal)); - dummyKernel<<<1, 1, 0, stream3>>>(); - HIP_CHECK(hipStreamEndCapture(stream3, &graph3)); - HIP_CHECK(hipGraphGetNodes(graph1, nullptr, &numNodes1)); - HIP_CHECK(hipGraphGetNodes(graph2, nullptr, &numNodes2)); - HIP_CHECK(hipGraphGetNodes(graph3, nullptr, &numNodes3)); + EventsGuard events(1); + hipEvent_t event = events[0]; + + HIP_CHECK(hipEventCreate(&event)); + HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event, streams[0])); + HIP_CHECK(hipStreamWaitEvent(streams[1], event, 0)); + HIP_CHECK(hipStreamWaitEvent(streams[2], event, 0)); + dummyKernel<<<1, 1, 0, streams[0]>>>(); + HIP_CHECK(hipStreamEndCapture(streams[0], &graphs[0])); + HIP_CHECK(hipStreamBeginCapture(streams[1], hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, streams[1]>>>(); + HIP_CHECK(hipStreamEndCapture(streams[1], &graphs[1])); + HIP_CHECK(hipStreamBeginCapture(streams[2], hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, streams[2]>>>(); + HIP_CHECK(hipStreamEndCapture(streams[2], &graphs[2])); + HIP_CHECK(hipGraphGetNodes(graphs[0], nullptr, &numNodes1)); + HIP_CHECK(hipGraphGetNodes(graphs[1], nullptr, &numNodes2)); + HIP_CHECK(hipGraphGetNodes(graphs[2], nullptr, &numNodes3)); REQUIRE(numNodes1 == 1); REQUIRE(numNodes2 == 1); REQUIRE(numNodes3 == 1); - HIP_CHECK(hipEventDestroy(event1)); } - HIP_CHECK(hipStreamDestroy(stream3)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); + + for (int i = 0; i < 3; i++) { + HIP_CHECK(hipGraphDestroy(graphs[i])); + } } -/* Test scenario 8 + +/** + * Test Description + * ------------------------ + * - Test to verify queue operations (increment kernels) in 3 streams. Start + * capturing the streams after some operations have been queued. This scenario + * validates that only operations queued after hipStreamBeginCapture are + * captured in the graph + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_func") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - colligatedStrmCaptureFunc(stream1, stream2); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); -} -/* Test scenario 9.1 - */ -TEST_CASE("Unit_hipStreamBeginCapture_Multithreaded_Global") { - multithreadedTest(hipStreamCaptureModeGlobal); -} -/* Test scenario 9.2 - */ -TEST_CASE("Unit_hipStreamBeginCapture_Multithreaded_ThreadLocal") { - multithreadedTest(hipStreamCaptureModeThreadLocal); -} -/* Test scenario 9.3 - */ -TEST_CASE("Unit_hipStreamBeginCapture_Multithreaded_Relaxed") { - multithreadedTest(hipStreamCaptureModeRelaxed); -} -/* Test scenario 10 - */ -TEST_CASE("Unit_hipStreamBeginCapture_CapturingFromWithinStrms") { - hipGraph_t graph; - hipStream_t stream1, stream2, stream3; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipStreamCreate(&stream3)); - hipEvent_t e1, e2, e3; - HIP_CHECK(hipEventCreate(&e1)); - HIP_CHECK(hipEventCreate(&e2)); - HIP_CHECK(hipEventCreate(&e3)); +TEST_CASE("Unit_hipStreamBeginCapture_Positive_CapturingFromWithinStrms") { + constexpr int INCREMENT_KERNEL_FINALEXP_VAL = 7; + + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + StreamsGuard streams(3); + EventsGuard events(3); + // Create a device memory of size int and initialize it to 0 - int *devMem{nullptr}, *hostMem{nullptr}; - hostMem = reinterpret_cast(malloc(sizeof(int))); - HIP_CHECK(hipMalloc(&devMem, sizeof(int))); + LinearAllocGuard hostMem_g(LinearAllocs::malloc, sizeof(int)); + LinearAllocGuard devMem_g(LinearAllocs::hipMalloc, sizeof(int)); + int* hostMem = hostMem_g.host_ptr(); + int* devMem = devMem_g.ptr(); HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); HIP_CHECK(hipDeviceSynchronize()); - // Start Capturing stream1 - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(e1, stream1)); - incrementKernel<<<1, 1, 0, stream2>>>(devMem); - incrementKernel<<<1, 1, 0, stream2>>>(devMem); - incrementKernel<<<1, 1, 0, stream3>>>(devMem); - HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); - HIP_CHECK(hipStreamWaitEvent(stream3, e1, 0)); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - incrementKernel<<<1, 1, 0, stream2>>>(devMem); - incrementKernel<<<1, 1, 0, stream3>>>(devMem); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - incrementKernel<<<1, 1, 0, stream2>>>(devMem); - incrementKernel<<<1, 1, 0, stream3>>>(devMem); - incrementKernel<<<1, 1, 0, stream3>>>(devMem); - HIP_CHECK(hipEventRecord(e2, stream2)); - HIP_CHECK(hipEventRecord(e3, stream3)); - HIP_CHECK(hipStreamWaitEvent(stream1, e2, 0)); - HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); - HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + // Start Capturing + incrementKernel<<<1, 1, 0, streams[0]>>>(devMem); + HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(events[0], streams[0])); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem); + HIP_CHECK(hipStreamWaitEvent(streams[1], events[0], 0)); + HIP_CHECK(hipStreamWaitEvent(streams[2], events[0], 0)); + incrementKernel<<<1, 1, 0, streams[0]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[0]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem); + HIP_CHECK(hipEventRecord(events[1], streams[1])); + 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(hipStreamEndCapture(streams[0], &graph)); // End Capture // Reset device memory HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); HIP_CHECK(hipDeviceSynchronize()); + // Create Executable Graphs - hipGraphExec_t graphExec{nullptr}; HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, stream1)); - HIP_CHECK(hipStreamSynchronize(stream1)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); + REQUIRE(graphExec != nullptr); + + HIP_CHECK(hipGraphLaunch(graphExec, streams[0])); + HIP_CHECK(hipStreamSynchronize(streams[0])); REQUIRE((*hostMem) == INCREMENT_KERNEL_FINALEXP_VAL); - HIP_CHECK(hipFree(devMem)); - free(hostMem); + + HIP_CHECK(hipGraphExecDestroy(graphExec)) HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipEventDestroy(e3)); - HIP_CHECK(hipEventDestroy(e2)); - HIP_CHECK(hipEventDestroy(e1)); - HIP_CHECK(hipStreamDestroy(stream3)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } -/* Test scenario 11 + +/** + * Test Description + * ------------------------ + * - Detecting invalid capture. Create 2 streams s1 and s2. Start capturing + * s1. Create event dependency between s1 and s2 using event record and event + * wait. Try capturing s2. hipStreamBeginCapture must return error + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_DetectingInvalidCapture") { - hipStream_t stream1, stream2; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - hipEvent_t event; - HIP_CHECK(hipEventCreate(&event)); - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(event, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, event, 0)); - dummyKernel<<<1, 1, 0, stream1>>>(); - // Since stream2 is already in capture mode due to event wait - // hipStreamBeginCapture on stream2 is expected to return error. - REQUIRE(hipSuccess != hipStreamBeginCapture(stream2, - hipStreamCaptureModeGlobal)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); +TEST_CASE("Unit_hipStreamBeginCapture_Negative_DetectingInvalidCapture") { + StreamsGuard streams(2); + EventsGuard events(1); + hipEvent_t event = events[0]; + + HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event, streams[0])); + HIP_CHECK(hipStreamWaitEvent(streams[1], event, 0)); + 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), + hipErrorIllegalState); } -/* Test scenario 12 + +/** + * Test Description + * ------------------------ + * - Test to verify wtream reuse. Capture multiple graphs from the same + * stream. Validate graphs are captured correctly + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_CapturingMultGraphsFrom1Strm") { - hipStream_t stream1; - HIP_CHECK(hipStreamCreate(&stream1)); - hipGraph_t graph[3]; +TEST_CASE("Unit_hipStreamBeginCapture_Positive_CapturingMultGraphsFrom1Strm") { + hipGraph_t graphs[3]; + + StreamGuard stream_guard(Streams::created); + hipStream_t stream1 = stream_guard.stream(); + // Create a device memory of size int and initialize it to 0 - int *devMem{nullptr}, *hostMem{nullptr}; - hostMem = reinterpret_cast(malloc(sizeof(int))); - HIP_CHECK(hipMalloc(&devMem, sizeof(int))); + LinearAllocGuard hostMem_g(LinearAllocs::malloc, sizeof(int)); + LinearAllocGuard devMem_g(LinearAllocs::hipMalloc, sizeof(int)); + int* hostMem = hostMem_g.host_ptr(); + int* devMem = devMem_g.ptr(); HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); HIP_CHECK(hipDeviceSynchronize()); - // Capture Graph1 - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph[0])); - // Capture Graph2 - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph[1])); - // Capture Graph3 - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph[2])); + + 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(hipStreamEndCapture(stream1, &graphs[i])); + } // Instantiate and execute all graphs for (int i = 0; i < 3; i++) { hipGraphExec_t graphExec{nullptr}; HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); - HIP_CHECK(hipGraphInstantiate(&graphExec, graph[i], nullptr, - nullptr, 0)); + HIP_CHECK(hipGraphInstantiate(&graphExec, graphs[i], nullptr, nullptr, 0)); HIP_CHECK(hipGraphLaunch(graphExec, stream1)); HIP_CHECK(hipStreamSynchronize(stream1)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); REQUIRE((*hostMem) == (i + 1)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graphs[i])); } - HIP_CHECK(hipFree(devMem)); - free(hostMem); - for (int i = 0; i < 3; i++) { - HIP_CHECK(hipGraphDestroy(graph[i])); - } - HIP_CHECK(hipStreamDestroy(stream1)); } + #if HT_NVIDIA -/* Test scenario 13 +/** + * Test Description + * ------------------------ + * - Test to verify synchronization during stream capture returns an error: + * -# Synchronize stream during capture + * -# Synchronize device during capture + * -# Synchronize event during capture + * -# Query stream during capture + * -# Query for an event during capture + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_CheckingSyncDuringCapture") { - hipStream_t stream; - HIP_CHECK(hipStreamCreate(&stream)); +TEST_CASE("Unit_hipStreamBeginCapture_Negative_CheckingSyncDuringCapture") { + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + EventsGuard events_guard(1); + hipEvent_t e = events_guard[0]; + + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); SECTION("Synchronize stream during capture") { - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - REQUIRE(hipErrorStreamCaptureUnsupported == - hipStreamSynchronize(stream)); + HIP_CHECK_ERROR(hipStreamSynchronize(stream), hipErrorStreamCaptureUnsupported); } SECTION("Synchronize device during capture") { - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - REQUIRE(hipErrorStreamCaptureUnsupported == hipDeviceSynchronize()); + HIP_CHECK_ERROR(hipDeviceSynchronize(), hipErrorStreamCaptureUnsupported); } SECTION("Synchronize event during capture") { - hipEvent_t e; - HIP_CHECK(hipEventCreate(&e)); HIP_CHECK(hipEventRecord(e, stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - REQUIRE(hipErrorStreamCaptureUnsupported == hipEventSynchronize(e)); - HIP_CHECK(hipEventDestroy(e)); - } - SECTION("Wait for an event during capture") { - hipEvent_t e; - HIP_CHECK(hipEventCreate(&e)); - HIP_CHECK(hipEventRecord(e, stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - REQUIRE(hipErrorStreamCaptureIsolation == - hipStreamWaitEvent(stream, e, 0)); - HIP_CHECK(hipEventDestroy(e)); + HIP_CHECK_ERROR(hipEventSynchronize(e), hipErrorCapturedEvent); } SECTION("Query stream during capture") { - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - REQUIRE(hipErrorStreamCaptureUnsupported == hipStreamQuery(stream)); + HIP_CHECK_ERROR(hipStreamQuery(stream), hipErrorStreamCaptureUnsupported); } SECTION("Query for an event during capture") { - hipEvent_t e; - HIP_CHECK(hipEventCreate(&e)); HIP_CHECK(hipEventRecord(e, stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - REQUIRE(hipSuccess != hipEventQuery(e)); - HIP_CHECK(hipEventDestroy(e)); + HIP_CHECK_ERROR(hipEventQuery(e), hipErrorCapturedEvent); + } +} + +/** + * Test Description + * ------------------------ + * - Test to verify unsafe API calls during stream capture with initiated + * with hipStreamCaptureModeGlobal and hipStreamCaptureModeThreadLocal return an + * error: + * -# hipMalloc during capture + * -# hipMemcpy during capture + * -# hipMemset during capture + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamBeginCapture_Negative_UnsafeCallsDuringCapture") { + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + LinearAllocGuard hostMem(LinearAllocs::malloc, sizeof(int)); + LinearAllocGuard devMem(LinearAllocs::hipMalloc, sizeof(int)); + + int* devMem2; + + const hipStreamCaptureMode captureMode = + GENERATE(hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal); + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + SECTION("hipMalloc during capture") { + HIP_CHECK_ERROR(hipMalloc(&devMem2, sizeof(int)), hipErrorStreamCaptureUnsupported); + } + SECTION("hipMemcpy during capture") { + 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(hipStreamDestroy(stream)); } #endif -/* Test scenario 14 + +/** + * Test Description + * ------------------------ + * - Test to verify end stream capture when the stream capture is still in + * progress: + * -# Abruptly end stream capture when stream capture is in progress in + * forked stream. hipStreamEndCapture must return an error + * -# Abruptly end stream capture when operations in forked stream are + * still waiting to be captured. hipStreamEndCapture must return an error + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_EndingCapturewhenCaptureInProgress") { - hipStream_t stream1, stream2; - hipGraph_t graph; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); +TEST_CASE("Unit_hipStreamBeginCapture_Negative_EndingCapturewhenCaptureInProgress") { + hipGraph_t graph{nullptr}; + + StreamsGuard streams_guard(2); + hipStream_t stream1 = streams_guard[0]; + hipStream_t stream2 = streams_guard[1]; + SECTION("Abruptly end strm capture when in progress in forked strm") { - hipEvent_t e; + EventsGuard events_guard(1); + hipEvent_t e = events_guard[0]; HIP_CHECK(hipEventCreate(&e)); HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); dummyKernel<<<1, 1, 0, stream1>>>(); HIP_CHECK(hipEventRecord(e, stream1)); HIP_CHECK(hipStreamWaitEvent(stream2, e, 0)); dummyKernel<<<1, 1, 0, stream2>>>(); - REQUIRE(hipErrorStreamCaptureUnjoined == - hipStreamEndCapture(stream1, &graph)); - HIP_CHECK(hipEventDestroy(e)); + HIP_CHECK_ERROR(hipStreamEndCapture(stream1, &graph), hipErrorStreamCaptureUnjoined); } SECTION("End strm capture when forked strm still has operations") { - hipEvent_t e1, e2; - HIP_CHECK(hipEventCreate(&e1)); - HIP_CHECK(hipEventCreate(&e2)); + EventsGuard events_guard(2); + hipEvent_t e1 = events_guard[0]; + hipEvent_t e2 = events_guard[1]; HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); dummyKernel<<<1, 1, 0, stream1>>>(); HIP_CHECK(hipEventRecord(e1, stream1)); @@ -931,18 +919,22 @@ TEST_CASE("Unit_hipStreamBeginCapture_EndingCapturewhenCaptureInProgress") { HIP_CHECK(hipEventRecord(e2, stream2)); HIP_CHECK(hipStreamWaitEvent(stream1, e2, 0)); dummyKernel<<<1, 1, 0, stream2>>>(); - REQUIRE(hipErrorStreamCaptureUnjoined == - hipStreamEndCapture(stream1, &graph)); - HIP_CHECK(hipEventDestroy(e2)); - HIP_CHECK(hipEventDestroy(e1)); + HIP_CHECK_ERROR(hipStreamEndCapture(stream1, &graph), hipErrorStreamCaptureUnjoined); } - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } - -/* Test scenario 15 +/** + * Test Description + * ------------------------ + * - Testing independent stream capture using multiple GPUs. Capture a stream + * in each device context and execute the captured graph in the context GPU + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_MultiGPU") { +TEST_CASE("Unit_hipStreamBeginCapture_Positive_MultiGPU") { int devcount = 0; HIP_CHECK(hipGetDeviceCount(&devcount)); // If only single GPU is detected then return @@ -950,40 +942,36 @@ TEST_CASE("Unit_hipStreamBeginCapture_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)); + hostMem = reinterpret_cast(malloc(sizeof(int*) * devcount)); REQUIRE(hostMem != nullptr); - devMem = reinterpret_cast(malloc(sizeof(int*)*devcount)); + 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)); HIP_CHECK(hipStreamCreate(&stream[dev])); hostMem[dev] = reinterpret_cast(malloc(sizeof(int))); HIP_CHECK(hipMalloc(&devMem[dev], sizeof(int))); - HIP_CHECK(hipStreamBeginCapture(stream[dev], - hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamBeginCapture(stream[dev], hipStreamCaptureModeGlobal)); HIP_CHECK(hipMemsetAsync(devMem[dev], 0, sizeof(int), stream[dev])); 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 @@ -1004,291 +992,278 @@ TEST_CASE("Unit_hipStreamBeginCapture_MultiGPU") { free(stream); free(graph); } -/* Test scenario 16 + +/** + * Test Description + * ------------------------ + * - Test Nested Stream Capture Functionality: Create 3 streams. Capture s1, + * record event e1 on s1, wait for event e1 on s2 and queue operations in s1. + * Record event e2 on s2 and wait for it on s3. Queue operations on both s2 and + * s3. Record event e4 on s3 and wait for it in s1. Record event e3 on s2 and + * wait for it in s1. End stream capture on s1. Execute the graph and verify the + * result. + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_nestedStreamCapture") { - hipGraph_t graph; - hipStream_t stream1, stream2, stream3; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipStreamCreate(&stream3)); - hipEvent_t e1, e2, e3, e4; - HIP_CHECK(hipEventCreate(&e1)); - HIP_CHECK(hipEventCreate(&e2)); - HIP_CHECK(hipEventCreate(&e3)); - HIP_CHECK(hipEventCreate(&e4)); +TEST_CASE("Unit_hipStreamBeginCapture_Positive_nestedStreamCapture") { + constexpr int INCREMENT_KERNEL_FINALEXP_VAL = 7; + + hipGraph_t graph{nullptr}; + StreamsGuard streams(3); + EventsGuard events(4); + // Create a device memory of size int and initialize it to 0 - int *devMem{nullptr}, *hostMem{nullptr}; - hostMem = reinterpret_cast(malloc(sizeof(int))); - REQUIRE(hostMem != nullptr); - HIP_CHECK(hipMalloc(&devMem, sizeof(int))); - HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + LinearAllocGuard hostMem_g(LinearAllocs::malloc, sizeof(int)); + LinearAllocGuard devMem_g(LinearAllocs::hipMalloc, sizeof(int)); + HIP_CHECK(hipMemset(devMem_g.ptr(), 0, sizeof(int))); HIP_CHECK(hipDeviceSynchronize()); // Start Capturing stream1 - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(e1, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); - HIP_CHECK(hipEventRecord(e2, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream3, e2, 0)); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - incrementKernel<<<1, 1, 0, stream2>>>(devMem); - incrementKernel<<<1, 1, 0, stream3>>>(devMem); - incrementKernel<<<1, 1, 0, stream1>>>(devMem); - incrementKernel<<<1, 1, 0, stream2>>>(devMem); - incrementKernel<<<1, 1, 0, stream3>>>(devMem); - incrementKernel<<<1, 1, 0, stream3>>>(devMem); - HIP_CHECK(hipEventRecord(e3, stream2)); - HIP_CHECK(hipEventRecord(e4, stream3)); - HIP_CHECK(hipStreamWaitEvent(stream1, e4, 0)); - HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); - HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(events[0], streams[0])); + HIP_CHECK(hipStreamWaitEvent(streams[1], events[0], 0)); + HIP_CHECK(hipEventRecord(events[1], streams[1])); + HIP_CHECK(hipStreamWaitEvent(streams[2], events[1], 0)); + incrementKernel<<<1, 1, 0, streams[0]>>>(devMem_g.ptr()); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem_g.ptr()); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem_g.ptr()); + incrementKernel<<<1, 1, 0, streams[0]>>>(devMem_g.ptr()); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem_g.ptr()); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem_g.ptr()); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem_g.ptr()); + HIP_CHECK(hipEventRecord(events[2], streams[1])); + 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(hipStreamEndCapture(streams[0], &graph)); // End Capture // Reset device memory - HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + HIP_CHECK(hipMemset(devMem_g.ptr(), 0, sizeof(int))); HIP_CHECK(hipDeviceSynchronize()); // Create Executable Graphs hipGraphExec_t graphExec{nullptr}; HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, stream1)); - HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipGraphLaunch(graphExec, streams[0])); + HIP_CHECK(hipStreamSynchronize(streams[0])); + REQUIRE((*hostMem_g.host_ptr()) == INCREMENT_KERNEL_FINALEXP_VAL); + HIP_CHECK(hipGraphExecDestroy(graphExec)); - REQUIRE((*hostMem) == INCREMENT_KERNEL_FINALEXP_VAL); - HIP_CHECK(hipFree(devMem)); - free(hostMem); HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipEventDestroy(e4)); - HIP_CHECK(hipEventDestroy(e3)); - HIP_CHECK(hipEventDestroy(e2)); - HIP_CHECK(hipEventDestroy(e1)); - HIP_CHECK(hipStreamDestroy(stream3)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); -} -/* Test scenario 17 - */ -TEST_CASE("Unit_hipStreamBeginCapture_streamReuse") { - hipGraph_t graph1, graph2, graph3; - hipStream_t stream1, stream2, stream3; - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipStreamCreate(&stream3)); - hipEvent_t e1, e2, e3, e4; - HIP_CHECK(hipEventCreate(&e1)); - HIP_CHECK(hipEventCreate(&e2)); - HIP_CHECK(hipEventCreate(&e3)); - HIP_CHECK(hipEventCreate(&e4)); - // Create a device memory of size int and initialize it to 0 - int *devMem1{nullptr}, *hostMem1{nullptr}, *devMem2{nullptr}, - *hostMem2{nullptr}, *devMem3{nullptr}, *hostMem3{nullptr}; - HipTest::initArrays(&devMem1, &devMem2, &devMem3, - &hostMem1, &hostMem2, &hostMem3, 1, false); - HIP_CHECK(hipMemset(devMem1, 0, sizeof(int))); - HIP_CHECK(hipMemset(devMem2, 0, sizeof(int))); - HIP_CHECK(hipMemset(devMem3, 0, sizeof(int))); - HIP_CHECK(hipDeviceSynchronize()); - // Start Capturing stream1 - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(e1, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); - HIP_CHECK(hipEventRecord(e2, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream3, e2, 0)); - incrementKernel<<<1, 1, 0, stream1>>>(devMem1); - incrementKernel<<<1, 1, 0, stream2>>>(devMem1); - incrementKernel<<<1, 1, 0, stream3>>>(devMem1); - incrementKernel<<<1, 1, 0, stream1>>>(devMem1); - incrementKernel<<<1, 1, 0, stream2>>>(devMem1); - incrementKernel<<<1, 1, 0, stream3>>>(devMem1); - incrementKernel<<<1, 1, 0, stream3>>>(devMem1); - HIP_CHECK(hipEventRecord(e3, stream2)); - HIP_CHECK(hipEventRecord(e4, stream3)); - HIP_CHECK(hipStreamWaitEvent(stream1, e4, 0)); - HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); - HIP_CHECK(hipMemcpyAsync(hostMem1, devMem1, sizeof(int), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); // End Capture - // Start capturing graph2 from stream 2 - HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); - incrementKernel<<<1, 1, 0, stream2>>>(devMem2); - incrementKernel<<<1, 1, 0, stream2>>>(devMem2); - incrementKernel<<<1, 1, 0, stream2>>>(devMem2); - HIP_CHECK(hipMemcpyAsync(hostMem2, devMem2, sizeof(int), - hipMemcpyDefault, stream2)); - HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); // End Capture - // Start capturing graph3 from stream 3 - HIP_CHECK(hipStreamBeginCapture(stream3, hipStreamCaptureModeGlobal)); - incrementKernel<<<1, 1, 0, stream3>>>(devMem3); - incrementKernel<<<1, 1, 0, stream3>>>(devMem3); - incrementKernel<<<1, 1, 0, stream3>>>(devMem3); - incrementKernel<<<1, 1, 0, stream3>>>(devMem3); - incrementKernel<<<1, 1, 0, stream3>>>(devMem3); - HIP_CHECK(hipMemcpyAsync(hostMem3, devMem3, sizeof(int), - hipMemcpyDefault, stream3)); - HIP_CHECK(hipStreamEndCapture(stream3, &graph3)); // End Capture - // Reset device memory - HIP_CHECK(hipMemset(devMem1, 0, sizeof(int))); - HIP_CHECK(hipMemset(devMem2, 0, sizeof(int))); - HIP_CHECK(hipMemset(devMem3, 0, sizeof(int))); - HIP_CHECK(hipDeviceSynchronize()); - // Create Executable Graphs - hipGraphExec_t graphExec{nullptr}; - // Verify graph1 - HIP_CHECK(hipGraphInstantiate(&graphExec, graph1, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, stream1)); - HIP_CHECK(hipStreamSynchronize(stream1)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - REQUIRE((*hostMem1) == INCREMENT_KERNEL_FINALEXP_VAL); - // Verify graph2 - HIP_CHECK(hipGraphInstantiate(&graphExec, graph2, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, stream2)); - HIP_CHECK(hipStreamSynchronize(stream2)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - REQUIRE((*hostMem2) == 3); - // Verify graph3 - HIP_CHECK(hipGraphInstantiate(&graphExec, graph3, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, stream3)); - HIP_CHECK(hipStreamSynchronize(stream3)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - REQUIRE((*hostMem3) == 5); - HipTest::freeArrays(devMem1, devMem2, devMem3, - hostMem1, hostMem2, hostMem3, false); - HIP_CHECK(hipGraphDestroy(graph1)); - HIP_CHECK(hipGraphDestroy(graph2)); - HIP_CHECK(hipGraphDestroy(graph3)); - HIP_CHECK(hipEventDestroy(e4)); - HIP_CHECK(hipEventDestroy(e3)); - HIP_CHECK(hipEventDestroy(e2)); - HIP_CHECK(hipEventDestroy(e1)); - HIP_CHECK(hipStreamDestroy(stream3)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } -/* Test scenario 18 +/** + * Test Description + * ------------------------ + * - Test Nested Stream Capture Functionality: Create 3 streams. Capture s1, + * record event e1 on s1, wait for event e1 on s2 and queue operations in s1. + * Record event e2 on s2 and wait for it on s3. Queue operations on both s2 and + * s3. Record event e4 on s3 and wait for it in s1. Record event e3 on s2 and + * wait for it in s1. End stream capture on s1. Queue operations on both s2 and + * s3, and capture their graphs. Execute the graphs and verify the result. + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_captureComplexGraph") { - hipGraph_t graph; - hipStream_t stream1, stream2, stream3, stream4, stream5; - // Stream and event create - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipStreamCreate(&stream3)); - HIP_CHECK(hipStreamCreate(&stream4)); - HIP_CHECK(hipStreamCreate(&stream5)); - hipEvent_t e0, e1, e2, e3, e4, e5, e6; - HIP_CHECK(hipEventCreate(&e0)); - HIP_CHECK(hipEventCreate(&e1)); - HIP_CHECK(hipEventCreate(&e2)); - HIP_CHECK(hipEventCreate(&e3)); - HIP_CHECK(hipEventCreate(&e4)); - HIP_CHECK(hipEventCreate(&e5)); - HIP_CHECK(hipEventCreate(&e6)); +TEST_CASE("Unit_hipStreamBeginCapture_Positive_streamReuse") { + constexpr int increment_kernel_vals[3] = {7, 3, 5}; + + 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)); + + 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)); + HIP_CHECK(hipMemset(devMem[i], 0, sizeof(int))); + } + HIP_CHECK(hipDeviceSynchronize()); + // Start Capturing stream1 + HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(events[0], streams[0])); + HIP_CHECK(hipStreamWaitEvent(streams[1], events[0], 0)); + HIP_CHECK(hipEventRecord(events[1], streams[1])); + HIP_CHECK(hipStreamWaitEvent(streams[2], events[1], 0)); + incrementKernel<<<1, 1, 0, streams[0]>>>(devMem[0]); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem[0]); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[0]); + incrementKernel<<<1, 1, 0, streams[0]>>>(devMem[0]); + incrementKernel<<<1, 1, 0, streams[1]>>>(devMem[0]); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[0]); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[0]); + HIP_CHECK(hipEventRecord(events[2], streams[1])); + 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(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(hipStreamEndCapture(streams[1], &graphs[1])); // End Capture + // Start capturing graph3 from stream 3 + HIP_CHECK(hipStreamBeginCapture(streams[2], hipStreamCaptureModeGlobal)); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[2]); + incrementKernel<<<1, 1, 0, streams[2]>>>(devMem[2]); + 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(hipStreamEndCapture(streams[2], &graphs[2])); // End Capture + // Reset device memory + HIP_CHECK(hipMemset(devMem[0], 0, sizeof(int))); + HIP_CHECK(hipMemset(devMem[1], 0, sizeof(int))); + HIP_CHECK(hipMemset(devMem[2], 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Create Executable Graphs and verify graphs + for (int i = 0; i < 3; i++) { + hipGraphExec_t graphExec{nullptr}; + HIP_CHECK(hipMemset(devMem[i], 0, sizeof(int))); + HIP_CHECK(hipGraphInstantiate(&graphExec, graphs[i], nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streams[i])); + HIP_CHECK(hipStreamSynchronize(streams[i])); + REQUIRE((*hostMem[i]) == increment_kernel_vals[i]); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graphs[i])); + } +} + +/** + * Test Description + * ------------------------ + * - Capture a complex graph containing multiple independent memcpy, kernel + * and host nodes. Launch the graph on random input data and validate the output + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamBeginCapture_Positive_captureComplexGraph") { + constexpr int GRIDSIZE = 256; + constexpr int BLOCKSIZE = 256; + constexpr int CONST_KER1_VAL = 3; + constexpr int CONST_KER2_VAL = 2; + constexpr int CONST_KER3_VAL = 5; + + hipGraph_t graph{nullptr}; + StreamsGuard streams(5); + EventsGuard events(7); // Allocate Device memory and Host memory - size_t N = GRIDSIZE*BLOCKSIZE; - int *Ah{nullptr}, *Bh{nullptr}, *Ch{nullptr}, *Ad{nullptr}, *Bd{nullptr}; - HipTest::initArrays(&Ad, &Bd, nullptr, &Ah, &Bh, &Ch, N, false); + 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)); + // Capture streams into graph - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(e0, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream4, e0, 0)); - HIP_CHECK(hipStreamWaitEvent(stream5, e0, 0)); - HIP_CHECK(hipMemcpyAsync(Ad, Ah, (N*sizeof(int)), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipMemcpyAsync(Bd, Bh, (N*sizeof(int)), - hipMemcpyDefault, stream5)); + 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])); hipHostFn_t fn = hostNodeCallback; - HIPCHECK(hipLaunchHostFunc(stream4, fn, nullptr)); - HIP_CHECK(hipEventRecord(e1, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); - int *Ad_2nd_half = Ad + N/2; - int *Ad_1st_half = Ad; - mymul<<>>(Ad_2nd_half, CONST_KER2_VAL); - mymul<<>>(Ad_1st_half, CONST_KER1_VAL); - HIP_CHECK(hipEventRecord(e2, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream3, e2, 0)); - mymul<<>>(Ad_1st_half, CONST_KER3_VAL); - HIPCHECK(hipLaunchHostFunc(stream3, fn, nullptr)); - HIP_CHECK(hipEventRecord(e6, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream1, e6, 0)); - HIP_CHECK(hipEventRecord(e5, stream5)); - HIP_CHECK(hipStreamWaitEvent(stream1, e5, 0)); - myadd<<>>(Ad, Bd); - HIP_CHECK(hipEventRecord(e3, stream3)); - HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); - HIP_CHECK(hipEventRecord(e4, stream4)); - HIP_CHECK(hipStreamWaitEvent(stream1, e4, 0)); - HIP_CHECK(hipMemcpyAsync(Ch, Ad, (N*sizeof(int)), - hipMemcpyDefault, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + 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); + HIP_CHECK(hipEventRecord(events[2], streams[1])); + HIP_CHECK(hipStreamWaitEvent(streams[2], events[2], 0)); + 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)); + HIP_CHECK(hipEventRecord(events[5], streams[4])); + HIP_CHECK(hipStreamWaitEvent(streams[0], events[5], 0)); + myadd<<>>(Ad.ptr(), Bd.ptr()); + HIP_CHECK(hipEventRecord(events[3], streams[2])); + 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(hipStreamEndCapture(streams[0], &graph)); // End Capture // Execute and test the graph - // Create Executable Graphs hipGraphExec_t graphExec{nullptr}; - // Verify graph1 HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - for (int iter = 0; iter < LAUNCH_ITERS; iter++) { - init_input(Ah, N); - init_input(Bh, N); - HIP_CHECK(hipGraphLaunch(graphExec, stream1)); - HIP_CHECK(hipStreamSynchronize(stream1)); + // Verify graph + for (int iter = 0; iter < kLaunchIters; iter++) { + std::fill_n(Ah.host_ptr(), N, iter); + std::fill_n(Bh.host_ptr(), N, iter); + HIP_CHECK(hipGraphLaunch(graphExec, streams[0])); + HIP_CHECK(hipStreamSynchronize(streams[0])); for (size_t i = 0; i < N; i++) { - if (i > (N/2 - 1)) { - REQUIRE(Ch[i] == (Bh[i] + Ah[i]*CONST_KER2_VAL)); + if (i > (N / 2 - 1)) { + REQUIRE(Ch.host_ptr()[i] == (Bh.host_ptr()[i] + Ah.host_ptr()[i] * CONST_KER2_VAL)); } else { - REQUIRE(Ch[i] == (Bh[i] + Ah[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)); } } } - REQUIRE(gCbackIter == (2*LAUNCH_ITERS)); + REQUIRE(gCbackIter == (2 * kLaunchIters)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); - // Free Device memory and Host memory - HipTest::freeArrays(Ad, Bd, nullptr, Ah, Bh, Ch, false); - // Destroy graph, events and streams HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipEventDestroy(e6)); - HIP_CHECK(hipEventDestroy(e5)); - HIP_CHECK(hipEventDestroy(e4)); - HIP_CHECK(hipEventDestroy(e3)); - HIP_CHECK(hipEventDestroy(e2)); - HIP_CHECK(hipEventDestroy(e1)); - HIP_CHECK(hipEventDestroy(e0)); - HIP_CHECK(hipStreamDestroy(stream5)); - HIP_CHECK(hipStreamDestroy(stream4)); - HIP_CHECK(hipStreamDestroy(stream3)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } -/* Test scenario 19 + +/** + * Test Description + * ------------------------ + * - Test to verify capturing empty streams (parent + forked streams) and + * validate the captured graph has no nodes + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamBeginCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipStreamBeginCapture_captureEmptyStreams") { - hipGraph_t graph; - hipStream_t stream1, stream2, stream3; +TEST_CASE("Unit_hipStreamBeginCapture_Positive_captureEmptyStreams") { + hipGraph_t graph{nullptr}; + // Stream and event create - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipStreamCreate(&stream3)); - hipEvent_t e0, e1, e2; - HIP_CHECK(hipEventCreate(&e0)); - HIP_CHECK(hipEventCreate(&e1)); - HIP_CHECK(hipEventCreate(&e2)); + StreamsGuard streams(3); + EventsGuard events(3); + // Capture streams into graph - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(e0, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, e0, 0)); - HIP_CHECK(hipStreamWaitEvent(stream3, e0, 0)); - HIP_CHECK(hipEventRecord(e1, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream1, e1, 0)); - HIP_CHECK(hipEventRecord(e2, stream3)); - HIP_CHECK(hipStreamWaitEvent(stream1, e2, 0)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + HIP_CHECK(hipStreamBeginCapture(streams[0], hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(events[0], streams[0])); + HIP_CHECK(hipStreamWaitEvent(streams[1], events[0], 0)); + HIP_CHECK(hipStreamWaitEvent(streams[2], events[0], 0)); + HIP_CHECK(hipEventRecord(events[1], streams[1])); + HIP_CHECK(hipStreamWaitEvent(streams[0], events[1], 0)); + HIP_CHECK(hipEventRecord(events[2], streams[2])); + HIP_CHECK(hipStreamWaitEvent(streams[0], events[2], 0)); + HIP_CHECK(hipStreamEndCapture(streams[0], &graph)); // End Capture size_t numNodes = 0; HIP_CHECK(hipGraphGetNodes(graph, nullptr, &numNodes)); REQUIRE(numNodes == 0); - // Destroy graph, events and streams + HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipEventDestroy(e2)); - HIP_CHECK(hipEventDestroy(e1)); - HIP_CHECK(hipEventDestroy(e0)); - HIP_CHECK(hipStreamDestroy(stream3)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipStreamDestroy(stream1)); } diff --git a/catch/unit/graph/hipStreamBeginCapture_old.cc b/catch/unit/graph/hipStreamBeginCapture_old.cc new file mode 100644 index 0000000000..80be624e8a --- /dev/null +++ b/catch/unit/graph/hipStreamBeginCapture_old.cc @@ -0,0 +1,1294 @@ +/* +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. +*/ + +/** +Testcase Scenarios : Functional + 1) Initiate stream capture with different modes on custom stream. + Capture stream sequence and replay the sequence in multiple iterations. + 2) End capture and validate that API returns captured graph for + all possible modes on custom stream. + 3) Initiate stream capture with different modes on hipStreamPerThread. + Capture stream sequence and replay the sequence in multiple iterations. + 4) End capture and validate that API returns captured graph for + all possible modes on hipStreamPerThread. + 5) Waiting on an event recorded on a captured stream. Initiate capture + on stream1, record an event on stream1, wait for the event on stream2, + end the stream1 capture and Initiate stream capture on stream2 + 5.1) Both streams are created with default flags. + 5.2) Both streams are created with flag = hipStreamCaptureModeGlobal. + 5.3) Both streams are created with different flags. + 5.4) Both streams are created with different priorities. + 5.5) Validate the number of nodes in both the captured graphs. + 6) Colligated Streams capture. Capture operation sequences queued in + 2 streams by overlapping the 2 captures. + 6.1) Both streams are created with default flags. + 6.2) Both streams are created with flag = hipStreamCaptureModeGlobal. + 6.3) Both streams are created with different flags. + 6.4) Both streams are created with different priorities. + 7) Extend the scenario 5.1 for 3 streamsss. + 8) Create 2 streams. Start capturing both stream1 and stream2 at the same + time. On stream1 queue memcpy, kernel and memcpy operations and on stream2 + queue memcpy, kernel and memcpy operations. Execute both the captured + graphs and validate the results. + 9) Capture 2 streams in parallel using threads. Execute the graphs in + sequence in main thread and validate the results. + 9.1) mode = hipStreamCaptureModeGlobal + 9.2) mode = hipStreamCaptureModeThreadLocal + 9.3) mode = hipStreamCaptureModeRelaxed + 10) Queue operations (increment kernels) in 3 streams. Start capturing + the streams after some operations have been queued. This scenario validates + that only operations queued after hipStreamBeginCapture are captured in + the graph. + 11) Detecting invalid capture. Create 2 streams s1 and s2. Start capturing + s1. Create event dependency between s1 and s2 using event record and event + wait. Try capturing s2. hipStreamBeginCapture must return error. + 12) Stream reuse. Capture multiple graphs from the same stream. Validate + graphs are captured correctly. + 13) Test different synchronization during stream capture. + 13.1) Test hipStreamSynchronize. Must return + hipErrorStreamCaptureUnsupported. + 13.2) Test hipDeviceSynchronize. Must return + hipErrorStreamCaptureUnsupported. + 13.3) Test hipDeviceSynchronize. Must return + hipEventSynchronize. + 13.4) Test hipStreamWaitEvent. Must return + hipErrorStreamCaptureIsolation. + 14) End Stream Capture when the stream capture is still in progress. + 14.1) Abruptly end stream capture when stream capture is in progress in + forked stream. hipStreamEndCapture must return + hipErrorStreamCaptureUnjoined. + 14.2) Abruptly end stream capture when operations in forked stream + are still waiting to be captured. hipStreamEndCapture must return + hipErrorStreamCaptureUnjoined. + 15) Testing independent stream capture using multiple GPUs. Capture + a stream in each device context and execute the captured graph in the + context GPU. + 16) Test Nested Stream Capture Functionality: Create 3 streams s1, s2 & s3. + Capture s1, record event e1 on s1, wait for event e1 on s2 and queue + operations in s1. Record event e2 on s2 and wait for it on s3. Queue + operations on both s2 and s3. Record event e4 on s3 and wait for it in s1. + Record event e3 on s2 and wait for it in s1. End stream capture on s1. + Execute the graph and verify the result. + 17) Forked Stream Reuse: In scenario 16, after end capture on s1, queue + operations on both s2 and s3, and capture their graphs. Execute both the + graphs and validate the functionality. + 18) Capture a complex graph containing multiple independent memcpy, kernel + and host nodes. Launch the graph on random input data and validate the + output. + 19) Capture empty streams (parent + forked streams) and validate the + functionality. +*/ + +#include +#include +#include + +#define INCREMENT_KERNEL_FINALEXP_VAL 7 +constexpr size_t N = 1000000; +constexpr int LAUNCH_ITERS = 50; +static int gCbackIter = 0; +#define GRIDSIZE 256 +#define BLOCKSIZE 256 +#define CONST_KER1_VAL 3 +#define CONST_KER2_VAL 2 +#define CONST_KER3_VAL 5 + +static __global__ void dummyKernel() { + return; +} + +static __global__ void incrementKernel(int *data) { + atomicAdd(data, 1); + return; +} + +static __global__ void myadd(int* A_d, int* B_d) { + int myId = threadIdx.x + blockDim.x * blockIdx.x; + A_d[myId] = A_d[myId] + B_d[myId]; +} + +static __global__ void mymul(int* devMem, int value) { + int myId = threadIdx.x + blockDim.x * blockIdx.x; + devMem[myId] = devMem[myId] * value; +} + +static void hostNodeCallback(void* data) { + REQUIRE(data == nullptr); + gCbackIter++; +} + +bool CaptureStreamAndLaunchGraph(float *A_d, float *C_d, float *A_h, + float *C_h, hipStreamCaptureMode mode, hipStream_t stream) { + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + size_t Nbytes = N * sizeof(float); + + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + + HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + + // Validate end capture is successful + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + REQUIRE(graphExec != nullptr); + + // Replay the recorded sequence multiple times + for (int i = 0; i < LAUNCH_ITERS; i++) { + HIP_CHECK(hipGraphLaunch(graphExec, stream)); + } + + HIP_CHECK(hipStreamSynchronize(stream)); + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); + + // Validate the computation + for (size_t i = 0; i < N; i++) { + if (C_h[i] != A_h[i] * A_h[i]) { + UNSCOPED_INFO("A and C not matching at " << i); + return false; + } + } + return true; +} + +/** + * Basic Functional Test for API capturing custom stream and replaying sequence. + * Test exercises the API on available/possible modes. + * Stream capture with different modes behave the same when supported/ + * safe apis are used in sequence. + */ +TEST_CASE("Unit_hipStreamBeginCapture_BasicFunctional") { + float *A_d, *C_d; + float *A_h, *C_h; + size_t Nbytes = N * sizeof(float); + hipStream_t stream; + bool ret; + + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + + // Fill with Phi + i + for (size_t i = 0; i < N; i++) { + A_h[i] = 1.618f + i; + } + + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + + SECTION("Capture stream and launch graph when mode is global") { + ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, + hipStreamCaptureModeGlobal, stream); + REQUIRE(ret == true); + } + + SECTION("Capture stream and launch graph when mode is local") { + ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, + hipStreamCaptureModeThreadLocal, stream); + REQUIRE(ret == true); + } + + SECTION("Capture stream and launch graph when mode is relaxed") { + ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, + hipStreamCaptureModeRelaxed, stream); + REQUIRE(ret == true); + } + + HIP_CHECK(hipStreamDestroy(stream)); + free(A_h); + free(C_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); +} + +/** + * Perform capture on hipStreamPerThread, launch the graph and verify results. + */ +TEST_CASE("Unit_hipStreamBeginCapture_hipStreamPerThread") { + float *A_d, *C_d; + float *A_h, *C_h; + size_t Nbytes = N * sizeof(float); + hipStream_t stream{hipStreamPerThread}; + bool ret; + + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + + // Fill with Phi + i + for (size_t i = 0; i < N; i++) { + A_h[i] = 1.618f + i; + } + + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + + SECTION("Capture hipStreamPerThread and launch graph when mode is global") { + ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, + hipStreamCaptureModeGlobal, stream); + REQUIRE(ret == true); + } + + SECTION("Capture hipStreamPerThread and launch graph when mode is local") { + ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, + hipStreamCaptureModeThreadLocal, stream); + REQUIRE(ret == true); + } + + SECTION("Capture hipStreamPerThread and launch graph when mode is relaxed") { + ret = CaptureStreamAndLaunchGraph(A_d, C_d, A_h, C_h, + hipStreamCaptureModeRelaxed, stream); + REQUIRE(ret == true); + } + + free(A_h); + free(C_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); +} + + +/* Test verifies hipStreamBeginCapture API Negative scenarios. + */ + +TEST_CASE("Unit_hipStreamBeginCapture_Negative") { + hipError_t ret; + hipStream_t stream{}; + HIP_CHECK(hipStreamCreate(&stream)); + + SECTION("Stream capture on legacy/null stream returns error code.") { + ret = hipStreamBeginCapture(nullptr, hipStreamCaptureModeGlobal); + REQUIRE(hipErrorStreamCaptureUnsupported == ret); + } + SECTION("Capturing hipStream status with same stream again") { + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + ret = hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal); + REQUIRE(hipErrorIllegalState == ret); + } + SECTION("Creating hipStream with invalid mode") { + ret = hipStreamBeginCapture(stream, hipStreamCaptureMode(-1)); + REQUIRE(hipErrorInvalidValue == ret); + } + HIP_CHECK(hipStreamDestroy(stream)); +} + +TEST_CASE("Unit_hipStreamBeginCapture_Basic") { + hipStream_t s1, s2, s3; + + HIP_CHECK(hipStreamCreate(&s1)); + HIP_CHECK(hipStreamBeginCapture(s1, hipStreamCaptureModeGlobal)); + + HIP_CHECK(hipStreamCreate(&s2)); + HIP_CHECK(hipStreamBeginCapture(s2, hipStreamCaptureModeThreadLocal)); + + HIP_CHECK(hipStreamCreate(&s3)); + HIP_CHECK(hipStreamBeginCapture(s3, hipStreamCaptureModeRelaxed)); + + HIP_CHECK(hipStreamDestroy(s1)); + HIP_CHECK(hipStreamDestroy(s2)); + HIP_CHECK(hipStreamDestroy(s3)); +} +/* Local Function + */ +static void interStrmEventSyncCapture(const hipStream_t &stream1, + const hipStream_t &stream2) { + hipGraph_t graph1, graph2; + hipEvent_t event; + hipGraphExec_t graphExec1{nullptr}, graphExec2{nullptr}; + HIP_CHECK(hipEventCreate(&event)); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, event, 0)); + dummyKernel<<<1, 1, 0, stream1>>>(); + HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); + HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, stream2>>>(); + dummyKernel<<<1, 1, 0, stream2>>>(); + HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); + // Create Executable Graphs + HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); + REQUIRE(graphExec1 != nullptr); + HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); + REQUIRE(graphExec2 != nullptr); + size_t numNodes1 = 0, numNodes2 = 0; + HIP_CHECK(hipGraphGetNodes(graph1, nullptr, &numNodes1)); + HIP_CHECK(hipGraphGetNodes(graph2, nullptr, &numNodes2)); + REQUIRE(numNodes1 == 1); + REQUIRE(numNodes2 == 2); + // Execute the Graphs + HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); + HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipStreamSynchronize(stream2)); + // Free + HIP_CHECK(hipGraphExecDestroy(graphExec2)); + HIP_CHECK(hipGraphExecDestroy(graphExec1)); + HIP_CHECK(hipGraphDestroy(graph2)); + HIP_CHECK(hipGraphDestroy(graph1)); + HIP_CHECK(hipEventDestroy(event)); +} +/* Local Function + */ +static void colligatedStrmCapture(const hipStream_t &stream1, + const hipStream_t &stream2) { + hipGraph_t graph1, graph2; + hipEvent_t event; + hipGraphExec_t graphExec1{nullptr}, graphExec2{nullptr}; + HIP_CHECK(hipEventCreate(&event)); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event, stream1)); + HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamWaitEvent(stream1, event, 0)); + dummyKernel<<<1, 1, 0, stream1>>>(); + HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); + dummyKernel<<<1, 1, 0, stream2>>>(); + HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); + // Validate end capture is successful + REQUIRE(graph2 != nullptr); + REQUIRE(graph1 != nullptr); + // Create Executable Graphs + HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); + REQUIRE(graphExec1 != nullptr); + HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); + REQUIRE(graphExec2 != nullptr); + // Execute the Graphs + HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); + HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipStreamSynchronize(stream2)); + // Free + HIP_CHECK(hipGraphExecDestroy(graphExec2)); + HIP_CHECK(hipGraphExecDestroy(graphExec1)); + HIP_CHECK(hipGraphDestroy(graph2)); + HIP_CHECK(hipGraphDestroy(graph1)); + HIP_CHECK(hipEventDestroy(event)); +} +/* Fill input Data + */ +static void init_input(int* a, size_t size) { + unsigned int seed = time(nullptr); + for (size_t i = 0; i < size; i++) { + a[i] = (HipTest::RAND_R(&seed) & 0xFF); + } +} +/* Validate Output + */ +static void validate_output(int* a, int *b, size_t size) { + for (size_t i = 0; i < size; i++) { + REQUIRE(a[i] == (b[i]*b[i])); + } +} +/* Local Function + */ +static void colligatedStrmCaptureFunc(const hipStream_t &stream1, + const hipStream_t &stream2) { + constexpr size_t size = 1024; + constexpr auto blocksPerCU = 6; + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, + threadsPerBlock, size); + hipGraph_t graph1, graph2; + 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}; + hipGraphExec_t graphExec1{nullptr}, graphExec2{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); + // Capture 2 streams + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipMemcpyAsync(inputVec_d1, inputVec_h1, sizeof(int) * size, + hipMemcpyDefault, stream1)); + HIP_CHECK(hipMemcpyAsync(inputVec_d2, inputVec_h2, sizeof(int) * size, + hipMemcpyDefault, stream2)); + HipTest::vector_square<<>>( + inputVec_d1, outputVec_d1, size); + HipTest::vector_square<<>>( + inputVec_d2, outputVec_d2, size); + HIP_CHECK(hipMemcpyAsync(outputVec_h1, outputVec_d1, sizeof(int) * size, + hipMemcpyDefault, stream1)); + HIP_CHECK(hipMemcpyAsync(outputVec_h2, outputVec_d2, sizeof(int) * size, + hipMemcpyDefault, stream2)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); + HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); + // Validate end capture is successful + REQUIRE(graph2 != nullptr); + REQUIRE(graph1 != nullptr); + // Create Executable Graphs + HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); + REQUIRE(graphExec1 != nullptr); + HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); + REQUIRE(graphExec2 != nullptr); + // Execute the Graphs + for (int iter = 0; iter < LAUNCH_ITERS; iter++) { + init_input(inputVec_h1, size); + init_input(inputVec_h2, size); + HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); + HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipStreamSynchronize(stream2)); + validate_output(outputVec_h1, inputVec_h1, size); + validate_output(outputVec_h2, inputVec_h2, size); + } + // 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); + HIP_CHECK(hipGraphExecDestroy(graphExec2)); + HIP_CHECK(hipGraphExecDestroy(graphExec1)); + HIP_CHECK(hipGraphDestroy(graph2)); + HIP_CHECK(hipGraphDestroy(graph1)); +} +/* Stream Capture thread function + */ +static void threadStrmCaptureFunc(hipStream_t stream, int *inputVec_d, +int *outputVec_d, int *inputVec_h, int *outputVec_h, hipGraph_t *graph, +size_t size, hipStreamCaptureMode mode) { + constexpr auto blocksPerCU = 6; + constexpr auto threadsPerBlock = 256; + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, + threadsPerBlock, size); + // Capture stream + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + HIP_CHECK(hipMemcpyAsync(inputVec_d, inputVec_h, sizeof(int) * size, + hipMemcpyDefault, stream)); + HipTest::vector_square<<>>( + inputVec_d, outputVec_d, size); + HIP_CHECK(hipMemcpyAsync(outputVec_h, outputVec_d, sizeof(int) * size, + hipMemcpyDefault, stream)); + HIP_CHECK(hipStreamEndCapture(stream, graph)); +} +/* Local Function for multithreaded tests + */ +static void multithreadedTest(hipStreamCaptureMode mode) { + hipStream_t stream1, stream2; + constexpr size_t size = 1024; + hipGraph_t graph1, graph2; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + 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}; + hipGraphExec_t graphExec1{nullptr}, graphExec2{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); + // Launch 2 threads to capture the 2 streams into graphs + std::thread t1(threadStrmCaptureFunc, stream1, inputVec_d1, + outputVec_d1, inputVec_h1, outputVec_h1, &graph1, size, mode); + std::thread t2(threadStrmCaptureFunc, stream2, inputVec_d2, + outputVec_d2, inputVec_h2, outputVec_h2, &graph2, size, mode); + t1.join(); + t2.join(); + // Create Executable Graphs + HIP_CHECK(hipGraphInstantiate(&graphExec1, graph1, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphInstantiate(&graphExec2, graph2, nullptr, nullptr, 0)); + // Execute the Graphs + for (int iter = 0; iter < LAUNCH_ITERS; iter++) { + init_input(inputVec_h1, size); + init_input(inputVec_h2, size); + HIP_CHECK(hipGraphLaunch(graphExec1, stream1)); + HIP_CHECK(hipGraphLaunch(graphExec2, stream2)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipStreamSynchronize(stream2)); + validate_output(outputVec_h1, inputVec_h1, size); + validate_output(outputVec_h2, inputVec_h2, size); + } + // 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); + HIP_CHECK(hipGraphExecDestroy(graphExec2)); + HIP_CHECK(hipGraphExecDestroy(graphExec1)); + HIP_CHECK(hipGraphDestroy(graph1)); + HIP_CHECK(hipGraphDestroy(graph2)); + HIP_CHECK(hipStreamDestroy(stream1)); + HIP_CHECK(hipStreamDestroy(stream2)); +} +/* Test scenario 5.1 + */ +TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_defaultflag") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + interStrmEventSyncCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 5.2 + */ +TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_blockingflag") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); + HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamNonBlocking)); + interStrmEventSyncCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 5.3 + */ +TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_diffflags") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); + HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamDefault)); + interStrmEventSyncCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 5.4 + */ +TEST_CASE("Unit_hipStreamBeginCapture_InterStrmEventSync_diffprio") { + hipStream_t stream1, stream2; + int minPriority = 0, maxPriority = 0; + HIP_CHECK(hipDeviceGetStreamPriorityRange(&minPriority, &maxPriority)); + HIP_CHECK(hipStreamCreateWithPriority(&stream1, hipStreamDefault, + minPriority)); + HIP_CHECK(hipStreamCreateWithPriority(&stream2, hipStreamDefault, + maxPriority)); + interStrmEventSyncCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 6.1 + */ +TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_defaultflag") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + colligatedStrmCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 6.2 + */ +TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_blockingflag") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); + HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamNonBlocking)); + colligatedStrmCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 6.3 + */ +TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_diffflags") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreateWithFlags(&stream1, hipStreamNonBlocking)); + HIP_CHECK(hipStreamCreateWithFlags(&stream2, hipStreamDefault)); + colligatedStrmCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 6.4 + */ +TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_diffprio") { + hipStream_t stream1, stream2; + int minPriority = 0, maxPriority = 0; + HIP_CHECK(hipDeviceGetStreamPriorityRange(&minPriority, &maxPriority)); + HIP_CHECK(hipStreamCreateWithPriority(&stream1, hipStreamDefault, + minPriority)); + HIP_CHECK(hipStreamCreateWithPriority(&stream2, hipStreamDefault, + maxPriority)); + colligatedStrmCapture(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 7 + */ +TEST_CASE("Unit_hipStreamBeginCapture_multiplestrms") { + hipStream_t stream1, stream2, stream3; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipStreamCreate(&stream3)); + hipGraph_t graph1, graph2, graph3; + size_t numNodes1 = 0, numNodes2 = 0, numNodes3 = 0; + SECTION("Capture Multiple stream with interdependent events") { + hipEvent_t event1, event2; + HIP_CHECK(hipEventCreate(&event1)); + HIP_CHECK(hipEventCreate(&event2)); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event1, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, event1, 0)); + dummyKernel<<<1, 1, 0, stream1>>>(); + HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); + HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event2, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream3, event2, 0)); + dummyKernel<<<1, 1, 0, stream2>>>(); + HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); + HIP_CHECK(hipStreamBeginCapture(stream3, hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, stream3>>>(); + HIP_CHECK(hipStreamEndCapture(stream3, &graph3)); + HIP_CHECK(hipGraphGetNodes(graph1, nullptr, &numNodes1)); + HIP_CHECK(hipGraphGetNodes(graph2, nullptr, &numNodes2)); + HIP_CHECK(hipGraphGetNodes(graph3, nullptr, &numNodes3)); + REQUIRE(numNodes1 == 1); + REQUIRE(numNodes2 == 1); + REQUIRE(numNodes3 == 1); + HIP_CHECK(hipEventDestroy(event2)); + HIP_CHECK(hipEventDestroy(event1)); + } + SECTION("Capture Multiple stream with single event") { + hipEvent_t event1; + HIP_CHECK(hipEventCreate(&event1)); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event1, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, event1, 0)); + HIP_CHECK(hipStreamWaitEvent(stream3, event1, 0)); + dummyKernel<<<1, 1, 0, stream1>>>(); + HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); + HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, stream2>>>(); + HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); + HIP_CHECK(hipStreamBeginCapture(stream3, hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, stream3>>>(); + HIP_CHECK(hipStreamEndCapture(stream3, &graph3)); + HIP_CHECK(hipGraphGetNodes(graph1, nullptr, &numNodes1)); + HIP_CHECK(hipGraphGetNodes(graph2, nullptr, &numNodes2)); + HIP_CHECK(hipGraphGetNodes(graph3, nullptr, &numNodes3)); + REQUIRE(numNodes1 == 1); + REQUIRE(numNodes2 == 1); + REQUIRE(numNodes3 == 1); + HIP_CHECK(hipEventDestroy(event1)); + } + HIP_CHECK(hipStreamDestroy(stream3)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 8 + */ +TEST_CASE("Unit_hipStreamBeginCapture_ColligatedStrmCapture_func") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + colligatedStrmCaptureFunc(stream1, stream2); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 9.1 + */ +TEST_CASE("Unit_hipStreamBeginCapture_Multithreaded_Global") { + multithreadedTest(hipStreamCaptureModeGlobal); +} +/* Test scenario 9.2 + */ +TEST_CASE("Unit_hipStreamBeginCapture_Multithreaded_ThreadLocal") { + multithreadedTest(hipStreamCaptureModeThreadLocal); +} +/* Test scenario 9.3 + */ +TEST_CASE("Unit_hipStreamBeginCapture_Multithreaded_Relaxed") { + multithreadedTest(hipStreamCaptureModeRelaxed); +} +/* Test scenario 10 + */ +TEST_CASE("Unit_hipStreamBeginCapture_CapturingFromWithinStrms") { + hipGraph_t graph; + hipStream_t stream1, stream2, stream3; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipStreamCreate(&stream3)); + hipEvent_t e1, e2, e3; + HIP_CHECK(hipEventCreate(&e1)); + HIP_CHECK(hipEventCreate(&e2)); + HIP_CHECK(hipEventCreate(&e3)); + // Create a device memory of size int and initialize it to 0 + int *devMem{nullptr}, *hostMem{nullptr}; + hostMem = reinterpret_cast(malloc(sizeof(int))); + HIP_CHECK(hipMalloc(&devMem, sizeof(int))); + HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Start Capturing stream1 + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(e1, stream1)); + incrementKernel<<<1, 1, 0, stream2>>>(devMem); + incrementKernel<<<1, 1, 0, stream2>>>(devMem); + incrementKernel<<<1, 1, 0, stream3>>>(devMem); + HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); + HIP_CHECK(hipStreamWaitEvent(stream3, e1, 0)); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + incrementKernel<<<1, 1, 0, stream2>>>(devMem); + incrementKernel<<<1, 1, 0, stream3>>>(devMem); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + incrementKernel<<<1, 1, 0, stream2>>>(devMem); + incrementKernel<<<1, 1, 0, stream3>>>(devMem); + incrementKernel<<<1, 1, 0, stream3>>>(devMem); + HIP_CHECK(hipEventRecord(e2, stream2)); + HIP_CHECK(hipEventRecord(e3, stream3)); + HIP_CHECK(hipStreamWaitEvent(stream1, e2, 0)); + HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); + HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + // Reset device memory + HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Create Executable Graphs + hipGraphExec_t graphExec{nullptr}; + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream1)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + REQUIRE((*hostMem) == INCREMENT_KERNEL_FINALEXP_VAL); + HIP_CHECK(hipFree(devMem)); + free(hostMem); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipEventDestroy(e3)); + HIP_CHECK(hipEventDestroy(e2)); + HIP_CHECK(hipEventDestroy(e1)); + HIP_CHECK(hipStreamDestroy(stream3)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 11 + */ +TEST_CASE("Unit_hipStreamBeginCapture_DetectingInvalidCapture") { + hipStream_t stream1, stream2; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + hipEvent_t event; + HIP_CHECK(hipEventCreate(&event)); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(event, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, event, 0)); + dummyKernel<<<1, 1, 0, stream1>>>(); + // Since stream2 is already in capture mode due to event wait + // hipStreamBeginCapture on stream2 is expected to return error. + REQUIRE(hipSuccess != hipStreamBeginCapture(stream2, + hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 12 + */ +TEST_CASE("Unit_hipStreamBeginCapture_CapturingMultGraphsFrom1Strm") { + hipStream_t stream1; + HIP_CHECK(hipStreamCreate(&stream1)); + hipGraph_t graph[3]; + // Create a device memory of size int and initialize it to 0 + int *devMem{nullptr}, *hostMem{nullptr}; + hostMem = reinterpret_cast(malloc(sizeof(int))); + HIP_CHECK(hipMalloc(&devMem, sizeof(int))); + HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Capture Graph1 + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph[0])); + // Capture Graph2 + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph[1])); + // Capture Graph3 + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph[2])); + // Instantiate and execute all graphs + for (int i = 0; i < 3; i++) { + hipGraphExec_t graphExec{nullptr}; + HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + HIP_CHECK(hipGraphInstantiate(&graphExec, graph[i], nullptr, + nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream1)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + REQUIRE((*hostMem) == (i + 1)); + } + HIP_CHECK(hipFree(devMem)); + free(hostMem); + for (int i = 0; i < 3; i++) { + HIP_CHECK(hipGraphDestroy(graph[i])); + } + HIP_CHECK(hipStreamDestroy(stream1)); +} +#if HT_NVIDIA +/* Test scenario 13 + */ +TEST_CASE("Unit_hipStreamBeginCapture_CheckingSyncDuringCapture") { + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + SECTION("Synchronize stream during capture") { + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + REQUIRE(hipErrorStreamCaptureUnsupported == + hipStreamSynchronize(stream)); + } + SECTION("Synchronize device during capture") { + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + REQUIRE(hipErrorStreamCaptureUnsupported == hipDeviceSynchronize()); + } + SECTION("Synchronize event during capture") { + hipEvent_t e; + HIP_CHECK(hipEventCreate(&e)); + HIP_CHECK(hipEventRecord(e, stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + REQUIRE(hipErrorStreamCaptureUnsupported == hipEventSynchronize(e)); + HIP_CHECK(hipEventDestroy(e)); + } + SECTION("Wait for an event during capture") { + hipEvent_t e; + HIP_CHECK(hipEventCreate(&e)); + HIP_CHECK(hipEventRecord(e, stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + REQUIRE(hipErrorStreamCaptureIsolation == + hipStreamWaitEvent(stream, e, 0)); + HIP_CHECK(hipEventDestroy(e)); + } + SECTION("Query stream during capture") { + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + REQUIRE(hipErrorStreamCaptureUnsupported == hipStreamQuery(stream)); + } + SECTION("Query for an event during capture") { + hipEvent_t e; + HIP_CHECK(hipEventCreate(&e)); + HIP_CHECK(hipEventRecord(e, stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + REQUIRE(hipSuccess != hipEventQuery(e)); + HIP_CHECK(hipEventDestroy(e)); + } + HIP_CHECK(hipStreamDestroy(stream)); +} +#endif +/* Test scenario 14 + */ +TEST_CASE("Unit_hipStreamBeginCapture_EndingCapturewhenCaptureInProgress") { + hipStream_t stream1, stream2; + hipGraph_t graph; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + SECTION("Abruptly end strm capture when in progress in forked strm") { + hipEvent_t e; + HIP_CHECK(hipEventCreate(&e)); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, stream1>>>(); + HIP_CHECK(hipEventRecord(e, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, e, 0)); + dummyKernel<<<1, 1, 0, stream2>>>(); + REQUIRE(hipErrorStreamCaptureUnjoined == + hipStreamEndCapture(stream1, &graph)); + HIP_CHECK(hipEventDestroy(e)); + } + SECTION("End strm capture when forked strm still has operations") { + hipEvent_t e1, e2; + HIP_CHECK(hipEventCreate(&e1)); + HIP_CHECK(hipEventCreate(&e2)); + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + dummyKernel<<<1, 1, 0, stream1>>>(); + HIP_CHECK(hipEventRecord(e1, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); + dummyKernel<<<1, 1, 0, stream2>>>(); + HIP_CHECK(hipEventRecord(e2, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream1, e2, 0)); + dummyKernel<<<1, 1, 0, stream2>>>(); + REQUIRE(hipErrorStreamCaptureUnjoined == + hipStreamEndCapture(stream1, &graph)); + HIP_CHECK(hipEventDestroy(e2)); + HIP_CHECK(hipEventDestroy(e1)); + } + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} + +/* Test scenario 15 + */ +TEST_CASE("Unit_hipStreamBeginCapture_MultiGPU") { + int devcount = 0; + HIP_CHECK(hipGetDeviceCount(&devcount)); + // If only single GPU is detected then return + if (devcount < 2) { + SUCCEED("skipping the testcases as numDevices < 2"); + return; + } + hipStream_t* stream = reinterpret_cast(malloc( + devcount*sizeof(hipStream_t))); + REQUIRE(stream != nullptr); + 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))); + // Capture stream in each device + for (int dev = 0; dev < devcount; dev++) { + HIP_CHECK(hipSetDevice(dev)); + HIP_CHECK(hipStreamCreate(&stream[dev])); + hostMem[dev] = reinterpret_cast(malloc(sizeof(int))); + HIP_CHECK(hipMalloc(&devMem[dev], sizeof(int))); + HIP_CHECK(hipStreamBeginCapture(stream[dev], + hipStreamCaptureModeGlobal)); + HIP_CHECK(hipMemsetAsync(devMem[dev], 0, sizeof(int), stream[dev])); + 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(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(hipGraphLaunch(graphExec[dev], stream[dev])); + } + // Validate output + for (int dev = 0; dev < devcount; dev++) { + HIP_CHECK(hipSetDevice(dev)); + HIP_CHECK(hipStreamSynchronize(stream[dev])); + REQUIRE((*hostMem[dev]) == (dev + 1)); + } + // Destroy all device resources + for (int dev = 0; dev < devcount; dev++) { + HIP_CHECK(hipSetDevice(dev)); + HIP_CHECK(hipGraphExecDestroy(graphExec[dev])); + HIP_CHECK(hipStreamDestroy(stream[dev])); + } + free(graphExec); + free(hostMem); + free(devMem); + free(stream); + free(graph); +} +/* Test scenario 16 + */ +TEST_CASE("Unit_hipStreamBeginCapture_nestedStreamCapture") { + hipGraph_t graph; + hipStream_t stream1, stream2, stream3; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipStreamCreate(&stream3)); + hipEvent_t e1, e2, e3, e4; + HIP_CHECK(hipEventCreate(&e1)); + HIP_CHECK(hipEventCreate(&e2)); + HIP_CHECK(hipEventCreate(&e3)); + HIP_CHECK(hipEventCreate(&e4)); + // Create a device memory of size int and initialize it to 0 + int *devMem{nullptr}, *hostMem{nullptr}; + hostMem = reinterpret_cast(malloc(sizeof(int))); + REQUIRE(hostMem != nullptr); + HIP_CHECK(hipMalloc(&devMem, sizeof(int))); + HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Start Capturing stream1 + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(e1, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); + HIP_CHECK(hipEventRecord(e2, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream3, e2, 0)); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + incrementKernel<<<1, 1, 0, stream2>>>(devMem); + incrementKernel<<<1, 1, 0, stream3>>>(devMem); + incrementKernel<<<1, 1, 0, stream1>>>(devMem); + incrementKernel<<<1, 1, 0, stream2>>>(devMem); + incrementKernel<<<1, 1, 0, stream3>>>(devMem); + incrementKernel<<<1, 1, 0, stream3>>>(devMem); + HIP_CHECK(hipEventRecord(e3, stream2)); + HIP_CHECK(hipEventRecord(e4, stream3)); + HIP_CHECK(hipStreamWaitEvent(stream1, e4, 0)); + HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); + HIP_CHECK(hipMemcpyAsync(hostMem, devMem, sizeof(int), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + // Reset device memory + HIP_CHECK(hipMemset(devMem, 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Create Executable Graphs + hipGraphExec_t graphExec{nullptr}; + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream1)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + REQUIRE((*hostMem) == INCREMENT_KERNEL_FINALEXP_VAL); + HIP_CHECK(hipFree(devMem)); + free(hostMem); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipEventDestroy(e4)); + HIP_CHECK(hipEventDestroy(e3)); + HIP_CHECK(hipEventDestroy(e2)); + HIP_CHECK(hipEventDestroy(e1)); + HIP_CHECK(hipStreamDestroy(stream3)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 17 + */ +TEST_CASE("Unit_hipStreamBeginCapture_streamReuse") { + hipGraph_t graph1, graph2, graph3; + hipStream_t stream1, stream2, stream3; + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipStreamCreate(&stream3)); + hipEvent_t e1, e2, e3, e4; + HIP_CHECK(hipEventCreate(&e1)); + HIP_CHECK(hipEventCreate(&e2)); + HIP_CHECK(hipEventCreate(&e3)); + HIP_CHECK(hipEventCreate(&e4)); + // Create a device memory of size int and initialize it to 0 + int *devMem1{nullptr}, *hostMem1{nullptr}, *devMem2{nullptr}, + *hostMem2{nullptr}, *devMem3{nullptr}, *hostMem3{nullptr}; + HipTest::initArrays(&devMem1, &devMem2, &devMem3, + &hostMem1, &hostMem2, &hostMem3, 1, false); + HIP_CHECK(hipMemset(devMem1, 0, sizeof(int))); + HIP_CHECK(hipMemset(devMem2, 0, sizeof(int))); + HIP_CHECK(hipMemset(devMem3, 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Start Capturing stream1 + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(e1, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); + HIP_CHECK(hipEventRecord(e2, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream3, e2, 0)); + incrementKernel<<<1, 1, 0, stream1>>>(devMem1); + incrementKernel<<<1, 1, 0, stream2>>>(devMem1); + incrementKernel<<<1, 1, 0, stream3>>>(devMem1); + incrementKernel<<<1, 1, 0, stream1>>>(devMem1); + incrementKernel<<<1, 1, 0, stream2>>>(devMem1); + incrementKernel<<<1, 1, 0, stream3>>>(devMem1); + incrementKernel<<<1, 1, 0, stream3>>>(devMem1); + HIP_CHECK(hipEventRecord(e3, stream2)); + HIP_CHECK(hipEventRecord(e4, stream3)); + HIP_CHECK(hipStreamWaitEvent(stream1, e4, 0)); + HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); + HIP_CHECK(hipMemcpyAsync(hostMem1, devMem1, sizeof(int), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph1)); // End Capture + // Start capturing graph2 from stream 2 + HIP_CHECK(hipStreamBeginCapture(stream2, hipStreamCaptureModeGlobal)); + incrementKernel<<<1, 1, 0, stream2>>>(devMem2); + incrementKernel<<<1, 1, 0, stream2>>>(devMem2); + incrementKernel<<<1, 1, 0, stream2>>>(devMem2); + HIP_CHECK(hipMemcpyAsync(hostMem2, devMem2, sizeof(int), + hipMemcpyDefault, stream2)); + HIP_CHECK(hipStreamEndCapture(stream2, &graph2)); // End Capture + // Start capturing graph3 from stream 3 + HIP_CHECK(hipStreamBeginCapture(stream3, hipStreamCaptureModeGlobal)); + incrementKernel<<<1, 1, 0, stream3>>>(devMem3); + incrementKernel<<<1, 1, 0, stream3>>>(devMem3); + incrementKernel<<<1, 1, 0, stream3>>>(devMem3); + incrementKernel<<<1, 1, 0, stream3>>>(devMem3); + incrementKernel<<<1, 1, 0, stream3>>>(devMem3); + HIP_CHECK(hipMemcpyAsync(hostMem3, devMem3, sizeof(int), + hipMemcpyDefault, stream3)); + HIP_CHECK(hipStreamEndCapture(stream3, &graph3)); // End Capture + // Reset device memory + HIP_CHECK(hipMemset(devMem1, 0, sizeof(int))); + HIP_CHECK(hipMemset(devMem2, 0, sizeof(int))); + HIP_CHECK(hipMemset(devMem3, 0, sizeof(int))); + HIP_CHECK(hipDeviceSynchronize()); + // Create Executable Graphs + hipGraphExec_t graphExec{nullptr}; + // Verify graph1 + HIP_CHECK(hipGraphInstantiate(&graphExec, graph1, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream1)); + HIP_CHECK(hipStreamSynchronize(stream1)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + REQUIRE((*hostMem1) == INCREMENT_KERNEL_FINALEXP_VAL); + // Verify graph2 + HIP_CHECK(hipGraphInstantiate(&graphExec, graph2, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream2)); + HIP_CHECK(hipStreamSynchronize(stream2)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + REQUIRE((*hostMem2) == 3); + // Verify graph3 + HIP_CHECK(hipGraphInstantiate(&graphExec, graph3, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, stream3)); + HIP_CHECK(hipStreamSynchronize(stream3)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + REQUIRE((*hostMem3) == 5); + HipTest::freeArrays(devMem1, devMem2, devMem3, + hostMem1, hostMem2, hostMem3, false); + HIP_CHECK(hipGraphDestroy(graph1)); + HIP_CHECK(hipGraphDestroy(graph2)); + HIP_CHECK(hipGraphDestroy(graph3)); + HIP_CHECK(hipEventDestroy(e4)); + HIP_CHECK(hipEventDestroy(e3)); + HIP_CHECK(hipEventDestroy(e2)); + HIP_CHECK(hipEventDestroy(e1)); + HIP_CHECK(hipStreamDestroy(stream3)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} + +/* Test scenario 18 + */ +TEST_CASE("Unit_hipStreamBeginCapture_captureComplexGraph") { + hipGraph_t graph; + hipStream_t stream1, stream2, stream3, stream4, stream5; + // Stream and event create + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipStreamCreate(&stream3)); + HIP_CHECK(hipStreamCreate(&stream4)); + HIP_CHECK(hipStreamCreate(&stream5)); + hipEvent_t e0, e1, e2, e3, e4, e5, e6; + HIP_CHECK(hipEventCreate(&e0)); + HIP_CHECK(hipEventCreate(&e1)); + HIP_CHECK(hipEventCreate(&e2)); + HIP_CHECK(hipEventCreate(&e3)); + HIP_CHECK(hipEventCreate(&e4)); + HIP_CHECK(hipEventCreate(&e5)); + HIP_CHECK(hipEventCreate(&e6)); + // Allocate Device memory and Host memory + size_t N = GRIDSIZE*BLOCKSIZE; + int *Ah{nullptr}, *Bh{nullptr}, *Ch{nullptr}, *Ad{nullptr}, *Bd{nullptr}; + HipTest::initArrays(&Ad, &Bd, nullptr, &Ah, &Bh, &Ch, N, false); + // Capture streams into graph + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(e0, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream4, e0, 0)); + HIP_CHECK(hipStreamWaitEvent(stream5, e0, 0)); + HIP_CHECK(hipMemcpyAsync(Ad, Ah, (N*sizeof(int)), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipMemcpyAsync(Bd, Bh, (N*sizeof(int)), + hipMemcpyDefault, stream5)); + hipHostFn_t fn = hostNodeCallback; + HIPCHECK(hipLaunchHostFunc(stream4, fn, nullptr)); + HIP_CHECK(hipEventRecord(e1, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, e1, 0)); + int *Ad_2nd_half = Ad + N/2; + int *Ad_1st_half = Ad; + mymul<<>>(Ad_2nd_half, CONST_KER2_VAL); + mymul<<>>(Ad_1st_half, CONST_KER1_VAL); + HIP_CHECK(hipEventRecord(e2, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream3, e2, 0)); + mymul<<>>(Ad_1st_half, CONST_KER3_VAL); + HIPCHECK(hipLaunchHostFunc(stream3, fn, nullptr)); + HIP_CHECK(hipEventRecord(e6, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream1, e6, 0)); + HIP_CHECK(hipEventRecord(e5, stream5)); + HIP_CHECK(hipStreamWaitEvent(stream1, e5, 0)); + myadd<<>>(Ad, Bd); + HIP_CHECK(hipEventRecord(e3, stream3)); + HIP_CHECK(hipStreamWaitEvent(stream1, e3, 0)); + HIP_CHECK(hipEventRecord(e4, stream4)); + HIP_CHECK(hipStreamWaitEvent(stream1, e4, 0)); + HIP_CHECK(hipMemcpyAsync(Ch, Ad, (N*sizeof(int)), + hipMemcpyDefault, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + // Execute and test the graph + // Create Executable Graphs + hipGraphExec_t graphExec{nullptr}; + // Verify graph1 + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + for (int iter = 0; iter < LAUNCH_ITERS; iter++) { + init_input(Ah, N); + init_input(Bh, N); + HIP_CHECK(hipGraphLaunch(graphExec, stream1)); + HIP_CHECK(hipStreamSynchronize(stream1)); + for (size_t i = 0; i < N; i++) { + if (i > (N/2 - 1)) { + REQUIRE(Ch[i] == (Bh[i] + Ah[i]*CONST_KER2_VAL)); + } else { + REQUIRE(Ch[i] == (Bh[i] + Ah[i]*CONST_KER1_VAL*CONST_KER3_VAL)); + } + } + } + REQUIRE(gCbackIter == (2*LAUNCH_ITERS)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); + // Free Device memory and Host memory + HipTest::freeArrays(Ad, Bd, nullptr, Ah, Bh, Ch, false); + // Destroy graph, events and streams + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipEventDestroy(e6)); + HIP_CHECK(hipEventDestroy(e5)); + HIP_CHECK(hipEventDestroy(e4)); + HIP_CHECK(hipEventDestroy(e3)); + HIP_CHECK(hipEventDestroy(e2)); + HIP_CHECK(hipEventDestroy(e1)); + HIP_CHECK(hipEventDestroy(e0)); + HIP_CHECK(hipStreamDestroy(stream5)); + HIP_CHECK(hipStreamDestroy(stream4)); + HIP_CHECK(hipStreamDestroy(stream3)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} +/* Test scenario 19 + */ +TEST_CASE("Unit_hipStreamBeginCapture_captureEmptyStreams") { + hipGraph_t graph; + hipStream_t stream1, stream2, stream3; + // Stream and event create + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipStreamCreate(&stream3)); + hipEvent_t e0, e1, e2; + HIP_CHECK(hipEventCreate(&e0)); + HIP_CHECK(hipEventCreate(&e1)); + HIP_CHECK(hipEventCreate(&e2)); + // Capture streams into graph + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(e0, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, e0, 0)); + HIP_CHECK(hipStreamWaitEvent(stream3, e0, 0)); + HIP_CHECK(hipEventRecord(e1, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream1, e1, 0)); + HIP_CHECK(hipEventRecord(e2, stream3)); + HIP_CHECK(hipStreamWaitEvent(stream1, e2, 0)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); // End Capture + size_t numNodes = 0; + HIP_CHECK(hipGraphGetNodes(graph, nullptr, &numNodes)); + REQUIRE(numNodes == 0); + // Destroy graph, events and streams + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipEventDestroy(e2)); + HIP_CHECK(hipEventDestroy(e1)); + HIP_CHECK(hipEventDestroy(e0)); + HIP_CHECK(hipStreamDestroy(stream3)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipStreamDestroy(stream1)); +} diff --git a/catch/unit/graph/hipStreamEndCapture.cc b/catch/unit/graph/hipStreamEndCapture.cc index 740764d4b2..22762268ab 100644 --- a/catch/unit/graph/hipStreamEndCapture.cc +++ b/catch/unit/graph/hipStreamEndCapture.cc @@ -17,421 +17,190 @@ OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** -Negative Testcase Scenarios : -1) Pass stream as nullptr and verify there is no crash, api returns error code. -2) Pass graph as nullptr and verify there is no crash, api returns error code. -3) Pass graph as nullptr and and stream as hipStreamPerThread verify there - is no crash, api returns error code. -4) End capture on stream where capture has not yet started and verify - error code is returned. -5) Destroy stream and try to end capture. -6) Destroy Graph and try to end capture. -7) Begin capture on a thread with mode other than hipStreamCaptureModeRelaxed - and try to end capture from different thread. Expect to return - hipErrorStreamCaptureWrongThread. -8) Start stream capture on stream1 using mode hipStreamCaptureModeRelaxed. - In stream1 queue a memcpy operation, queue a kernel square of a number operation. - Launch a thread. In the thread, queue a memcpy operation. End the capture on - stream1 and return the captured graph. Wait for the thread in main function. - Create an executable graph and launch the graph on input data and validate the - output. -9) Create 2 streams s1 and s2. Begin stream capture in s1, spawn a - captured fork stream on s2. Queue some operations - (like increment kernel) on both s1 and s2. End the stream capture - on s2 and verify the error returned by the End capture. -10)Create 2 streams s1 and s2. Begin stream capture in s1 and spawn a captured - fork stream s2. In main thread, queue a memcpy operation on s1. - Launch a thread, queue a memcpy operation on s2. Perform hipEventRecord on - s2 and wait Event on S1. Wait for the thread to complete. Queue operations - kernel addition(Cd = Ad + Bd) operation and memcpy(Ch <- Cd) in s1. End the - stream capture in s1. Create an executable graph and launch the graph on input - data and validate the output. -*/ - #include #include +#include + +#include "stream_capture_common.hh" + +/** + * @addtogroup hipStreamEndCapture hipStreamEndCapture + * @{ + * @ingroup GraphTest + * `hipStreamEndCapture(hipStream_t stream, hipGraph_t *pGraph)` - + * ends capture on a stream, returning the captured graph + */ + +/** + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# End capture on legacy/null stream + * -# End capture when graph is nullptr + * -# End capture on stream where capture has not yet started + * -# Destroy stream and try to end capture + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamEndCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamEndCapture_Negative_Parameters") { + hipGraph_t graph{nullptr}; + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); -TEST_CASE("Unit_hipStreamEndCapture_Negative") { - hipError_t ret; SECTION("Pass stream as nullptr") { - hipGraph_t graph; - ret = hipStreamEndCapture(nullptr, &graph); - REQUIRE(hipErrorIllegalState == ret); + HIP_CHECK_ERROR(hipStreamEndCapture(nullptr, &graph), hipErrorIllegalState); } #if HT_NVIDIA SECTION("Pass graph as nullptr") { - hipStream_t stream; - HIP_CHECK(hipStreamCreate(&stream)); - ret = hipStreamEndCapture(stream, nullptr); - REQUIRE(hipErrorInvalidValue == ret); - HIP_CHECK(hipStreamDestroy(stream)); - } - SECTION("Pass graph as nullptr and stream as hipStreamPerThread") { - ret = hipStreamEndCapture(hipStreamPerThread, nullptr); - REQUIRE(hipErrorInvalidValue == ret); + HIP_CHECK_ERROR(hipStreamEndCapture(stream, nullptr), hipErrorInvalidValue); } #endif SECTION("End capture on stream where capture has not yet started") { - hipStream_t stream; - hipGraph_t graph; - HIP_CHECK(hipStreamCreate(&stream)); - ret = hipStreamEndCapture(stream, &graph); - REQUIRE(hipErrorIllegalState == ret); - HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK_ERROR(hipStreamEndCapture(stream, &graph), hipErrorIllegalState); } SECTION("Destroy stream and try to end capture") { - hipStream_t stream; - hipGraph_t graph; - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipStreamDestroy(stream)); - ret = hipStreamEndCapture(stream, &graph); - REQUIRE(hipErrorContextIsDestroyed == ret); - } - SECTION("Destroy graph and try to end capture in between") { - hipStream_t stream{nullptr}; - hipGraph_t graph{nullptr}; - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - constexpr size_t N = 100000; - size_t Nbytes = N * sizeof(float); - float *A_d, *C_d; - float *A_h, *C_h; - - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - - // Fill with Phi + i - for (size_t i = 0; i < N; i++) { - A_h[i] = 1.618f + i; - } - - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); - - HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); - - HIP_CHECK(hipGraphDestroy(graph)); - ret = hipStreamEndCapture(stream, &graph); - REQUIRE(hipSuccess == ret); - - free(A_h); - free(C_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); - HIP_CHECK(hipStreamDestroy(stream)); + hipStream_t destroyed_stream; + HIP_CHECK(hipStreamCreate(&destroyed_stream)); + HIP_CHECK(hipStreamBeginCapture(destroyed_stream, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamDestroy(destroyed_stream)); + HIP_CHECK_ERROR(hipStreamEndCapture(destroyed_stream, &graph), hipErrorContextIsDestroyed); } } -static void thread_func(hipStream_t stream, hipGraph_t graph) { - HIP_ASSERT(hipErrorStreamCaptureWrongThread == - hipStreamEndCapture(stream, &graph)); -} -static void StreamEndCaptureThreadNegative(float* A_d, float* A_h, - float* C_d, float* C_h, hipStreamCaptureMode mode) { - hipStream_t stream{nullptr}; +/** + * Test Description + * ------------------------ + * - Test to verify no error occurs when graph is destroyed before capture + * ends + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamEndCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamEndCapture_Positive_GraphDestroy") { hipGraph_t graph{nullptr}; - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - constexpr size_t N = 100000; + constexpr size_t N = 1000000; size_t Nbytes = N * sizeof(float); - HIP_CHECK(hipStreamCreate(&stream)); + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = hipStreamCaptureModeGlobal; HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipStreamBeginCapture(stream, mode)); - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); - HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, C_d, N); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), N, stream); - std::thread t(thread_func, stream, graph); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamEndCapture(stream, &graph)); +} + +static void thread_func_neg(hipStream_t stream, hipGraph_t graph) { + HIP_ASSERT(hipErrorStreamCaptureWrongThread == hipStreamEndCapture(stream, &graph)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify that when capture is initiated on a thread with mode + * other than hipStreamCaptureModeRelaxed and try to end capture from different + * thread, it is expected to return hipErrorStreamCaptureWrongThread + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamEndCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamEndCapture_Negative_Thread") { + constexpr size_t N = 1000000; + size_t Nbytes = N * sizeof(float); + + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + + hipGraph_t graph{nullptr}; + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = hipStreamCaptureModeGlobal; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), N, stream); + + std::thread t(thread_func_neg, stream, graph); t.join(); #if HT_AMD HIP_CHECK(hipStreamEndCapture(stream, &graph)); #endif - HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK(hipGraphDestroy(graph)); } -TEST_CASE("Unit_hipStreamEndCapture_Thread_Negative") { - constexpr size_t N = 100000; - size_t Nbytes = N * sizeof(float); - float *A_d, *C_d; - float *A_h, *C_h; - A_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(C_h != nullptr); - - // Fill with Phi + i - for (size_t i = 0; i < N; i++) { - A_h[i] = 1.618f + i; - } - - HIP_CHECK(hipMalloc(&A_d, Nbytes)); - HIP_CHECK(hipMalloc(&C_d, Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(C_d != nullptr); - - SECTION("Capture Mode:hipStreamCaptureModeGlobal") { - StreamEndCaptureThreadNegative(A_d, A_h, C_d, C_h, - hipStreamCaptureModeGlobal); - } - SECTION("Capture Mode:hipStreamCaptureModeThreadLocal") { - StreamEndCaptureThreadNegative(A_d, A_h, C_d, C_h, - hipStreamCaptureModeThreadLocal); - } - free(A_h); - free(C_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(C_d)); -} -// Thread function -static void thread_func1(hipStream_t stream, hipGraph_t *graph, - size_t Nbytes, float* A_d, float* B_h) { - HIP_CHECK(hipMemcpyAsync(B_h, A_d, Nbytes, hipMemcpyDeviceToHost, stream)); +static void thread_func_pos(hipStream_t stream, hipGraph_t* graph) { HIP_CHECK(hipStreamEndCapture(stream, graph)); } -/* - * Start stream capture on stream1 using mode hipStreamCaptureModeRelaxed. - * In stream1 queue a memcpy operation, queue a kernel square of a number operation. - * Launch a thread. In the thread, queue a memcpy operation. End the capture on - * stream1 and return the captured graph. Wait for the thread in main function. - * Create an executable graph and launch the graph on input data and validate the output. - * */ -TEST_CASE("Unit_hipStreamEndCapture_mode_hipStreamCaptureModeRelaxed") { - hipStream_t stream{nullptr}, streamForGraph{nullptr}; - hipGraph_t graph{nullptr}; - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - constexpr size_t N = 10; + +/** + * Test Description + * ------------------------ + * - Test to verify that when capture is initiated on a thread with + * hipStreamCaptureModeRelaxed mode, end capture in a different thread is + * successful + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamEndCapture.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipStreamEndCapture_Positive_Thread") { + constexpr size_t N = 1000000; size_t Nbytes = N * sizeof(float); - // Device Pointers - float *A_d; - // Host Pointers - float *A_h, *B_h, *C_h; - // Memory allocation to Host pointers - A_h = reinterpret_cast(malloc(Nbytes)); - B_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(B_h != nullptr); - REQUIRE(C_h != nullptr); + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); - // Initialize the Host data - for (size_t i = 0; i < N; i++) { - A_h[i] = 1.0f + i; - C_h[i] = A_h[i]; - } - // Memory allocation to Device pointers - HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), Nbytes)); - REQUIRE(A_d != nullptr); - - HIP_CHECK(hipStreamCreate(&stream)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeRelaxed)); - // Copy data from Host to Device - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); - - hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), - dim3(threadsPerBlock), 0, stream, A_d, A_d, N); - // Thread Launch - std::thread t(thread_func1, stream, &graph, Nbytes, A_d, B_h); - t.join(); - - // Launch the graph - hipGraphExec_t graphExec; - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); - - // Output verification - for (size_t i = 0; i < N; i++) { - C_h[i] = C_h[i] * C_h[i]; - REQUIRE(B_h[i] == C_h[i]); - } - - free(A_h); - free(B_h); - free(C_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipStreamDestroy(stream)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); -} - -static __global__ void increment(int* A_d) { - atomicAdd(A_d, 1); -} -/* - * Create 2 streams s1 and s2. Begin stream capture in s1, spawn a - * captured fork stream on s2. Queue some operations - * (like increment kernel) on both s1 and s2. End the stream capture - * on s2 and verify the error returned by the End capture. -*/ -TEST_CASE("Unit_hipStreamEndCapture_chkError_on_wrongStream") { - int *A_d{nullptr}, *A_h{nullptr}; - hipStream_t stream1{nullptr}, stream2{nullptr}; - hipEvent_t forkStreamEvent{nullptr}; hipGraph_t graph{nullptr}; - hipError_t err; - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - size_t Nbytes = sizeof(int); + hipGraphExec_t graphExec{nullptr}; + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipEventCreate(&forkStreamEvent)); + const hipStreamCaptureMode captureMode = hipStreamCaptureModeRelaxed; - A_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - // Initialize the Host data - *A_h = 0; - HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), Nbytes)); - REQUIRE(A_d != nullptr); + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceSimple(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), N, stream); - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); - - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, - hipMemcpyHostToDevice, stream1)); - - hipLaunchKernelGGL(increment, dim3(blocks), - dim3(threadsPerBlock), 0, stream1, A_d); - hipLaunchKernelGGL(increment, dim3(blocks), - dim3(threadsPerBlock), 0, stream2, A_d); - - err = hipStreamEndCapture(stream2, &graph); - REQUIRE(err == hipErrorStreamCaptureUnmatched); - - HIP_CHECK(hipStreamDestroy(stream1)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipEventDestroy(forkStreamEvent)); - free(A_h); - HIP_CHECK(hipFree(A_d)); -} -static void thread_func4(hipStream_t stream1, hipStream_t stream2, - hipEvent_t event, size_t Nbytes, int* B_d, int* B_h) { - HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream2)); - HIP_CHECK(hipEventRecord(event, stream2)); - HIP_CHECK(hipStreamWaitEvent(stream1, event, 0)); -} -/* - * Create 2 streams s1 and s2. Begin stream capture in s1 and spawn a captured - * fork stream s2. In main thread, queue a memcpy operation on s1. - * Launch a thread, queue a memcpy operation on s2. Perform hipEventRecord on - * s2 and wait Event on S1. Wait for the thread to complete. Queue operations - * kernel addition(Cd = Ad + Bd) operation and memcpy(Ch <- Cd) in s1. End the - * stream capture in s1. Create an executable graph and launch the graph on input - * data and validate the output. - * */ -TEST_CASE("Unit_hipStreamEndCapture_streamMerge_in_thread") { - // Device Pointers - int *A_d, *B_d, *C_d; - // Host Pointers - int *A_h, *B_h, *C_h, *D_h; - hipStream_t stream1{nullptr}, stream2{nullptr}, streamForGraph{nullptr}; - hipEvent_t forkStreamEvent{nullptr}, event{nullptr}; - hipGraph_t graph{nullptr}; - - constexpr unsigned blocks = 512; - constexpr unsigned threadsPerBlock = 256; - constexpr size_t N = 5; - size_t Nbytes = N * sizeof(int); - - HIP_CHECK(hipStreamCreate(&stream1)); - HIP_CHECK(hipStreamCreate(&stream2)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - HIP_CHECK(hipEventCreate(&forkStreamEvent)); - HIP_CHECK(hipEventCreate(&event)); - // Memory allocation to Host Pointers - A_h = reinterpret_cast(malloc(Nbytes)); - B_h = reinterpret_cast(malloc(Nbytes)); - C_h = reinterpret_cast(malloc(Nbytes)); - D_h = reinterpret_cast(malloc(Nbytes)); - REQUIRE(A_h != nullptr); - REQUIRE(B_h != nullptr); - REQUIRE(C_h != nullptr); - REQUIRE(D_h != nullptr); - // Initialize the Host data - for (size_t i = 0; i < N; i++) { - A_h[i] = 1 + i; - B_h[i] = 2 + i; - C_h[i] = 0; - D_h[i] = 0; - } - // Memory allocation to Device Pointers - HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), Nbytes)); - HIP_CHECK(hipMalloc(reinterpret_cast(&B_d), Nbytes)); - HIP_CHECK(hipMalloc(reinterpret_cast(&C_d), Nbytes)); - REQUIRE(A_d != nullptr); - REQUIRE(B_d != nullptr); - REQUIRE(C_d != nullptr); - - // Begin Capture - HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); - - HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); - HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); - - HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, - hipMemcpyHostToDevice, stream1)); - // Thread Launch - std::thread t(thread_func4, stream1, stream2, event, Nbytes, B_d, B_h); + std::thread t(thread_func_pos, stream, &graph); t.join(); - // Launch kernal - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), - dim3(threadsPerBlock), 0, stream1, A_d, - B_d, C_d, N); + // Validate end capture is successful + REQUIRE(graph != nullptr); - HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, - hipMemcpyDeviceToHost, stream1)); - HIP_CHECK(hipStreamEndCapture(stream1, &graph)); - - // Launch graph - hipGraphExec_t graphExec; HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); - // Verify Output - for (size_t i = 0; i < N; i++) { - D_h[i] = A_h[i] + B_h[i]; - REQUIRE(C_h[i] == D_h[i]); + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + 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), N); } HIP_CHECK(hipGraphExecDestroy(graphExec)); HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(stream1)); - HIP_CHECK(hipStreamDestroy(stream2)); - HIP_CHECK(hipEventDestroy(forkStreamEvent)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); - - // Release the memory - free(A_h); - free(B_h); - free(C_h); - free(D_h); - HIP_CHECK(hipFree(A_d)); - HIP_CHECK(hipFree(B_d)); - HIP_CHECK(hipFree(C_d)); } diff --git a/catch/unit/graph/hipStreamEndCapture_old.cc b/catch/unit/graph/hipStreamEndCapture_old.cc new file mode 100644 index 0000000000..740764d4b2 --- /dev/null +++ b/catch/unit/graph/hipStreamEndCapture_old.cc @@ -0,0 +1,437 @@ +/* +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. +*/ + +/** +Negative Testcase Scenarios : +1) Pass stream as nullptr and verify there is no crash, api returns error code. +2) Pass graph as nullptr and verify there is no crash, api returns error code. +3) Pass graph as nullptr and and stream as hipStreamPerThread verify there + is no crash, api returns error code. +4) End capture on stream where capture has not yet started and verify + error code is returned. +5) Destroy stream and try to end capture. +6) Destroy Graph and try to end capture. +7) Begin capture on a thread with mode other than hipStreamCaptureModeRelaxed + and try to end capture from different thread. Expect to return + hipErrorStreamCaptureWrongThread. +8) Start stream capture on stream1 using mode hipStreamCaptureModeRelaxed. + In stream1 queue a memcpy operation, queue a kernel square of a number operation. + Launch a thread. In the thread, queue a memcpy operation. End the capture on + stream1 and return the captured graph. Wait for the thread in main function. + Create an executable graph and launch the graph on input data and validate the + output. +9) Create 2 streams s1 and s2. Begin stream capture in s1, spawn a + captured fork stream on s2. Queue some operations + (like increment kernel) on both s1 and s2. End the stream capture + on s2 and verify the error returned by the End capture. +10)Create 2 streams s1 and s2. Begin stream capture in s1 and spawn a captured + fork stream s2. In main thread, queue a memcpy operation on s1. + Launch a thread, queue a memcpy operation on s2. Perform hipEventRecord on + s2 and wait Event on S1. Wait for the thread to complete. Queue operations + kernel addition(Cd = Ad + Bd) operation and memcpy(Ch <- Cd) in s1. End the + stream capture in s1. Create an executable graph and launch the graph on input + data and validate the output. +*/ + +#include +#include + +TEST_CASE("Unit_hipStreamEndCapture_Negative") { + hipError_t ret; + SECTION("Pass stream as nullptr") { + hipGraph_t graph; + ret = hipStreamEndCapture(nullptr, &graph); + REQUIRE(hipErrorIllegalState == ret); + } +#if HT_NVIDIA + SECTION("Pass graph as nullptr") { + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + ret = hipStreamEndCapture(stream, nullptr); + REQUIRE(hipErrorInvalidValue == ret); + HIP_CHECK(hipStreamDestroy(stream)); + } + SECTION("Pass graph as nullptr and stream as hipStreamPerThread") { + ret = hipStreamEndCapture(hipStreamPerThread, nullptr); + REQUIRE(hipErrorInvalidValue == ret); + } +#endif + SECTION("End capture on stream where capture has not yet started") { + hipStream_t stream; + hipGraph_t graph; + HIP_CHECK(hipStreamCreate(&stream)); + ret = hipStreamEndCapture(stream, &graph); + REQUIRE(hipErrorIllegalState == ret); + HIP_CHECK(hipStreamDestroy(stream)); + } + SECTION("Destroy stream and try to end capture") { + hipStream_t stream; + hipGraph_t graph; + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipStreamDestroy(stream)); + ret = hipStreamEndCapture(stream, &graph); + REQUIRE(hipErrorContextIsDestroyed == ret); + } + SECTION("Destroy graph and try to end capture in between") { + hipStream_t stream{nullptr}; + hipGraph_t graph{nullptr}; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + constexpr size_t N = 100000; + size_t Nbytes = N * sizeof(float); + float *A_d, *C_d; + float *A_h, *C_h; + + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + + // Fill with Phi + i + for (size_t i = 0; i < N; i++) { + A_h[i] = 1.618f + i; + } + + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + + HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + + HIP_CHECK(hipGraphDestroy(graph)); + ret = hipStreamEndCapture(stream, &graph); + REQUIRE(hipSuccess == ret); + + free(A_h); + free(C_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); + HIP_CHECK(hipStreamDestroy(stream)); + } +} + +static void thread_func(hipStream_t stream, hipGraph_t graph) { + HIP_ASSERT(hipErrorStreamCaptureWrongThread == + hipStreamEndCapture(stream, &graph)); +} +static void StreamEndCaptureThreadNegative(float* A_d, float* A_h, + float* C_d, float* C_h, hipStreamCaptureMode mode) { + hipStream_t stream{nullptr}; + hipGraph_t graph{nullptr}; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + constexpr size_t N = 100000; + size_t Nbytes = N * sizeof(float); + + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + + HIP_CHECK(hipMemsetAsync(C_d, 0, Nbytes, stream)); + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, C_d, N); + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, hipMemcpyDeviceToHost, stream)); + + std::thread t(thread_func, stream, graph); + t.join(); + +#if HT_AMD + HIP_CHECK(hipStreamEndCapture(stream, &graph)); +#endif + HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK(hipGraphDestroy(graph)); +} +TEST_CASE("Unit_hipStreamEndCapture_Thread_Negative") { + constexpr size_t N = 100000; + size_t Nbytes = N * sizeof(float); + float *A_d, *C_d; + float *A_h, *C_h; + + A_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(C_h != nullptr); + + // Fill with Phi + i + for (size_t i = 0; i < N; i++) { + A_h[i] = 1.618f + i; + } + + HIP_CHECK(hipMalloc(&A_d, Nbytes)); + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(C_d != nullptr); + + SECTION("Capture Mode:hipStreamCaptureModeGlobal") { + StreamEndCaptureThreadNegative(A_d, A_h, C_d, C_h, + hipStreamCaptureModeGlobal); + } + SECTION("Capture Mode:hipStreamCaptureModeThreadLocal") { + StreamEndCaptureThreadNegative(A_d, A_h, C_d, C_h, + hipStreamCaptureModeThreadLocal); + } + free(A_h); + free(C_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(C_d)); +} +// Thread function +static void thread_func1(hipStream_t stream, hipGraph_t *graph, + size_t Nbytes, float* A_d, float* B_h) { + HIP_CHECK(hipMemcpyAsync(B_h, A_d, Nbytes, hipMemcpyDeviceToHost, stream)); + HIP_CHECK(hipStreamEndCapture(stream, graph)); +} +/* + * Start stream capture on stream1 using mode hipStreamCaptureModeRelaxed. + * In stream1 queue a memcpy operation, queue a kernel square of a number operation. + * Launch a thread. In the thread, queue a memcpy operation. End the capture on + * stream1 and return the captured graph. Wait for the thread in main function. + * Create an executable graph and launch the graph on input data and validate the output. + * */ +TEST_CASE("Unit_hipStreamEndCapture_mode_hipStreamCaptureModeRelaxed") { + hipStream_t stream{nullptr}, streamForGraph{nullptr}; + hipGraph_t graph{nullptr}; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + constexpr size_t N = 10; + size_t Nbytes = N * sizeof(float); + // Device Pointers + float *A_d; + // Host Pointers + float *A_h, *B_h, *C_h; + + // Memory allocation to Host pointers + A_h = reinterpret_cast(malloc(Nbytes)); + B_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(B_h != nullptr); + REQUIRE(C_h != nullptr); + + // Initialize the Host data + for (size_t i = 0; i < N; i++) { + A_h[i] = 1.0f + i; + C_h[i] = A_h[i]; + } + // Memory allocation to Device pointers + HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), Nbytes)); + REQUIRE(A_d != nullptr); + + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamCreate(&streamForGraph)); + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeRelaxed)); + // Copy data from Host to Device + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, hipMemcpyHostToDevice, stream)); + + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), + dim3(threadsPerBlock), 0, stream, A_d, A_d, N); + // Thread Launch + std::thread t(thread_func1, stream, &graph, Nbytes, A_d, B_h); + t.join(); + + // Launch the graph + hipGraphExec_t graphExec; + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + + // Output verification + for (size_t i = 0; i < N; i++) { + C_h[i] = C_h[i] * C_h[i]; + REQUIRE(B_h[i] == C_h[i]); + } + + free(A_h); + free(B_h); + free(C_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipGraphExecDestroy(graphExec)); +} + +static __global__ void increment(int* A_d) { + atomicAdd(A_d, 1); +} +/* + * Create 2 streams s1 and s2. Begin stream capture in s1, spawn a + * captured fork stream on s2. Queue some operations + * (like increment kernel) on both s1 and s2. End the stream capture + * on s2 and verify the error returned by the End capture. +*/ +TEST_CASE("Unit_hipStreamEndCapture_chkError_on_wrongStream") { + int *A_d{nullptr}, *A_h{nullptr}; + hipStream_t stream1{nullptr}, stream2{nullptr}; + hipEvent_t forkStreamEvent{nullptr}; + hipGraph_t graph{nullptr}; + hipError_t err; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + size_t Nbytes = sizeof(int); + + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipEventCreate(&forkStreamEvent)); + + A_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + // Initialize the Host data + *A_h = 0; + HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), Nbytes)); + REQUIRE(A_d != nullptr); + + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); + + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, + hipMemcpyHostToDevice, stream1)); + + hipLaunchKernelGGL(increment, dim3(blocks), + dim3(threadsPerBlock), 0, stream1, A_d); + hipLaunchKernelGGL(increment, dim3(blocks), + dim3(threadsPerBlock), 0, stream2, A_d); + + err = hipStreamEndCapture(stream2, &graph); + REQUIRE(err == hipErrorStreamCaptureUnmatched); + + HIP_CHECK(hipStreamDestroy(stream1)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipEventDestroy(forkStreamEvent)); + free(A_h); + HIP_CHECK(hipFree(A_d)); +} +static void thread_func4(hipStream_t stream1, hipStream_t stream2, + hipEvent_t event, size_t Nbytes, int* B_d, int* B_h) { + HIP_CHECK(hipMemcpyAsync(B_d, B_h, Nbytes, hipMemcpyHostToDevice, stream2)); + HIP_CHECK(hipEventRecord(event, stream2)); + HIP_CHECK(hipStreamWaitEvent(stream1, event, 0)); +} +/* + * Create 2 streams s1 and s2. Begin stream capture in s1 and spawn a captured + * fork stream s2. In main thread, queue a memcpy operation on s1. + * Launch a thread, queue a memcpy operation on s2. Perform hipEventRecord on + * s2 and wait Event on S1. Wait for the thread to complete. Queue operations + * kernel addition(Cd = Ad + Bd) operation and memcpy(Ch <- Cd) in s1. End the + * stream capture in s1. Create an executable graph and launch the graph on input + * data and validate the output. + * */ +TEST_CASE("Unit_hipStreamEndCapture_streamMerge_in_thread") { + // Device Pointers + int *A_d, *B_d, *C_d; + // Host Pointers + int *A_h, *B_h, *C_h, *D_h; + hipStream_t stream1{nullptr}, stream2{nullptr}, streamForGraph{nullptr}; + hipEvent_t forkStreamEvent{nullptr}, event{nullptr}; + hipGraph_t graph{nullptr}; + + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + constexpr size_t N = 5; + size_t Nbytes = N * sizeof(int); + + HIP_CHECK(hipStreamCreate(&stream1)); + HIP_CHECK(hipStreamCreate(&stream2)); + HIP_CHECK(hipStreamCreate(&streamForGraph)); + HIP_CHECK(hipEventCreate(&forkStreamEvent)); + HIP_CHECK(hipEventCreate(&event)); + // Memory allocation to Host Pointers + A_h = reinterpret_cast(malloc(Nbytes)); + B_h = reinterpret_cast(malloc(Nbytes)); + C_h = reinterpret_cast(malloc(Nbytes)); + D_h = reinterpret_cast(malloc(Nbytes)); + REQUIRE(A_h != nullptr); + REQUIRE(B_h != nullptr); + REQUIRE(C_h != nullptr); + REQUIRE(D_h != nullptr); + // Initialize the Host data + for (size_t i = 0; i < N; i++) { + A_h[i] = 1 + i; + B_h[i] = 2 + i; + C_h[i] = 0; + D_h[i] = 0; + } + // Memory allocation to Device Pointers + HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), Nbytes)); + HIP_CHECK(hipMalloc(reinterpret_cast(&B_d), Nbytes)); + HIP_CHECK(hipMalloc(reinterpret_cast(&C_d), Nbytes)); + REQUIRE(A_d != nullptr); + REQUIRE(B_d != nullptr); + REQUIRE(C_d != nullptr); + + // Begin Capture + HIP_CHECK(hipStreamBeginCapture(stream1, hipStreamCaptureModeGlobal)); + + HIP_CHECK(hipEventRecord(forkStreamEvent, stream1)); + HIP_CHECK(hipStreamWaitEvent(stream2, forkStreamEvent, 0)); + + HIP_CHECK(hipMemcpyAsync(A_d, A_h, Nbytes, + hipMemcpyHostToDevice, stream1)); + // Thread Launch + std::thread t(thread_func4, stream1, stream2, event, Nbytes, B_d, B_h); + t.join(); + // Launch kernal + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), + dim3(threadsPerBlock), 0, stream1, A_d, + B_d, C_d, N); + + HIP_CHECK(hipMemcpyAsync(C_h, C_d, Nbytes, + hipMemcpyDeviceToHost, stream1)); + HIP_CHECK(hipStreamEndCapture(stream1, &graph)); + + // Launch graph + hipGraphExec_t graphExec; + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); + HIP_CHECK(hipStreamSynchronize(streamForGraph)); + + // Verify Output + for (size_t i = 0; i < N; i++) { + D_h[i] = A_h[i] + B_h[i]; + REQUIRE(C_h[i] == D_h[i]); + } + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK(hipStreamDestroy(stream1)); + HIP_CHECK(hipStreamDestroy(stream2)); + HIP_CHECK(hipEventDestroy(forkStreamEvent)); + HIP_CHECK(hipStreamDestroy(streamForGraph)); + + // Release the memory + free(A_h); + free(B_h); + free(C_h); + free(D_h); + HIP_CHECK(hipFree(A_d)); + HIP_CHECK(hipFree(B_d)); + HIP_CHECK(hipFree(C_d)); +} diff --git a/catch/unit/graph/hipStreamUpdateCaptureDependencies.cc b/catch/unit/graph/hipStreamUpdateCaptureDependencies.cc new file mode 100644 index 0000000000..871ede9ea4 --- /dev/null +++ b/catch/unit/graph/hipStreamUpdateCaptureDependencies.cc @@ -0,0 +1,472 @@ +/* +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 + +#include "stream_capture_common.hh" + +/** + * @addtogroup hipStreamUpdateCaptureDependencies + * hipStreamUpdateCaptureDependencies + * @{ + * @ingroup GraphTest + * `hipStreamUpdateCaptureDependencies(hipStream_t stream, hipGraphNode_t + * *dependencies, size_t numDependencies, unsigned int flags __dparm(0)))` - + * update the set of dependencies in a capturing stream + */ + +static __global__ void vectorSet(const float* A_d, float* B_d, int64_t NELEM) { + size_t offset = (blockIdx.x * blockDim.x + threadIdx.x); + size_t stride = blockDim.x * gridDim.x; + + for (size_t i = offset; i < NELEM; i += stride) { + B_d[i] = A_d[i]; + } +} + +static __global__ void vectorSum(const float* A_d, const float* B_d, float* C_d, size_t NELEM) { + size_t offset = (blockIdx.x * blockDim.x + threadIdx.x); + size_t stride = blockDim.x * gridDim.x; + + for (size_t i = offset; i < NELEM; i += stride) { + C_d[i] = A_d[i] + B_d[i] + C_d[i]; + } +} + +/* Local Function for setting new dependency + */ +static void UpdateStreamCaptureDependenciesSet(hipStream_t stream, + hipStreamCaptureMode captureMode) { + constexpr size_t N = 1000000; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + size_t Nbytes = N * sizeof(float); + + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + hipGraph_t capInfoGraph{nullptr}; + const hipGraphNode_t* nodelist{}; + size_t numDependencies; + + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard C_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard B_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard C_d(LinearAllocs::hipMalloc, Nbytes); + + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + EventsGuard events_guard(3); + StreamsGuard streams_guard(2); + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceBranched(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream, + streams_guard.stream_list(), events_guard.event_list()); + + constexpr int numDepsCreated = 2; // Num of dependencies created + + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, nullptr, &capInfoGraph, &nodelist, + &numDependencies)); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + REQUIRE(capInfoGraph != nullptr); + REQUIRE(numDependencies == numDepsCreated); + + SECTION("Set dependency to independent Memcpy node") { + // Create memcpy node and set it as a capture dependency in graph + hipMemcpy3DParms myparams{}; + hipGraphNode_t memcpyNodeC{}; + + memset(&myparams, 0x0, sizeof(hipMemcpy3DParms)); + myparams.srcPos = make_hipPos(0, 0, 0); + myparams.dstPos = make_hipPos(0, 0, 0); + myparams.extent = make_hipExtent(Nbytes, 1, 1); + myparams.srcPtr = make_hipPitchedPtr(C_h.host_ptr(), Nbytes, N, 1); + myparams.dstPtr = make_hipPitchedPtr(C_d.ptr(), Nbytes, N, 1); + myparams.kind = hipMemcpyHostToDevice; + + HIP_CHECK(hipGraphAddMemcpyNode(&memcpyNodeC, capInfoGraph, nullptr, 0, &myparams)); + + // Replace capture dependency with new memcpy node created. + // Further nodes captured in stream will depend on the new memcpy node. + HIP_CHECK(hipStreamUpdateCaptureDependencies(stream, &memcpyNodeC, 1, + hipStreamSetCaptureDependencies)); + + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, nullptr, &capInfoGraph, &nodelist, + &numDependencies)); + + // Verify updating dependency is taking effect. + REQUIRE(numDependencies == 1); + REQUIRE(nodelist[0] == memcpyNodeC); + + hipLaunchKernelGGL(HipTest::vector_square, dim3(blocks), dim3(threadsPerBlock), 0, stream, + C_d.ptr(), C_d.ptr(), N); + } + + SECTION("Set dependency to Kernel node depending on graph branch") { + hipGraphNode_t kernelNode{}; + hipKernelNodeParams kernelNodeParams{}; + + // Add node to modify vector sqr result and plug-in the nod + float* C_ptr = C_d.ptr(); + float* A_ptr = A_d.ptr(); + size_t NElem{N}; + void* kernelArgs[] = {&A_ptr, &C_ptr, reinterpret_cast(&NElem)}; + kernelNodeParams.func = reinterpret_cast(HipTest::vector_square); + kernelNodeParams.gridDim = dim3(blocks); + kernelNodeParams.blockDim = dim3(threadsPerBlock); + kernelNodeParams.sharedMemBytes = 0; + kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs); + kernelNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&kernelNode, capInfoGraph, &nodelist[0], 1, &kernelNodeParams)); + + // Replace capture dependency with new kernel node created. + // Further nodes captured in stream will depend on the new kernel node. + HIP_CHECK(hipStreamUpdateCaptureDependencies(stream, &kernelNode, 1, + hipStreamSetCaptureDependencies)); + + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, nullptr, &capInfoGraph, &nodelist, + &numDependencies)); + + // Verify updating dependency is taking effect. + REQUIRE(numDependencies == 1); + REQUIRE(nodelist[0] == kernelNode); + } + + HIP_CHECK(hipMemcpyAsync(B_h.ptr(), C_d.ptr(), Nbytes, hipMemcpyDeviceToHost, stream)); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + std::fill_n(A_h.host_ptr(), N, static_cast(i)); + std::fill_n(C_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); + } + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/* Local Function for adding new dependency + */ +static void UpdateStreamCaptureDependenciesAdd(hipStream_t stream, + hipStreamCaptureMode captureMode) { + constexpr size_t N = 1000000; + constexpr unsigned blocks = 512; + constexpr unsigned threadsPerBlock = 256; + size_t Nbytes = N * sizeof(float); + + hipStreamCaptureStatus captureStatus{hipStreamCaptureStatusNone}; + hipGraph_t capInfoGraph{nullptr}; + const hipGraphNode_t* nodelist{}; + size_t numDependencies; + + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard C_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard B_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard C_d(LinearAllocs::hipMalloc, Nbytes); + + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + EventsGuard events_guard(3); + StreamsGuard streams_guard(2); + + HIP_CHECK(hipStreamBeginCapture(stream, captureMode)); + captureSequenceBranched(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream, + streams_guard.stream_list(), events_guard.event_list()); + + constexpr int numDepsCreated = 2; // Num of dependencies created + + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, nullptr, &capInfoGraph, &nodelist, + &numDependencies)); + REQUIRE(captureStatus == hipStreamCaptureStatusActive); + REQUIRE(capInfoGraph != nullptr); + REQUIRE(numDependencies == numDepsCreated); + + SECTION("Add Dependency to independant Memcpy node") { + // Create memcpy node and add it as additional dependency in graph + hipMemcpy3DParms myparams{}; + hipGraphNode_t memcpyNodeC{}; + + memset(&myparams, 0x0, sizeof(hipMemcpy3DParms)); + myparams.srcPos = make_hipPos(0, 0, 0); + myparams.dstPos = make_hipPos(0, 0, 0); + myparams.extent = make_hipExtent(Nbytes, 1, 1); + myparams.srcPtr = make_hipPitchedPtr(C_h.host_ptr(), Nbytes, N, 1); + myparams.dstPtr = make_hipPitchedPtr(C_d.ptr(), Nbytes, N, 1); + myparams.kind = hipMemcpyHostToDevice; + + HIP_CHECK(hipGraphAddMemcpyNode(&memcpyNodeC, capInfoGraph, nullptr, 0, &myparams)); + + // Add/Append additional dependency MemcpyNodeC to the existing set. + // Further nodes captured in stream will depend on Memcpy nodes A, B and C. + HIP_CHECK(hipStreamUpdateCaptureDependencies(stream, &memcpyNodeC, 1, + hipStreamAddCaptureDependencies)); + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, nullptr, &capInfoGraph, &nodelist, + &numDependencies)); + + REQUIRE(numDependencies == numDepsCreated + 1); + + hipLaunchKernelGGL(vectorSum, dim3(blocks), dim3(threadsPerBlock), 0, stream, A_d.ptr(), + C_d.ptr(), B_d.ptr(), N); + } + + SECTION("Add Dependency to Kernel node depending on graph branch") { + hipGraphNode_t kernelNode{}; + hipKernelNodeParams kernelNodeParams{}; + + // Add node to modify vector sqr result and plug-in the nod + float* C_ptr = C_d.ptr(); + float* A_ptr = A_d.ptr(); + size_t NElem{N}; + void* kernelArgs[] = {&A_ptr, &C_ptr, reinterpret_cast(&NElem)}; + kernelNodeParams.func = reinterpret_cast(vectorSet); + kernelNodeParams.gridDim = dim3(blocks); + kernelNodeParams.blockDim = dim3(threadsPerBlock); + kernelNodeParams.sharedMemBytes = 0; + kernelNodeParams.kernelParams = reinterpret_cast(kernelArgs); + kernelNodeParams.extra = nullptr; + HIP_CHECK(hipGraphAddKernelNode(&kernelNode, capInfoGraph, &nodelist[0], 1, &kernelNodeParams)); + + // Add/Append additional dependency addNode to the existing set. + HIP_CHECK(hipStreamUpdateCaptureDependencies(stream, &kernelNode, 1, + hipStreamAddCaptureDependencies)); + + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, nullptr, &capInfoGraph, &nodelist, + &numDependencies)); + + REQUIRE(numDependencies == numDepsCreated + 1); + + hipLaunchKernelGGL(vectorSum, dim3(blocks), dim3(threadsPerBlock), 0, stream, A_d.ptr(), + C_d.ptr(), B_d.ptr(), N); + } + + HIP_CHECK(hipMemcpyAsync(B_h.ptr(), B_d.ptr(), Nbytes, hipMemcpyDeviceToHost, stream)); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + // Replay the recorded sequence multiple times + for (int i = 0; i < kLaunchIters; i++) { + std::fill_n(A_h.host_ptr(), N, static_cast(i)); + std::fill_n(C_h.host_ptr(), N, static_cast(i)); + HIP_CHECK(hipGraphLaunch(graphExec, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + ArrayFindIfNot(B_h.host_ptr(), static_cast(i) * 2, N); + } + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify replacing existing dependency set with new nodes by + * calling the api with flag hipStreamSetCaptureDependencies for + * created/hipStreamPerThread for all capture modes. Verify updated dependency + * list is taking effect: + * -# Replace existing dependencies with a new memcpy node that has no + * dependencies + * -# Replace existing dependencies with a new kernel node which depends + * on a previously captured sequence + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamUpdateCaptureDependencies.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipStreamSetCaptureDependencies_Positive_Functional") { + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + + UpdateStreamCaptureDependenciesSet(stream, captureMode); +} + +/** + * Test Description + * ------------------------ + * - Test to verify adding additional depencies in the flow by calling the + * api with flag hipStreamAddCaptureDependencies for created/hipStreamPerThread + * for all capture modes. Verify updated dependency list is taking effect: + * -# Add new memcpy node that has no parent to the existing dependecies + * -# Add new kernel node which depends on a previously captured sequence + * to the existing dependencies + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamUpdateCaptureDependencies.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipStreamAddCaptureDependencies_Positive_Functional") { + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + + UpdateStreamCaptureDependenciesAdd(stream, captureMode); +} + +/** + * Test Description + * ------------------------ + * - Test to verify when dependencies are passed as nullptr and numDeps as 0, + * api returns success + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamUpdateCaptureDependencies.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipStreamUpdateCaptureDependencies_Positive_Parameters") { + hipGraph_t graph{nullptr}; + + const auto stream_type = GENERATE(Streams::perThread, Streams::created); + StreamGuard stream_guard(stream_type); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureMode = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + const hipStreamUpdateCaptureDependenciesFlags flag = + GENERATE(hipStreamAddCaptureDependencies, hipStreamSetCaptureDependencies); + + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + + HIP_CHECK(hipStreamUpdateCaptureDependencies(stream, nullptr, 0, flag)); + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# Pass Dependencies as nullptr and numDeps as nonzero + * -# numDeps exceeds actual number of nodes + * -# Invalid flag is passed + * -# Dependency node is a un-initialized/invalid parameter + * -# Stream is not capturing + * Test source + * ------------------------ + * - catch\unit\graph\hipStreamUpdateCaptureDependencies.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipStreamUpdateCaptureDependencies_Negative_Parameters") { + const int Nbytes = 100; + hipGraph_t capInfoGraph{nullptr}; + hipGraph_t graph{nullptr}; + + hipStreamCaptureStatus captureStatus; + size_t numDependencies; + const hipGraphNode_t* nodelist{}; + hipGraphNode_t memsetNode{}; + std::vector dependencies; + + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + HIP_CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeGlobal)); + HIP_CHECK(hipMemsetAsync(A_d.ptr(), 0, Nbytes, stream)); + + HIP_CHECK(hipStreamGetCaptureInfo_v2(stream, &captureStatus, nullptr, &capInfoGraph, &nodelist, + &numDependencies)); + + hipMemsetParams memsetParams{}; + memsetParams.dst = reinterpret_cast(A_d.ptr()); + memsetParams.value = 1; + memsetParams.pitch = 0; + memsetParams.elementSize = sizeof(char); + memsetParams.width = Nbytes; + memsetParams.height = 1; + HIP_CHECK( + hipGraphAddMemsetNode(&memsetNode, capInfoGraph, nodelist, numDependencies, &memsetParams)); + dependencies.push_back(memsetNode); + + SECTION("Dependencies as nullptr and numDeps as nonzero") { + HIP_CHECK_ERROR(hipStreamUpdateCaptureDependencies(stream, nullptr, dependencies.size(), + hipStreamAddCaptureDependencies), + hipErrorInvalidValue); + } + + SECTION("Invalid flag") { + constexpr int invalidFlag = 20; + HIP_CHECK_ERROR(hipStreamUpdateCaptureDependencies(stream, dependencies.data(), + dependencies.size(), invalidFlag), + hipErrorInvalidValue); + } + +#if HT_NVIDIA // EXSWHTEC-227 + SECTION("numDeps exceeding actual number of nodes") { + HIP_CHECK_ERROR( + hipStreamUpdateCaptureDependencies(stream, dependencies.data(), dependencies.size() + 1, + hipStreamAddCaptureDependencies), + hipErrorInvalidValue); + } + + SECTION("depnode as un-initialized/invalid parameter") { + hipGraphNode_t uninit_node{}; + HIP_CHECK_ERROR(hipStreamUpdateCaptureDependencies(stream, &uninit_node, 1, + hipStreamAddCaptureDependencies), + hipErrorInvalidValue); + } +#endif + +#if HT_AMD // EXSWHTEC-227 + HIP_CHECK(hipStreamUpdateCaptureDependencies(stream, dependencies.data(), dependencies.size(), + hipStreamAddCaptureDependencies)); +#endif + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + + SECTION("Stream is not capturing") { + HIP_CHECK_ERROR( + hipStreamUpdateCaptureDependencies(stream, dependencies.data(), dependencies.size(), + hipStreamAddCaptureDependencies), + hipErrorIllegalState); + } + + HIP_CHECK(hipGraphDestroy(graph)); +} diff --git a/catch/unit/graph/hipThreadExchangeStreamCaptureMode.cc b/catch/unit/graph/hipThreadExchangeStreamCaptureMode.cc new file mode 100644 index 0000000000..c35fc18900 --- /dev/null +++ b/catch/unit/graph/hipThreadExchangeStreamCaptureMode.cc @@ -0,0 +1,151 @@ +/* +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 + +#include "stream_capture_common.hh" + + +/** + * @addtogroup hipThreadExchangeStreamCaptureMode + * hipThreadExchangeStreamCaptureMode + * @{ + * @ingroup GraphTest + * `hipThreadExchangeStreamCaptureMode(hipStreamCaptureMode *mode)` - + * swaps the stream capture mode of a thread + */ + +/* Local Function for swaping stream capture mode of a thread + */ +static void hipGraphLaunchWithMode(hipStream_t stream, hipStreamCaptureMode mode) { + constexpr size_t N = 1024; + size_t Nbytes = N * sizeof(float); + constexpr float fill_value = 5.0f; + + hipGraph_t graph{nullptr}; + hipGraphExec_t graphExec{nullptr}; + + LinearAllocGuard A_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard B_h(LinearAllocs::malloc, Nbytes); + LinearAllocGuard A_d(LinearAllocs::hipMalloc, Nbytes); + LinearAllocGuard B_d(LinearAllocs::hipMalloc, Nbytes); + float* C_d; + + HIP_CHECK(hipThreadExchangeStreamCaptureMode(&mode)); + + HIP_CHECK(hipStreamBeginCapture(stream, mode)); + + captureSequenceLinear(A_h.host_ptr(), A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream); + captureSequenceCompute(A_d.ptr(), B_h.host_ptr(), B_d.ptr(), N, stream); + + if (mode == hipStreamCaptureModeRelaxed) { + HIP_CHECK(hipMalloc(&C_d, Nbytes)); + } + + HIP_CHECK(hipStreamEndCapture(stream, &graph)); + + // Validate end capture is successful + REQUIRE(graph != nullptr); + + HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + + std::fill_n(A_h.host_ptr(), N, fill_value); + HIP_CHECK(hipGraphLaunch(graphExec, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + + // Validate the computation + ArrayFindIfNot(B_h.host_ptr(), fill_value * fill_value, N); + if (mode == hipStreamCaptureModeRelaxed) { + HIP_CHECK(hipFree(C_d)); + } + + HIP_CHECK(hipGraphExecDestroy(graphExec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +void threadFuncCaptureMode(hipStream_t stream, hipStreamCaptureMode mode) { + hipGraphLaunchWithMode(stream, mode); +} + +/** + * Test Description + * ------------------------ + * - Test to verify basic functionality for API that swaps the stream capture + * mode of a thread. All combinations for main and other thread capture modes + * are tested + * Test source + * ------------------------ + * - catch\unit\graph\hipThreadExchangeStreamCaptureMode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +TEST_CASE("Unit_hipThreadExchangeStreamCaptureMode_Positive_Functional") { + StreamGuard stream_guard(Streams::created); + hipStream_t stream = stream_guard.stream(); + + const hipStreamCaptureMode captureModeMain = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + const hipStreamCaptureMode captureModeThread = GENERATE( + hipStreamCaptureModeGlobal, hipStreamCaptureModeThreadLocal, hipStreamCaptureModeRelaxed); + + hipGraphLaunchWithMode(stream, captureModeMain); + std::thread t(threadFuncCaptureMode, stream, captureModeThread); + t.join(); +} + +/** + * Test Description + * ------------------------ + * - Test to verify API behavior with invalid arguments: + * -# Mode as nullptr + * -# Mode as -1 + * -# Mode as INT_MAX + * -# Mode other than existing 3 modes (hipStreamCaptureModeRelaxed + 1) + * Test source + * ------------------------ + * - catch\unit\graph\hipThreadExchangeStreamCaptureMode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.3 + */ +#if HT_AMD // getting error in Cuda Setup +TEST_CASE("Unit_hipThreadExchangeStreamCaptureMode_Negative_Parameters") { + hipStreamCaptureMode mode; + + SECTION("Pass Mode as nullptr") { + HIP_CHECK_ERROR(hipThreadExchangeStreamCaptureMode(nullptr), hipErrorInvalidValue); + } + SECTION("Pass Mode as -1") { + mode = hipStreamCaptureMode(-1); + HIP_CHECK_ERROR(hipThreadExchangeStreamCaptureMode(&mode), hipErrorInvalidValue); + } + SECTION("Pass Mode as INT_MAX") { + mode = hipStreamCaptureMode(INT_MAX); + HIP_CHECK_ERROR(hipThreadExchangeStreamCaptureMode(&mode), hipErrorInvalidValue); + } + SECTION("Pass Mode as hipStreamCaptureModeRelaxed + 1") { + mode = hipStreamCaptureMode(hipStreamCaptureModeRelaxed + 1); + HIP_CHECK_ERROR(hipThreadExchangeStreamCaptureMode(&mode), hipErrorInvalidValue); + } +} +#endif From 77a37371b014c74ea3be52cc24eff52ab02b24c8 Mon Sep 17 00:00:00 2001 From: music-dino <111048524+music-dino@users.noreply.github.com> Date: Mon, 6 Mar 2023 09:13:45 +0100 Subject: [PATCH 05/13] EXSWHTEC-193 - Implement new and update existing tests for the hipGraph*MemsetNode family of APIs (#12) - Implement positive and negative tests for hipGraphAddMemsetNode - Implement positive and negative tests for hipGraphMemsetNodeSetParams - Implement positive and negative tests for hipGraphExecMemsetNodeSetParams - Implement negative tests for hipGraphMemsetNodeGetParams - Add doxygen annotations - Add a dynamic section in positive test for memset for different dimensions --- .../config/config_amd_linux_common.json | 9 +- .../config/config_amd_windows_common.json | 15 +- .../graph/graph_memset_node_test_common.hh | 103 ++++++ catch/unit/graph/hipGraphAddMemsetNode.cc | 211 ++++++----- .../graph/hipGraphExecMemsetNodeSetParams.cc | 330 +++++++++--------- .../unit/graph/hipGraphMemsetNodeGetParams.cc | 166 +++------ .../unit/graph/hipGraphMemsetNodeSetParams.cc | 263 ++++++-------- 7 files changed, 562 insertions(+), 535 deletions(-) create mode 100644 catch/unit/graph/graph_memset_node_test_common.hh diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index f1a3283fb6..d14b0971fe 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -36,6 +36,13 @@ "Unit_hipGraphAddMemcpyNode1D_Negative_Basic", "Unit_hipStreamGetCaptureInfo_Nullstream_CaptureInfo", "intermittent issue: corrupted double-linked list", - "Unit_hipGraphRetainUserObject_Functional_2" + "Unit_hipGraphRetainUserObject_Functional_2", + "Note: Following four tests disabled due to defect - EXSWHTEC-203", + "Unit_hipGraphAddMemsetNode_Positive_Basic - uint16_t", + "Unit_hipGraphAddMemsetNode_Positive_Basic - uint32_t", + "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint16_t", + "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint32_t", + "Note: Test disabled due to defect - EXSWHTEC-207", + "Unit_hipGraphExecMemsetNodeSetParams_Negative_Updating_Non1D_Node" ] } diff --git a/catch/hipTestMain/config/config_amd_windows_common.json b/catch/hipTestMain/config/config_amd_windows_common.json index 09eadeb673..67c76ef238 100644 --- a/catch/hipTestMain/config/config_amd_windows_common.json +++ b/catch/hipTestMain/config/config_amd_windows_common.json @@ -56,7 +56,7 @@ "Unit_hipStreamWaitEvent_DifferentStreams", "Unit_hipStreamQuery_WithFinishedWork", "Unit_hipDeviceGetCacheConfig_Positive_Basic", - "Unit_hipDeviceGetCacheConfig_Positive_Basic", + "Unit_hipDeviceGetCacheConfig_Positive_Basic", "Unit_hipDeviceGetCacheConfig_Positive_Threaded", "Unit_hipStreamValue_Wait32_Blocking_Mask_Gte", "Unit_hipStreamValue_Wait32_Blocking_Mask_Eq_1", @@ -115,6 +115,17 @@ "Unit_hipStreamQuery_WithFinishedWork", "Unit_hipLaunchHostFunc_Graph", "Unit_hipLaunchHostFunc_KernelHost", - "Unit_hipStreamSetCaptureDependencies_Positive_Functional" + "Unit_hipStreamSetCaptureDependencies_Positive_Functional", + "Note: Following four tests disabled due to defect - EXSWHTEC-203", + "Unit_hipGraphAddMemsetNode_Positive_Basic - uint16_t", + "Unit_hipGraphAddMemsetNode_Positive_Basic - uint32_t", + "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint16_t", + "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint32_t", + "Note: Test disabled due to defect - EXSWHTEC-207", + "Unit_hipGraphExecMemsetNodeSetParams_Negative_Updating_Non1D_Node", + "Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic - uint8_t", + "Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic - uint16_t", + "Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic - uint32_t", + "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint8_t" ] } diff --git a/catch/unit/graph/graph_memset_node_test_common.hh b/catch/unit/graph/graph_memset_node_test_common.hh new file mode 100644 index 0000000000..f4b957283e --- /dev/null +++ b/catch/unit/graph/graph_memset_node_test_common.hh @@ -0,0 +1,103 @@ +/* +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 +#include +#include + +template void GraphMemsetNodeCommonPositive(F f) { + const size_t width = GENERATE(1, 64, kPageSize / sizeof(T) + 1); + const size_t height = GENERATE(1, 2, 1024); + DYNAMIC_SECTION("Width: " << width << " Height: " << height) { + LinearAllocGuard2D alloc(width, height); + + constexpr T set_value = 42; + hipMemsetParams params = {}; + params.dst = alloc.ptr(); + params.elementSize = sizeof(T); + params.width = width; + params.height = height; + params.pitch = alloc.pitch(); + params.value = set_value; + + HIP_CHECK(f(¶ms)); + + LinearAllocGuard buffer(LinearAllocs::hipHostMalloc, width * sizeof(T) * height); + HIP_CHECK(hipMemcpy2D(buffer.ptr(), width * sizeof(T), alloc.ptr(), alloc.pitch(), + width * sizeof(T), height, hipMemcpyDeviceToHost)); + ArrayFindIfNot(buffer.ptr(), set_value, width * height); + } +} + +template void MemsetCommonNegative(F f, hipMemsetParams params) { + SECTION("pMemsetParams == nullptr") { HIP_CHECK_ERROR(f(nullptr), hipErrorInvalidValue); } + + SECTION("pMemsetParams.dst == nullptr") { + params.dst = nullptr; + HIP_CHECK_ERROR(f(¶ms), hipErrorInvalidValue); + } + + SECTION("pMemsetParams.elementSize != 1, 2, 4") { + params.elementSize = GENERATE(0, 3, 5); + HIP_CHECK_ERROR(f(¶ms), hipErrorInvalidValue); + } + +// Disabled on AMD due to defect - EXSWHTEC-204 +#if HT_NVIDIA + SECTION("pMemsetParams.width == 0") { + params.width = 0; + HIP_CHECK_ERROR(f(¶ms), hipErrorInvalidValue); + } +#endif + + SECTION("pMemsetParams.width > allocation size") { + params.width = params.width + 1000; + HIP_CHECK_ERROR(f(¶ms), hipErrorInvalidValue); + } + + SECTION("pMemsetParams.height == 0") { + params.height = 0; + HIP_CHECK_ERROR(f(¶ms), hipErrorInvalidValue); + } + +// Disabled on AMD due to defect - EXSWHTEC-205 +#if HT_NVIDIA + SECTION("pMemsetParams.pitch < width when height > 1") { + params.width = 2; + params.height = 2; + params.pitch = params.elementSize; + HIP_CHECK_ERROR(f(¶ms), hipErrorInvalidValue); + } +#endif + +// Disabled on AMD due to defect - EXSWHTEC-206 +#if HT_NVIDIA + SECTION("pMemsetParams.pitch * height > allocation size") { + params.width = 2; + params.height = 2; + params.pitch = 3 * params.elementSize; + HIP_CHECK_ERROR(f(¶ms), hipErrorInvalidValue); + } +#endif +} \ No newline at end of file diff --git a/catch/unit/graph/hipGraphAddMemsetNode.cc b/catch/unit/graph/hipGraphAddMemsetNode.cc index c89d1a6f5c..1080741731 100644 --- a/catch/unit/graph/hipGraphAddMemsetNode.cc +++ b/catch/unit/graph/hipGraphAddMemsetNode.cc @@ -6,132 +6,121 @@ 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Negative Testcase Scenarios for api hipGraphAddMemsetNode : -1) Pass pGraphNode as nullptr and check if api returns error. -2) Pass pGraphNode as un-initialize object and check. -3) Pass Graph as nullptr and check if api returns error. -4) Pass Graph as empty object(skipping graph creation), api should return error code. -5) Pass pDependencies as nullptr, api should return success. -6) Pass numDependencies is max(size_t) and pDependencies is not valid ptr, api expected to return error code. -7) Pass pDependencies is nullptr, but numDependencies is non-zero, api expected to return error. -8) Pass pMemsetParams as nullptr and check if api returns error code. -9) Pass pMemsetParams as un-initialize object and check if api returns error code. -10) Pass hipMemsetParams::dst as nullptr should return error code. -11) Pass hipMemsetParams::element size other than 1, 2, or 4 and check api should return error code. -12) Pass hipMemsetParams::height as zero and check api should return error code. -Functional Scenarios for api hipGraphAddMemsetNode : -1. Allocate a 2D array using hipMallocPitch. Initialize the allocated memory using hipGraphAddMemsetNode. - Copy the values in device memory to host using hipGraphAddMemcpyNode. Verify the results -2. Allocate a 1D array using hipMallocPitch. Initialize the allocated memory using hipGraphAddMemsetNode. - Copy the values in device memory to host using hipGraphAddMemcpyNode. Verify the results.. -3. Allocate a 2D array using hipMalloc3D. Initialize the allocated memory using hipGraphAddMemsetNode. - Copy the values in device memory to host using hipGraphAddMemcpyNode. Verify the results. -4. Allocate a 1D array using hipMalloc3D. Initialize the allocated memory using hipGraphAddMemsetNode. - Copy the values in device memory to host using hipGraphAddMemcpyNode. Verify the results. -5. Allocate a 1D array using hipMalloc. Initialize the allocated memory using hipGraphAddMemsetNode. - Copy the values in device memory to host using hipGraphAddMemcpyNode. Verify the results. -6. Allocate memory using hipMallocManaged. Initialize the allocated memory using hipGraphAddMemsetNode. - Copy the values in device memory to host using hipGraphAddMemcpyNode. Verify the results. -*/ +#include +#include +#include #include -/** - * Negative Test for API hipGraphAddMemsetNode - */ +#include +#include + +#include "graph_memset_node_test_common.hh" +#include "graph_tests_common.hh" + #define SIZE 1024 static char memSetVal = 'a'; -TEST_CASE("Unit_hipGraphAddMemsetNode_Negative") { - hipError_t ret; - hipGraph_t graph; - hipGraphNode_t memsetNode; - char *devData; - HIP_CHECK(hipMalloc(&devData, 1024)); +/** + * @addtogroup hipGraphAddMemsetNode hipGraphAddMemsetNode + * @{ + * @ingroup GraphTest + * `hipGraphAddMemsetNode(hipGraphNode_t *pGraphNode, hipGraph_t graph, const hipGraphNode_t + * *pDependencies, size_t numDependencies, const hipMemsetParams *pMemsetParams)` - + * Creates a memset node and adds it to a graph + */ + +/** + * Test Description + * ------------------------ + * - Verify that all elements of destination memory are set to the correct value. + * The test is repeated for all valid element sizes(1, 2, 4), and several allocations of different + * height and width, both on host and device. + * Test source + * ------------------------ + * - unit/graph/hipGraphAddMemsetNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_hipGraphAddMemsetNode_Positive_Basic", "", uint8_t, uint16_t, uint32_t) { + const auto f = [](hipMemsetParams* params) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, params)); + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); + + return hipSuccess; + }; + + GraphMemsetNodeCommonPositive(f); +} + +/** + * Test Description + * ------------------------ + * - Verify API behaviour with invalid arguments: + * -# pGraphNode is nullptr + * -# graph is nullptr + * -# pDependencies is nullptr when numDependencies is not zero + * -# A node in pDependencies originates from a different graph + * -# numDependencies is invalid + * -# A node is duplicated in pDependencies + * -# pMemsetParams is nullptr + * -# pMemsetParams::dst is nullptr + * -# pMemsetParams::elementSize is different from 1, 2, and 4 + * -# pMemsetParams::width is zero + * -# pMemsetParams::width is larger than the allocated memory region + * -# pMemsetParams::height is zero + * -# pMemsetParams::pitch is less than width when height is more than 1 + * -# pMemsetParams::pitch * pMemsetParams::height is larger than the allocated memory region + * Test source + * ------------------------ + * - unit/graph/hipGraphAddMemsetNode.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphAddMemsetNode_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; HIP_CHECK(hipGraphCreate(&graph, 0)); - hipMemsetParams memsetParams{}; - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData); - memsetParams.value = 0; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = 1024; - memsetParams.height = 1; + LinearAllocGuard alloc(LinearAllocs::hipMalloc, 4 * sizeof(int)); + hipMemsetParams params = {}; + params.dst = alloc.ptr(); + params.elementSize = sizeof(*alloc.ptr()); + params.width = 1; + params.height = 1; + params.value = 42; + + GraphAddNodeCommonNegativeTests(std::bind(hipGraphAddMemsetNode, _1, _2, _3, _4, ¶ms), graph); + + hipGraphNode_t node = nullptr; + MemsetCommonNegative(std::bind(hipGraphAddMemsetNode, &node, graph, nullptr, 0, _1), params); - SECTION("Pass pGraphNode as nullptr") { - ret = hipGraphAddMemsetNode(nullptr, graph, nullptr, 0, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass pGraphNode as un-initialize object") { - hipGraphNode_t memsetNode_1; - ret = hipGraphAddMemsetNode(&memsetNode_1, graph, - nullptr, 0, &memsetParams); - REQUIRE(hipSuccess == ret); - } - SECTION("Pass graph as nullptr") { - ret = hipGraphAddMemsetNode(&memsetNode, nullptr, - nullptr, 0, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass Graph as empty object") { - hipGraph_t graph_1{}; - ret = hipGraphAddMemsetNode(&memsetNode, graph_1, - nullptr, 0, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass pDependencies as nullptr") { - ret = hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, &memsetParams); - REQUIRE(hipSuccess == ret); - } - SECTION("Pass numDependencies is max and pDependencies is not valid ptr") { - ret = hipGraphAddMemsetNode(&memsetNode, graph, - nullptr, INT_MAX, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass pDependencies as nullptr, but numDependencies is non-zero") { - ret = hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 9, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass pMemsetParams as nullptr") { - ret = hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, nullptr); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass pMemsetParams as un-initialize object") { - hipMemsetParams memsetParams1; - ret = hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, - &memsetParams1); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass hipMemsetParams::dst as nullptr") { - memsetParams.dst = nullptr; - ret = hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass hipMemsetParams::element size other than 1, 2, or 4") { - memsetParams.dst = reinterpret_cast(devData); - memsetParams.elementSize = 9; - ret = hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass hipMemsetParams::height as zero") { - memsetParams.elementSize = sizeof(char); - memsetParams.height = 0; - ret = hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - HIP_CHECK(hipFree(devData)); HIP_CHECK(hipGraphDestroy(graph)); } /* diff --git a/catch/unit/graph/hipGraphExecMemsetNodeSetParams.cc b/catch/unit/graph/hipGraphExecMemsetNodeSetParams.cc index 7e51eb296b..72ea020ca8 100644 --- a/catch/unit/graph/hipGraphExecMemsetNodeSetParams.cc +++ b/catch/unit/graph/hipGraphExecMemsetNodeSetParams.cc @@ -6,196 +6,208 @@ 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 + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 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 +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 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 +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. */ -/** -Testcase Scenarios : -Functional- -1) Instantiate a graph with memset node, obtain executable graph and update the - hipMemsetParams node params with set. Make sure they are taking effect. -Negative- -1) Pass hGraphExec as nullptr and verify api returns error code. -2) Pass graph node as nullptr and verify api returns error code. -3) Pass different hipGraphNode_t which was not used in graphExec and verify api returns error code. -4) Pass Pass different Graph which was not used in graphExec and verify api returns error code. -5) Pass pNodeParams as nullptr and verify api returns error code. -6) Pass pNodeParams as empty structure object and verify api returns error code. -7) Pass hipMemsetParams::dst as nullptr, api should return error code. -8) Pass hipMemsetParams::element size other than 1, 2, or 4 and check api should return error code. -9) Pass hipMemsetParams::height as zero and check api should return error code. -*/ +#include +#include #include -#include -/* Test verifies hipGraphExecMemsetNodeSetParams API Negative scenarios. +#include "graph_memset_node_test_common.hh" +#include "graph_tests_common.hh" + +/** + * @addtogroup hipGraphExecMemsetNodeSetParams hipGraphExecMemsetNodeSetParams + * @{ + * @ingroup GraphTest + * `hipGraphExecMemsetNodeSetParams(hipGraphExec_t hGraphExec, hipGraphNode_t node, const + * hipMemsetParams *pNodeParams)` - + * Sets the parameters for a memset node in the given graphExec */ -TEST_CASE("Unit_hipGraphExecMemsetNodeSetParams_Negative") { - constexpr size_t N = 1024; - constexpr size_t Nbytes = N * sizeof(char); - constexpr size_t val = 0; - char *devData, *hOutputData; - HIP_CHECK(hipMalloc(&devData, Nbytes)); - hOutputData = reinterpret_cast(malloc(Nbytes)); - REQUIRE(hOutputData != nullptr); - memset(hOutputData, 0, Nbytes); - - hipGraph_t graph; - hipError_t ret; - hipGraphExec_t graphExec; - hipStream_t streamForGraph; - hipGraphNode_t memsetNode; +/** + * Test Description + * ------------------------ + * - Verify that node parameters get updated correctly by creating a node with valid but + * incorrect parameters, and then setting them to the correct values in the executable graph. + * The executable graph is run and the results of the memset is verified. + * hipGraphMemsetNodeGetParams is used to verify that node parameters in the graph were not updated, + * which also constitutes a test for said API. + * The test is repeated for all valid element sizes(1, + * 2, 4), and several allocations of different width(height is always 1 because only 1D memset nodes + * can be updated), both on host and device + * Test source + * ------------------------ + * - unit/graph/hipGraphExecMemsetNodeSetParams.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEMPLATE_TEST_CASE("Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic", "", uint8_t, uint16_t, + uint32_t) { + const size_t width = GENERATE(1, 64, kPageSize / sizeof(TestType) + 1); + hipGraph_t graph = nullptr; HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - hipMemsetParams mParams{}; - memset(&mParams, 0, sizeof(mParams)); - mParams.dst = reinterpret_cast(devData); - mParams.value = val; - mParams.pitch = 0; - mParams.elementSize = sizeof(char); - mParams.width = Nbytes; - mParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, &mParams)); + hipGraphNode_t node = nullptr; + LinearAllocGuard initial_alloc(LinearAllocs::hipMalloc, 2 * sizeof(TestType)); - std::vector dependencies; - dependencies.push_back(memsetNode); + hipMemsetParams initial_params = {}; + initial_params.dst = initial_alloc.ptr(); + initial_params.elementSize = sizeof(TestType); + initial_params.width = 2; + initial_params.height = 1; + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, &initial_params)); - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); - SECTION("Pass hGraphExec as nullptr") { - ret = hipGraphExecMemsetNodeSetParams(nullptr, memsetNode, &mParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass hGraphNode as nullptr") { - ret = hipGraphExecMemsetNodeSetParams(graphExec, nullptr, &mParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass different hGraphNode which was not used in graphExec") { - hipGraphNode_t memsetNode1{}; - ret = hipGraphExecMemsetNodeSetParams(graphExec, memsetNode1, &mParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass different Graph which was not used in graphExec") { - hipGraph_t graph1; - HIP_CHECK(hipGraphCreate(&graph1, 0)); - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph1, nullptr, 0, &mParams)); - ret = hipGraphExecMemsetNodeSetParams(graphExec, memsetNode, &mParams); - REQUIRE(hipErrorInvalidValue == ret); - HIP_CHECK(hipGraphDestroy(graph1)); - } - SECTION("Pass pNodeParams as nullptr") { - ret = hipGraphExecMemsetNodeSetParams(graphExec, memsetNode, nullptr); - REQUIRE(hipErrorInvalidValue == ret); - } -#if HT_NVIDIA - SECTION("Pass pNodeParams as empty structure object") { - hipMemsetParams mParmTemp{}; - ret = hipGraphExecMemsetNodeSetParams(graphExec, memsetNode, &mParmTemp); - REQUIRE(hipErrorInvalidValue == ret); - } -#endif - SECTION("Pass hipMemsetParams::dst as nullptr") { - mParams.dst = nullptr; - ret = hipGraphExecMemsetNodeSetParams(graphExec, memsetNode, &mParams); - REQUIRE(hipErrorInvalidValue == ret); - } -#if HT_NVIDIA - SECTION("Pass hipMemsetParams::element size other than 1, 2, or 4") { - mParams.dst = reinterpret_cast(devData); - mParams.elementSize = 9; - ret = hipGraphExecMemsetNodeSetParams(graphExec, memsetNode, &mParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass hipMemsetParams::height as zero") { - mParams.elementSize = sizeof(char); - mParams.height = 0; - ret = hipGraphExecMemsetNodeSetParams(graphExec, memsetNode, &mParams); - REQUIRE(hipErrorInvalidValue == ret); - } -#endif + LinearAllocGuard2D alloc(width, 1); + constexpr TestType set_value = 42; + hipMemsetParams params = {}; + params.dst = alloc.ptr(); + params.elementSize = sizeof(TestType); + params.width = width; + params.height = 1; + params.value = set_value; + HIP_CHECK(hipGraphExecMemsetNodeSetParams(graph_exec, node, ¶ms)); - free(hOutputData); - HIP_CHECK(hipFree(devData)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); + hipMemsetParams retrieved_params = {}; + HIP_CHECK(hipGraphMemsetNodeGetParams(node, &retrieved_params)); + REQUIRE(initial_params.dst == retrieved_params.dst); + REQUIRE(initial_params.elementSize == retrieved_params.elementSize); + REQUIRE(initial_params.width == retrieved_params.width); + REQUIRE(initial_params.height == retrieved_params.height); + REQUIRE(initial_params.pitch == retrieved_params.pitch); + REQUIRE(initial_params.value == retrieved_params.value); + + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); + + LinearAllocGuard buffer(LinearAllocs::hipHostMalloc, width * sizeof(TestType)); + HIP_CHECK(hipMemcpy2D(buffer.ptr(), width * sizeof(TestType), alloc.ptr(), alloc.pitch(), + width * sizeof(TestType), 1, hipMemcpyDeviceToHost)); + ArrayFindIfNot(buffer.ptr(), set_value, width); } -/* Test verifies hipGraphExecMemsetNodeSetParams API Functional scenarios. +/** + * Test Description + * ------------------------ + * - Verify API behaviour with invalid arguments: + * -# pGraphExec is nullptr + * -# node is nullptr + * -# pNodeParams is nullptr + * -# pNodeParams::dst is nullptr + * -# pNodeParams::elementSize is different from 1, 2, and 4 + * -# pNodeParams::width is zero + * -# pNodeParams::width is larger than the allocated memory region + * -# pNodeParams::height is zero + * -# pNodeParams::pitch is less than width when height is more than 1 + * -# pNodeParams::pitch * pMemsetParams::height is larger than the allocated memory region + * -# pNodeParams::dst holds a pointer to memory allocated on a device different from the one + * the original dst was allocated on + * Test source + * ------------------------ + * - unit/graph/hipGraphExecMemsetNodeSetParams.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipGraphExecMemsetNodeSetParams_Functional") { - constexpr size_t N = 1024; - constexpr size_t Nbytes = N * sizeof(char); - constexpr size_t val = 0; - constexpr size_t updateVal = 2; - char *devData, *devData1, *hOutputData, *hOutputData1; - - HIP_CHECK(hipMalloc(&devData, Nbytes)); - HIP_CHECK(hipMalloc(&devData1, Nbytes)); - hOutputData = reinterpret_cast(malloc(Nbytes)); - REQUIRE(hOutputData != nullptr); - memset(hOutputData, updateVal, Nbytes); - hOutputData1 = reinterpret_cast(malloc(Nbytes)); - REQUIRE(hOutputData1 != nullptr); - memset(hOutputData1, 0, Nbytes); - - hipGraph_t graph; - hipGraphExec_t graphExec; - hipStream_t streamForGraph; - hipGraphNode_t memsetNode; +TEST_CASE("Unit_hipGraphExecMemsetNodeSetParams_Negative_Parameters") { + using namespace std::placeholders; + hipGraph_t graph = nullptr; HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - hipMemsetParams memsetParams{}; - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData); - memsetParams.value = val; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = Nbytes; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, - &memsetParams)); + LinearAllocGuard alloc(LinearAllocs::hipMalloc, 4 * sizeof(int)); + hipMemsetParams params = {}; + params.dst = alloc.ptr(); + params.elementSize = sizeof(*alloc.ptr()); + params.width = 1; + params.height = 1; + params.value = 42; - std::vector dependencies; - dependencies.push_back(memsetNode); + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, ¶ms)) - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData1); - memsetParams.value = updateVal; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = Nbytes; - memsetParams.height = 1; + SECTION("pGraphExec == nullptr") { + HIP_CHECK_ERROR(hipGraphExecMemsetNodeSetParams(nullptr, node, ¶ms), hipErrorInvalidValue); + } - REQUIRE(hipSuccess == hipGraphExecMemsetNodeSetParams(graphExec, memsetNode, - &memsetParams)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); + SECTION("node == nullptr") { + HIP_CHECK_ERROR(hipGraphExecMemsetNodeSetParams(graph_exec, nullptr, ¶ms), + hipErrorInvalidValue); + } - HIP_CHECK(hipMemcpy(hOutputData1, devData1, Nbytes, hipMemcpyDeviceToHost)); - HipTest::checkArray(hOutputData, hOutputData1, Nbytes, 1); + MemsetCommonNegative(std::bind(hipGraphExecMemsetNodeSetParams, graph_exec, node, _1), params); - free(hOutputData); - free(hOutputData1); - HIP_CHECK(hipFree(devData)); - HIP_CHECK(hipFree(devData1)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); + SECTION("Changing dst allocation device") { + if (HipTest::getDeviceCount() < 2) { + HipTest::HIP_SKIP_TEST("Test requires two connected GPUs"); + return; + } + HIP_CHECK(hipSetDevice(1)); + LinearAllocGuard new_alloc(LinearAllocs::hipMalloc, 4 * sizeof(int)); + params.dst = new_alloc.ptr(); + HIP_CHECK_ERROR(hipGraphExecMemsetNodeSetParams(graph_exec, node, ¶ms), + hipErrorInvalidValue); + } + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); +} + +/** + * Test Description + * ------------------------ + * - Verify that a 2D node cannot be updated + * Test source + * ------------------------ + * - unit/graph/hipGraphExecMemsetNodeSetParams.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphExecMemsetNodeSetParams_Negative_Updating_Non1D_Node") { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + LinearAllocGuard2D alloc(2, 2); + hipMemsetParams params = {}; + params.dst = alloc.ptr(); + params.elementSize = sizeof(*alloc.ptr()); + params.width = 1; + params.height = 2; + params.pitch = alloc.pitch(); + params.value = 42; + + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, ¶ms)) + + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); + + params.width = 2; + HIP_CHECK_ERROR(hipGraphExecMemsetNodeSetParams(graph_exec, node, ¶ms), hipErrorInvalidValue); + + HIP_CHECK(hipGraphExecDestroy(graph_exec)); HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); } diff --git a/catch/unit/graph/hipGraphMemsetNodeGetParams.cc b/catch/unit/graph/hipGraphMemsetNodeGetParams.cc index 03cbcaaf2c..b776b9171e 100644 --- a/catch/unit/graph/hipGraphMemsetNodeGetParams.cc +++ b/catch/unit/graph/hipGraphMemsetNodeGetParams.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 @@ -17,125 +19,65 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** -Testcase Scenarios : -Negative - -1) Pass node as nullptr and verify api returns error code. -2) Pass pNodeParams as nullptr and verify api returns error code. -Functional - -1) Create a graph, add Memset node to graph with desired node params. - Verify api fetches the node params mentioned while adding Memset node. -2) Set Memset node params with hipGraphMemsetNodeSetParams, - now get the params and verify both are same. -*/ - +#include #include +#include -/* Test verifies hipGraphMemsetNodeGetParams API Negative scenarios. - */ -TEST_CASE("Unit_hipGraphMemsetNodeGetParams_Negative") { - hipError_t ret; - hipGraph_t graph; - hipGraphNode_t memsetNode; - - HIP_CHECK(hipGraphCreate(&graph, 0)); - - char *devData; - HIP_CHECK(hipMalloc(&devData, 1024)); - hipMemsetParams memsetParams{}; - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData); - memsetParams.value = 0; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = 1024; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, - &memsetParams)); - SECTION("Pass node as nullptr") { - ret = hipGraphMemsetNodeGetParams(nullptr, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass GetNodeParams as nullptr") { - ret = hipGraphMemsetNodeGetParams(memsetNode, nullptr); - REQUIRE(hipErrorInvalidValue == ret); - } - HIP_CHECK(hipFree(devData)); - HIP_CHECK(hipGraphDestroy(graph)); -} - -/* Test verifies hipGraphMemsetNodeGetParams API Functional scenarios. +/** + * @addtogroup hipGraphMemsetNodeGetParams hipGraphMemsetNodeGetParams + * @{ + * @ingroup GraphTest + * `hipGraphMemsetNodeGetParams(hipGraphNode_t node, hipMemsetParams *pNodeParams)` - + * Gets a memset node's parameters + * ________________________ + * Test cases from other APIs: + * - @ref Unit_hipGraphMemsetNodeSetParams_Positive_Basic + * - @ref Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic */ -bool memsetNodeCompare(hipMemsetParams *mNode1, hipMemsetParams *mNode2) { - if (mNode1->dst != mNode2->dst) - return false; - if (mNode1->elementSize != mNode2->elementSize) - return false; - if (mNode1->height != mNode2->height) - return false; - if (mNode1->pitch != mNode2->pitch) - return false; - if (mNode1->value != mNode2->value) - return false; - if (mNode1->width != mNode2->width) - return false; - return true; -} +/** + * Test Description + * ------------------------ + * - Verify API behaviour with invalid arguments: + * -# node is nullptr + * -# pNodeParams is nullptr + * -# node is destroyed + * Test source + * ------------------------ + * - unit/graph/hipGraphMemsetNodeGetParams.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphMemsetNodeGetParams_Negative_Parameters") { + LinearAllocGuard2D alloc(1, 1); + hipMemsetParams params = {}; + params.dst = alloc.ptr(); + params.elementSize = sizeof(int); + params.width = 1; + params.height = 1; -TEST_CASE("Unit_hipGraphMemsetNodeGetParams_Functional") { - constexpr size_t N = 1024; - constexpr size_t Nbytes = N * sizeof(char); - constexpr size_t val = 0; + hipGraph_t graph = nullptr; + hipGraphNode_t node = nullptr; - char *devData; - HIP_CHECK(hipMalloc(&devData, Nbytes)); - - hipGraph_t graph; - hipGraphNode_t memsetNode; - HIP_CHECK(hipGraphCreate(&graph, 0)); - hipStream_t streamForGraph; - HIP_CHECK(hipStreamCreate(&streamForGraph)); - - hipMemsetParams memsetParams{}; - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData); - memsetParams.value = val; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = Nbytes; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, - &memsetParams)); - SECTION("Get Memset Param and verify.") { - hipMemsetParams memsetGetParams; - REQUIRE(hipSuccess == hipGraphMemsetNodeGetParams(memsetNode, - &memsetGetParams)); - // Validating the result - REQUIRE(true == memsetNodeCompare(&memsetParams, &memsetGetParams)); + SECTION("node == nullptr") { + HIP_CHECK_ERROR(hipGraphMemsetNodeGetParams(nullptr, ¶ms), hipErrorInvalidValue); } - SECTION("Set memset node params then Get and verify.") { - constexpr size_t updateVal = 2; - char *devData1; - HIP_CHECK(hipMalloc(&devData1, Nbytes)); - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData1); - memsetParams.value = updateVal; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = Nbytes; - memsetParams.height = 1; - hipMemsetParams memsetGetParams; - REQUIRE(hipSuccess == hipGraphMemsetNodeSetParams(memsetNode, - &memsetParams)); - REQUIRE(hipSuccess == hipGraphMemsetNodeGetParams(memsetNode, - &memsetGetParams)); - // Validating the result - REQUIRE(true == memsetNodeCompare(&memsetParams, &memsetGetParams)); - HIP_CHECK(hipFree(devData1)); + SECTION("pNodeParams == nullptr") { + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, ¶ms)); + HIP_CHECK_ERROR(hipGraphMemsetNodeGetParams(node, nullptr), hipErrorInvalidValue); + HIP_CHECK(hipGraphDestroy(graph)); } - HIP_CHECK(hipFree(devData)); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); + +// Disabled on AMD due to defect - EXSWHTEC-208 +#if 0 + SECTION("Node is destroyed") { + HIP_CHECK(hipGraphCreate(&graph, 0)); + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, ¶ms)); + HIP_CHECK(hipGraphDestroy(graph)); + HIP_CHECK_ERROR(hipGraphMemsetNodeGetParams(node, ¶ms), hipErrorInvalidValue); + } +#endif } diff --git a/catch/unit/graph/hipGraphMemsetNodeSetParams.cc b/catch/unit/graph/hipGraphMemsetNodeSetParams.cc index fe188d48c8..937eaa06dd 100644 --- a/catch/unit/graph/hipGraphMemsetNodeSetParams.cc +++ b/catch/unit/graph/hipGraphMemsetNodeSetParams.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 @@ -17,169 +19,130 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** -Testcase Scenarios : -Negative - -1) Pass node as nullptr and verify api returns error code. -2) Pass pNodeParams as nullptr and verify api returns error code. -3) Passing hipMemsetParams::dst as nullptr should return error code. -4) Passing hipMemsetParams::element size other than 1, 2, or 4 and check. -5) Passing hipMemsetParams::height as zero and check api should return error code. -Functional - -1) Add Memset node to graph, update the node params with set and - launch the graph and check the set params are executing properly. -2) Add Memset node to graph, launch graph, then update the Memset node params - with set and launch the graph and check updated params are taking effect. -*/ +#include +#include #include -#include -/* Test verifies hipGraphMemsetNodeSetParams API invalid params scenarios. +#include "graph_memset_node_test_common.hh" +#include "graph_tests_common.hh" + +/** + * @addtogroup hipGraphMemsetNodeSetParams hipGraphMemsetNodeSetParams + * @{ + * @ingroup GraphTest + * `hipGraphMemsetNodeSetParams(hipGraphNode_t node, const hipMemsetParams *pNodeParams)` - + * Sets a memset node's parameters */ -TEST_CASE("Unit_hipGraphMemsetNodeSetParams_InvalidParams") { - hipError_t ret; - hipGraph_t graph; - hipStream_t streamForGraph; - hipGraphNode_t memsetNode; - HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - - char *devData; - HIP_CHECK(hipMalloc(&devData, 1024)); - hipMemsetParams memsetParams{}; - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData); - memsetParams.value = 0; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = 1024; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, - &memsetParams)); - SECTION("Pass node as nullptr") { - ret = hipGraphMemsetNodeSetParams(nullptr, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass GetNodeParams as nullptr") { - ret = hipGraphMemsetNodeSetParams(memsetNode, nullptr); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass dest as nullptr") { - memsetParams.dst = nullptr; - ret = hipGraphMemsetNodeSetParams(memsetNode, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } -#if HT_NVIDIA - SECTION("Pass element size other than 1, 2, or 4") { - memsetParams.dst = reinterpret_cast(devData); - memsetParams.elementSize = 9; - ret = hipGraphMemsetNodeSetParams(memsetNode, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } - SECTION("Pass height as zero or negative") { - memsetParams.elementSize = 2; - memsetParams.height = 0; - ret = hipGraphMemsetNodeSetParams(memsetNode, &memsetParams); - REQUIRE(hipErrorInvalidValue == ret); - } -#endif - HIP_CHECK(hipFree(devData)); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); -} - -static void validate_result(char *hData, size_t size, char val) { - // Validating the result - for (size_t i = 0; i < size; i++) { - if (hData[i] != val) { - WARN("Validation failed at- " << i << " hData[i] " << hData[i]); - REQUIRE(false); - } - } -} - -/* Test verifies hipGraphMemsetNodeSetParams API Functional scenarios. +/** + * Test Description + * ------------------------ + * - Verify that node parameters get updated correctly by creating a node with valid but + * incorrect parameters, and then setting them to the correct values after which the graph is + * executed and the results verified. + * The parameters are also verified via hipGraphMemsetNodeGetParams, which also constitutes a test + * for said API. + * The test is repeated for all valid element sizes(1, 2, 4), and several allocations of different + * height and width both on host and device + * Test source + * ------------------------ + * - unit/graph/hipGraphMemsetNodeSetParams.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 */ -TEST_CASE("Unit_hipGraphMemsetNodeSetParams_Functional") { - constexpr size_t N = 1024; - constexpr size_t Nbytes = N * sizeof(char); - constexpr size_t val = 0; - constexpr size_t updateVal = 1; - constexpr size_t updateVal2 = 2; - char *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - char *A_h{nullptr}, *B_h{nullptr}; +TEMPLATE_TEST_CASE("Unit_hipGraphMemsetNodeSetParams_Positive_Basic", "", uint8_t, uint16_t, + uint32_t) { + const auto f = [](hipMemsetParams* params) { + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, nullptr, N, false); + hipGraphNode_t node = nullptr; + LinearAllocGuard initial_alloc(LinearAllocs::hipMalloc, 2 * sizeof(TestType)); - hipGraph_t graph; - hipGraphExec_t graphExec; - hipStream_t streamForGraph; - hipGraphNode_t memsetNode; + hipMemsetParams initial_params = {}; + initial_params.dst = initial_alloc.ptr(); + initial_params.elementSize = sizeof(TestType); + initial_params.width = 2; + initial_params.height = 1; + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, &initial_params)); - HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); + hipMemsetParams retrieved_params = {}; + HIP_CHECK(hipGraphMemsetNodeGetParams(node, &retrieved_params)); + REQUIRE(initial_params.dst == retrieved_params.dst); + REQUIRE(initial_params.elementSize == retrieved_params.elementSize); + REQUIRE(initial_params.width == retrieved_params.width); + REQUIRE(initial_params.height == retrieved_params.height); + REQUIRE(initial_params.pitch == retrieved_params.pitch); + REQUIRE(initial_params.value == retrieved_params.value); - hipMemsetParams memsetParams{}; - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(C_d); - memsetParams.value = val; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = Nbytes; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, - &memsetParams)); + HIP_CHECK(hipGraphMemsetNodeSetParams(node, params)); + HIP_CHECK(hipGraphMemsetNodeGetParams(node, &retrieved_params)); + REQUIRE(params->dst == retrieved_params.dst); + REQUIRE(params->elementSize == retrieved_params.elementSize); + REQUIRE(params->width == retrieved_params.width); + REQUIRE(params->height == retrieved_params.height); + REQUIRE(params->pitch == retrieved_params.pitch); + REQUIRE(params->value == retrieved_params.value); - std::vector dependencies; - dependencies.push_back(memsetNode); + hipGraphExec_t graph_exec = nullptr; + HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0)); - SECTION("Update the memsetNode and check") { - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(A_d); - memsetParams.value = updateVal; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = Nbytes; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, dependencies.data(), - dependencies.size(), &memsetParams)); - HIP_CHECK(hipGraphMemsetNodeSetParams(memsetNode, &memsetParams)); - dependencies.push_back(memsetNode); + HIP_CHECK(hipGraphLaunch(graph_exec, hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); + HIP_CHECK(hipGraphExecDestroy(graph_exec)); + HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipMemcpy(A_h, A_d, Nbytes, hipMemcpyDeviceToHost)); - validate_result(A_h, Nbytes, updateVal); - } - SECTION("Update the memsetNode again and check") { - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(B_d); - memsetParams.value = updateVal2; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = Nbytes; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, dependencies.data(), - dependencies.size(), &memsetParams)); - HIP_CHECK(hipGraphMemsetNodeSetParams(memsetNode, &memsetParams)); - dependencies.push_back(memsetNode); + return hipSuccess; + }; - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); - - HIP_CHECK(hipMemcpy(B_h, B_d, Nbytes, hipMemcpyDeviceToHost)); - validate_result(B_h, Nbytes, updateVal2); - } - - HipTest::freeArrays(A_d, B_d, C_d, - A_h, B_h, nullptr, false); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipGraphDestroy(graph)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); + GraphMemsetNodeCommonPositive(f); +} + +/** + * Test Description + * ------------------------ + * - Verify API behaviour with invalid arguments: + * -# node is nullptr + * -# pNodeParams is nullptr + * -# pNodeParams::dst is nullptr + * -# pNodeParams::elementSize is different from 1, 2, and 4 + * -# pNodeParams::width is zero + * -# pNodeParams::width is larger than the allocated memory region + * -# pNodeParams::height is zero + * -# pNodeParams::pitch is less than width when height is more than 1 + * -# pNodeParams::pitch * pMemsetParams::height is larger than the allocated memory region + * Test source + * ------------------------ + * - unit/graph/hipGraphMemsetNodeSetParams.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphMemsetNodeSetParams_Negative_Parameters") { + using namespace std::placeholders; + + hipGraph_t graph = nullptr; + HIP_CHECK(hipGraphCreate(&graph, 0)); + + LinearAllocGuard alloc(LinearAllocs::hipMalloc, 4 * sizeof(int)); + hipMemsetParams params = {}; + params.dst = alloc.ptr(); + params.elementSize = sizeof(*alloc.ptr()); + params.width = 1; + params.height = 1; + params.value = 42; + + hipGraphNode_t node = nullptr; + HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, ¶ms)) + + SECTION("node == nullptr") { + HIP_CHECK_ERROR(hipGraphMemsetNodeSetParams(nullptr, ¶ms), hipErrorInvalidValue); + } + + MemsetCommonNegative(std::bind(hipGraphMemsetNodeSetParams, node, _1), params); + + HIP_CHECK(hipGraphDestroy(graph)); } From 42ebc6cd102a7d5a37b261003f9c0181ad9f77b3 Mon Sep 17 00:00:00 2001 From: Marko Veniger <91256249+marko-veniger@users.noreply.github.com> Date: Mon, 6 Mar 2023 12:26:24 +0100 Subject: [PATCH 06/13] EXSWHTEC-171 - Hip graph creation and destruction tests (#35) * EXSWHTEC-171 - Implement positive and negative unit tests for the following API's: - hipGraphCreate - hipGraphDestroy --- catch/unit/graph/CMakeLists.txt | 2 + catch/unit/graph/hipGraphCreate.cc | 62 +++++++++++++------ catch/unit/graph/hipGraphDestroy.cc | 92 ++++++++++++----------------- 3 files changed, 85 insertions(+), 71 deletions(-) diff --git a/catch/unit/graph/CMakeLists.txt b/catch/unit/graph/CMakeLists.txt index 9c06e091b0..ee58c1672c 100644 --- a/catch/unit/graph/CMakeLists.txt +++ b/catch/unit/graph/CMakeLists.txt @@ -89,6 +89,8 @@ set(TEST_SRC hipGraphHostNodeGetParams.cc hipGraphExecChildGraphNodeSetParams.cc hipStreamGetCaptureInfo_v2.cc + hipGraphCreate.cc + hipGraphDestroy.cc hipStreamUpdateCaptureDependencies.cc hipThreadExchangeStreamCaptureMode.cc hipLaunchHostFunc.cc diff --git a/catch/unit/graph/hipGraphCreate.cc b/catch/unit/graph/hipGraphCreate.cc index befcc69c7e..301bf345a0 100644 --- a/catch/unit/graph/hipGraphCreate.cc +++ b/catch/unit/graph/hipGraphCreate.cc @@ -20,25 +20,53 @@ THE SOFTWARE. #include /** -Negative Testcase Scenarios : -1) Creating HipGraph with nullptr. -2) Creating hipGraph with non-zero mode. -*/ + * @addtogroup hipGraphCreate hipGraphCreate + * @{ + * @ingroup GraphTest + * `hipGraphCreate(hipGraph_t *pGraph, unsigned int flags)` - + * creates a graph + */ -TEST_CASE("Unit_hipGraphCreate_Negative") { - hipError_t ret; - SECTION("Creating HipGraph with nullptr") { - ret = hipGraphCreate(nullptr, 0); - REQUIRE(hipErrorInvalidValue == ret); +/** + * Test Description + * ------------------------ + * - Negative parameter test for hipGraphCreate: + * -# Expected hipErrorInvalidValue when pGraph is null + * -# Expected hipErrorInvalidValue when flags is not 0 + * Test source + * ------------------------ + * - unit/graph/hipGraphCreate.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphCreate_Negative_Parameters") { + hipGraph_t graph = nullptr; + + SECTION("pGraph is nullptr") { + HIP_CHECK_ERROR(hipGraphCreate(nullptr, 0), hipErrorInvalidValue); } - SECTION("Creating hipGraph with non-zero mode") { - hipGraph_t graph{}; - ret = hipGraphCreate(&graph, -1); - REQUIRE(hipErrorInvalidValue == ret); - - ret = hipGraphCreate(nullptr, -1); - REQUIRE(hipErrorInvalidValue == ret); - } + SECTION("flags is not 0") { HIP_CHECK_ERROR(hipGraphCreate(&graph, 1), hipErrorInvalidValue); } } +/** + * Test Description + * ------------------------ + * - Basic positive test for hipGraphCreate + * - Create an emtpy graph + * Test source + * ------------------------ + * - unit/graph/hipGraphCreate.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphCreate_Positive_Basic") { + hipGraph_t graph = nullptr; + + HIP_CHECK(hipGraphCreate(&graph, 0)); + REQUIRE(nullptr != graph); + + HIP_CHECK(hipGraphDestroy(graph)); +} diff --git a/catch/unit/graph/hipGraphDestroy.cc b/catch/unit/graph/hipGraphDestroy.cc index 15b8263f13..8ec41e4862 100644 --- a/catch/unit/graph/hipGraphDestroy.cc +++ b/catch/unit/graph/hipGraphDestroy.cc @@ -20,62 +20,46 @@ THE SOFTWARE. #include /** -Negative Testcase Scenarios : -1) Pass graph as nullptr and verify. -2) Destroy already destroyed graph and check api returns error code. -3) Destroy graph when is in use and make sure api handles it gracefully. -*/ + * @addtogroup hipGraphDestroy hipGraphDestroy + * @{ + * @ingroup GraphTest + * `hipGraphDestroy(hipGraph_t graph)` - + * Destroys a graph + */ -TEST_CASE("Unit_hipGraphDestroy_Negative") { - hipError_t ret; - SECTION("Deleting HipGraph with nullptr") { - ret = hipGraphDestroy(nullptr); - REQUIRE(hipErrorInvalidValue == ret); - } -#if HT_AMD - SECTION("Destroy already destroyed graph and check api returns error code") { - hipGraph_t graph; - HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipGraphDestroy(graph)); +/** + * Test Description + * ------------------------ + * - Basic positive test for hipGraphDestroy + * - Create an emtpy graph and then destroy it + * Test source + * ------------------------ + * - unit/graph/hipGraphDestroy.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphDestroy_Positive_Basic") { + hipGraph_t graph = nullptr; - ret = hipGraphDestroy(graph); - REQUIRE(hipErrorIllegalState == ret); - } -#endif - SECTION("Destroy graph when is in use and make sure api handles" - " it gracefully.") { - hipGraph_t graph; - hipGraphExec_t graphExec; - hipStream_t streamForGraph; - hipGraphNode_t memsetNode; + HIP_CHECK(hipGraphCreate(&graph, 0)); + REQUIRE(nullptr != graph); - HIP_CHECK(hipGraphCreate(&graph, 0)); - HIP_CHECK(hipStreamCreate(&streamForGraph)); - - char *devData; - HIP_CHECK(hipMalloc(&devData, 1024)); - hipMemsetParams memsetParams{}; - memset(&memsetParams, 0, sizeof(memsetParams)); - memsetParams.dst = reinterpret_cast(devData); - memsetParams.value = 0; - memsetParams.pitch = 0; - memsetParams.elementSize = sizeof(char); - memsetParams.width = 1024; - memsetParams.height = 1; - HIP_CHECK(hipGraphAddMemsetNode(&memsetNode, graph, nullptr, 0, - &memsetParams)); - - HIP_CHECK(hipGraphInstantiate(&graphExec, graph, nullptr, nullptr, 0)); - REQUIRE(graphExec != nullptr); - - HIP_CHECK(hipGraphDestroy(graph)); - - HIP_CHECK(hipGraphLaunch(graphExec, streamForGraph)); - HIP_CHECK(hipStreamSynchronize(streamForGraph)); - - HIP_CHECK(hipFree(devData)); - HIP_CHECK(hipGraphExecDestroy(graphExec)); - HIP_CHECK(hipStreamDestroy(streamForGraph)); - } + HIP_CHECK(hipGraphDestroy(graph)); } +/** + * Test Description + * ------------------------ + * - Basic negative parameter test for hipGraphDestroy + * -# Expected hipErrorInvalidValue when graph is invalid + * Test source + * ------------------------ + * - unit/graph/hipGraphDestroy.cc + * Test requirements + * ------------------------ + * - HIP_VERSION >= 5.2 + */ +TEST_CASE("Unit_hipGraphDestroy_Negative_Parameters") { + HIP_CHECK_ERROR(hipGraphDestroy(static_cast(nullptr)), hipErrorInvalidValue); +} From 4f9ba54fb175861ce6c303e4efcf1f90a136aa1e Mon Sep 17 00:00:00 2001 From: ROCm CI Service Account <66695075+rocm-ci@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:57:04 +0530 Subject: [PATCH 07/13] SWDEV-327563 - stress tests executable name change (#183) Helps to differentiate functional tests vs stress tests Change-Id: I6269fc58f21c9d4e8a71268006b8da926f0ddbe0 --- catch/stress/deviceallocation/CMakeLists.txt | 2 +- catch/stress/memory/CMakeLists.txt | 2 +- catch/stress/printf/CMakeLists.txt | 2 +- catch/stress/stream/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/catch/stress/deviceallocation/CMakeLists.txt b/catch/stress/deviceallocation/CMakeLists.txt index ee7eb2a580..1db7bef132 100644 --- a/catch/stress/deviceallocation/CMakeLists.txt +++ b/catch/stress/deviceallocation/CMakeLists.txt @@ -3,6 +3,6 @@ set(TEST_SRC Stress_deviceAllocationStress.cc ) -hip_add_exe_to_target(NAME devalloc +hip_add_exe_to_target(NAME devalloc_stress TEST_SRC ${TEST_SRC} TEST_TARGET_NAME stress_test) diff --git a/catch/stress/memory/CMakeLists.txt b/catch/stress/memory/CMakeLists.txt index bf172bd642..a455dea73d 100644 --- a/catch/stress/memory/CMakeLists.txt +++ b/catch/stress/memory/CMakeLists.txt @@ -7,6 +7,6 @@ set(TEST_SRC hipHostMallocStress.cc ) -hip_add_exe_to_target(NAME memory +hip_add_exe_to_target(NAME memory_stress TEST_SRC ${TEST_SRC} TEST_TARGET_NAME stress_test) diff --git a/catch/stress/printf/CMakeLists.txt b/catch/stress/printf/CMakeLists.txt index 1977d293f2..a1f6ac6b8f 100644 --- a/catch/stress/printf/CMakeLists.txt +++ b/catch/stress/printf/CMakeLists.txt @@ -4,6 +4,6 @@ set(TEST_SRC Stress_printf_SimpleKernels.cc ) -hip_add_exe_to_target(NAME printf +hip_add_exe_to_target(NAME printf_stress TEST_SRC ${TEST_SRC} TEST_TARGET_NAME stress_test) diff --git a/catch/stress/stream/CMakeLists.txt b/catch/stress/stream/CMakeLists.txt index ae6685da58..d3d6fd8644 100644 --- a/catch/stress/stream/CMakeLists.txt +++ b/catch/stress/stream/CMakeLists.txt @@ -4,7 +4,7 @@ set(TEST_SRC streamEnqueue.cc ) -hip_add_exe_to_target(NAME stream +hip_add_exe_to_target(NAME stream_stress TEST_SRC ${TEST_SRC} TEST_TARGET_NAME stress_test COMPILE_OPTIONS -std=c++14) From ce788b3cef4340ab5e4ea8e1aa0c1e3d0138acfa Mon Sep 17 00:00:00 2001 From: ROCm CI Service Account <66695075+rocm-ci@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:57:29 +0530 Subject: [PATCH 08/13] SWDEV-383201 - Sync for two memory tests (#184) Unit_hipMemcpy_Positive_Synchronization_Behavior Unit_hipMemcpyHtoD_Positive_Synchronization_Behavior Change-Id: Ia0d188d93fde99b4400a6b5ab60fcde969c2405d --- catch/include/memcpy1d_tests_common.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/catch/include/memcpy1d_tests_common.hh b/catch/include/memcpy1d_tests_common.hh index 72c1500fc8..37d48a95c2 100644 --- a/catch/include/memcpy1d_tests_common.hh +++ b/catch/include/memcpy1d_tests_common.hh @@ -237,6 +237,7 @@ void MemcpySyncBehaviorCheck(F memcpy_func, const bool should_sync, LaunchDelayKernel(std::chrono::milliseconds{100}, kernel_stream); HIP_CHECK(memcpy_func()); if (should_sync) { + HIP_CHECK(hipStreamSynchronize(kernel_stream)); HIP_CHECK(hipStreamQuery(kernel_stream)); } else { HIP_CHECK_ERROR(hipStreamQuery(kernel_stream), hipErrorNotReady); From d1212bb9ab9a559d93d239828584fff49560141b Mon Sep 17 00:00:00 2001 From: ROCm CI Service Account <66695075+rocm-ci@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:57:51 +0530 Subject: [PATCH 09/13] SWDEV-379358 - [catch2][dtest] DeviceLib tests migrated from direct to catch2 (#185) Change-Id: Ie69bb0189574e8e9e411aa946b27751efd99f322 --- catch/unit/deviceLib/CMakeLists.txt | 12 + .../deviceLib/DoublePrecisionIntrinsics.cc | 81 +++++ .../deviceLib/DoublePrecisionMathDevice.cc | 133 ++++++++ .../unit/deviceLib/DoublePrecisionMathHost.cc | 117 +++++++ catch/unit/deviceLib/FloatMathPrecise.cc | 128 ++++++++ catch/unit/deviceLib/IntegerIntrinsics.cc | 68 ++++ catch/unit/deviceLib/SimpleAtomicsTest.cc | 298 ++++++++++++++++++ .../deviceLib/SinglePrecisionIntrinsics.cc | 101 ++++++ .../deviceLib/SinglePrecisionMathDevice.cc | 123 ++++++++ .../unit/deviceLib/SinglePrecisionMathHost.cc | 113 +++++++ catch/unit/deviceLib/hipStdComplex.cc | 150 +++++++++ catch/unit/deviceLib/hipTestAtomicAdd.cc | 222 +++++++++++++ catch/unit/deviceLib/hipTestClock.cc | 51 +++ 13 files changed, 1597 insertions(+) create mode 100644 catch/unit/deviceLib/DoublePrecisionIntrinsics.cc create mode 100644 catch/unit/deviceLib/DoublePrecisionMathDevice.cc create mode 100644 catch/unit/deviceLib/DoublePrecisionMathHost.cc create mode 100644 catch/unit/deviceLib/FloatMathPrecise.cc create mode 100644 catch/unit/deviceLib/IntegerIntrinsics.cc create mode 100644 catch/unit/deviceLib/SimpleAtomicsTest.cc create mode 100644 catch/unit/deviceLib/SinglePrecisionIntrinsics.cc create mode 100644 catch/unit/deviceLib/SinglePrecisionMathDevice.cc create mode 100644 catch/unit/deviceLib/SinglePrecisionMathHost.cc create mode 100644 catch/unit/deviceLib/hipStdComplex.cc create mode 100644 catch/unit/deviceLib/hipTestAtomicAdd.cc create mode 100644 catch/unit/deviceLib/hipTestClock.cc diff --git a/catch/unit/deviceLib/CMakeLists.txt b/catch/unit/deviceLib/CMakeLists.txt index 3ba4bddd39..89d38a136e 100644 --- a/catch/unit/deviceLib/CMakeLists.txt +++ b/catch/unit/deviceLib/CMakeLists.txt @@ -14,6 +14,18 @@ set(TEST_SRC syncthreadscount.cc syncthreadsor.cc Atomic_func.cc + DoublePrecisionIntrinsics.cc + DoublePrecisionMathDevice.cc + DoublePrecisionMathHost.cc + FloatMathPrecise.cc + IntegerIntrinsics.cc + SinglePrecisionIntrinsics.cc + SinglePrecisionMathDevice.cc + SinglePrecisionMathHost.cc + SimpleAtomicsTest.cc + hipTestAtomicAdd.cc + hipStdComplex.cc + hipTestClock.cc ) if(UNIX) diff --git a/catch/unit/deviceLib/DoublePrecisionIntrinsics.cc b/catch/unit/deviceLib/DoublePrecisionIntrinsics.cc new file mode 100644 index 0000000000..6801decb9e --- /dev/null +++ b/catch/unit/deviceLib/DoublePrecisionIntrinsics.cc @@ -0,0 +1,81 @@ +/* +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 + +#pragma GCC diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wunused-variable" + +__device__ void double_precision_intrinsics() { +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __dadd_rd(0.0, 1.0); +#endif + __dadd_rn(0.0, 1.0); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __dadd_ru(0.0, 1.0); + __dadd_rz(0.0, 1.0); + __ddiv_rd(0.0, 1.0); +#endif + __ddiv_rn(0.0, 1.0); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __ddiv_ru(0.0, 1.0); + __ddiv_rz(0.0, 1.0); + __dmul_rd(1.0, 2.0); +#endif + __dmul_rn(1.0, 2.0); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __dmul_ru(1.0, 2.0); + __dmul_rz(1.0, 2.0); + __drcp_rd(2.0); +#endif + __drcp_rn(2.0); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __drcp_ru(2.0); + __drcp_rz(2.0); + __dsqrt_rd(4.0); +#endif + __dsqrt_rn(4.0); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __dsqrt_ru(4.0); + __dsqrt_rz(4.0); + __dsub_rd(2.0, 1.0); +#endif + __dsub_rn(2.0, 1.0); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __dsub_ru(2.0, 1.0); + __dsub_rz(2.0, 1.0); + __fma_rd(1.0, 2.0, 3.0); +#endif + __fma_rn(1.0, 2.0, 3.0); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fma_ru(1.0, 2.0, 3.0); + __fma_rz(1.0, 2.0, 3.0); +#endif +} + +__global__ void compileDoublePrecisionIntrinsics(int) { + double_precision_intrinsics(); +} + +TEST_CASE("Unit_DoublePrecisionIntrinsics") { + hipLaunchKernelGGL(compileDoublePrecisionIntrinsics, dim3(1, 1, 1), + dim3(1, 1, 1), 0, 0, 1); +} diff --git a/catch/unit/deviceLib/DoublePrecisionMathDevice.cc b/catch/unit/deviceLib/DoublePrecisionMathDevice.cc new file mode 100644 index 0000000000..9c695a7b41 --- /dev/null +++ b/catch/unit/deviceLib/DoublePrecisionMathDevice.cc @@ -0,0 +1,133 @@ +/* +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 + + +#pragma GCC diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wunused-variable" + +__device__ void double_precision_math_functions() { + int iX; + double fX, fY; + + acos(1.0); + acosh(1.0); + asin(0.0); + asinh(0.0); + atan(0.0); + atan2(0.0, 1.0); + atanh(0.0); + cbrt(0.0); + ceil(0.0); + copysign(1.0, -2.0); + cos(0.0); + cosh(0.0); + cospi(0.0); + cyl_bessel_i0(0.0); + cyl_bessel_i1(0.0); + erf(0.0); + erfc(0.0); + erfcinv(2.0); + erfcx(0.0); + erfinv(1.0); + exp(0.0); + exp10(0.0); + exp2(0.0); + expm1(0.0); + fabs(1.0); + fdim(1.0, 0.0); + floor(0.0); + fma(1.0, 2.0, 3.0); + fmax(0.0, 0.0); + fmin(0.0, 0.0); + fmod(0.0, 1.0); + frexp(0.0, &iX); + hypot(1.0, 0.0); + ilogb(1.0); + isfinite(0.0); + isinf(0.0); + isnan(0.0); + j0(0.0); + j1(0.0); + jn(-1.0, 1.0); + ldexp(0.0, 0); + lgamma(1.0); + llrint(0.0); + llround(0.0); + log(1.0); + log10(1.0); + log1p(-1.0); + log2(1.0); + logb(1.0); + lrint(0.0); + lround(0.0); + modf(0.0, &fX); + nan("1"); + nearbyint(0.0); + nextafter(0.0, 0.0); + fX = 1.0; + norm(1, &fX); + norm3d(1.0, 0.0, 0.0); + norm4d(1.0, 0.0, 0.0, 0.0); + normcdf(0.0); + normcdfinv(1.0); + pow(1.0, 0.0); + rcbrt(1.0); + remainder(2.0, 1.0); + remquo(1.0, 2.0, &iX); + rhypot(0.0, 1.0); + rint(1.0); + fX = 1.0; + rnorm(1, &fX); + rnorm3d(0.0, 0.0, 1.0); + rnorm4d(0.0, 0.0, 0.0, 1.0); + round(0.0); + rsqrt(1.0); + scalbln(0.0, 1); + scalbn(0.0, 1); + signbit(1.0); + sin(0.0); +#if HT_AMD + // NV A100 has a bug in sincos(), so temporarily disbale it + sincos(0.0, &fX, &fY); +#endif + sincospi(0.0, &fX, &fY); + sinh(0.0); + sinpi(0.0); + sqrt(0.0); + tan(0.0); + tanh(0.0); + tgamma(2.0); + trunc(0.0); + y0(1.0); + y1(1.0); + yn(1, 1.0); +} + +__global__ void compileDoublePrecisionMathOnDevice(int) { + double_precision_math_functions(); +} + +TEST_CASE("Unit_DoublePrecisionMathDevice") { + hipLaunchKernelGGL(compileDoublePrecisionMathOnDevice, dim3(1, 1, 1), + dim3(1, 1, 1), 0, 0, 1); +} diff --git a/catch/unit/deviceLib/DoublePrecisionMathHost.cc b/catch/unit/deviceLib/DoublePrecisionMathHost.cc new file mode 100644 index 0000000000..fd4e4bf238 --- /dev/null +++ b/catch/unit/deviceLib/DoublePrecisionMathHost.cc @@ -0,0 +1,117 @@ +/* +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 + +#pragma GCC diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wunused-variable" + +__host__ static void double_precision_math_functions() { + int iX; + double fX, fY; + + acos(1.0); + acosh(1.0); + asin(0.0); + asinh(0.0); + atan(0.0); + atan2(0.0, 1.0); + atanh(0.0); + cbrt(0.0); + ceil(0.0); + copysign(1.0, -2.0); + cos(0.0); + cosh(0.0); + erf(0.0); + erfc(0.0); + exp(0.0); + #ifdef __unix__ + exp10(0.0); + #endif + exp2(0.0); + expm1(0.0); + fabs(1.0); + fdim(1.0, 0.0); + floor(0.0); + fma(1.0, 2.0, 3.0); + fmax(0.0, 0.0); + fmin(0.0, 0.0); + fmod(0.0, 1.0); + frexp(0.0, &iX); + hypot(1.0, 0.0); + ilogb(1.0); + std::isfinite(0.0); + std::isinf(0.0); + std::isnan(0.0); + #ifdef __unix__ + j0(0.0); + j1(0.0); + jn(-1.0, 1.0); + #elif _WIN64 + _j0(0.0); + _j1(0.0); + _jn(-1.0, 1.0); + #endif + ldexp(0.0, 0); + llrint(0.0); + llround(0.0); + log(1.0); + log10(1.0); + log1p(-1.0); + log2(1.0); + logb(1.0); + lrint(0.0); + lround(0.0); + modf(0.0, &fX); + nan("1"); + nearbyint(0.0); + fX = 1.0; + pow(1.0, 0.0); + remainder(2.0, 1.0); + remquo(1.0, 2.0, &iX); + rint(1.0); + round(0.0); + scalbln(0.0, 1); + scalbn(0.0, 1); + std::signbit(1.0); + sin(0.0); + #ifdef _unix__ + sincos(0.0, &fX, &fY); + #endif + sinh(0.0); + sqrt(0.0); + tan(0.0); + tanh(0.0); + tgamma(2.0); + trunc(0.0); + #ifdef __unix__ + y0(1.0); + y1(1.0); + yn(1, 1.0); + #elif _WIN64 + _y0(1.0); + _y1(1.0); + _yn(1, 1.0); + #endif +} + +TEST_CASE("Unit_DoublePrecisionMathHost") { + double_precision_math_functions(); +} diff --git a/catch/unit/deviceLib/FloatMathPrecise.cc b/catch/unit/deviceLib/FloatMathPrecise.cc new file mode 100644 index 0000000000..357f2ed918 --- /dev/null +++ b/catch/unit/deviceLib/FloatMathPrecise.cc @@ -0,0 +1,128 @@ +/* +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 +#include + +__device__ void FloatMathPrecise() { + int iX; + float fX, fY; + + acosf(1.0f); + acoshf(1.0f); + asinf(0.0f); + asinhf(0.0f); + atan2f(0.0f, 1.0f); + atanf(0.0f); + atanhf(0.0f); + cbrtf(0.0f); + fX = ceilf(0.0f); + fX = copysignf(1.0f, -2.0f); + cosf(0.0f); + coshf(0.0f); + cospif(0.0f); + cyl_bessel_i0f(0.0f); + cyl_bessel_i1f(0.0f); + erfcf(0.0f); + erfcinvf(2.0f); + erfcxf(0.0f); + erff(0.0f); + erfinvf(1.0f); + exp10f(0.0f); + exp2f(0.0f); + expf(0.0f); + expm1f(0.0f); + fX = fabsf(1.0f); + fdimf(1.0f, 0.0f); + fdividef(0.0f, 1.0f); + fX = floorf(0.0f); + fmaf(1.0f, 2.0f, 3.0f); + fX = fmaxf(0.0f, 0.0f); + fX = fminf(0.0f, 0.0f); + fmodf(0.0f, 1.0f); + frexpf(0.0f, &iX); + hypotf(1.0f, 0.0f); + ilogbf(1.0f); + isfinite(0.0f); + fX = isinf(0.0f); + fX = isnan(0.0f); + j0f(0.0f); + j1f(0.0f); + jnf(-1.0f, 1.0f); + ldexpf(0.0f, 0); + lgammaf(1.0f); + llrintf(0.0f); + llroundf(0.0f); + log10f(1.0f); + log1pf(-1.0f); + log2f(1.0f); + logbf(1.0f); + logf(1.0f); + lrintf(0.0f); + lroundf(0.0f); + modff(0.0f, &fX); + fX = nanf("1"); + fX = nearbyintf(0.0f); + nextafterf(0.0f, 0.0f); + norm3df(1.0f, 0.0f, 0.0f); + norm4df(1.0f, 0.0f, 0.0f, 0.0f); + normcdff(0.0f); + normcdfinvf(1.0f); + fX = 1.0f; + normf(1, &fX); + powf(1.0f, 0.0f); + rcbrtf(1.0f); + remainderf(2.0f, 1.0f); + remquof(1.0f, 2.0f, &iX); + rhypotf(0.0f, 1.0f); + fY = rintf(1.0f); + rnorm3df(0.0f, 0.0f, 1.0f); + rnorm4df(0.0f, 0.0f, 0.0f, 1.0f); + fX = 1.0f; + rnormf(1, &fX); + fY = roundf(0.0f); + rsqrtf(1.0f); + scalblnf(0.0f, 1); + scalbnf(0.0f, 1); + signbit(1.0f); + sincosf(0.0f, &fX, &fY); + sincospif(0.0f, &fX, &fY); + sinf(0.0f); + sinhf(0.0f); + sinpif(0.0f); + sqrtf(0.0f); + tanf(0.0f); + tanhf(0.0f); + tgammaf(2.0f); + fY = truncf(0.0f); + y0f(1.0f); + y1f(1.0f); + ynf(1, 1.0f); +} + +__global__ void CompileFloatMathPrecise(int) { + FloatMathPrecise(); +} + +TEST_CASE("Unit_FloatMathPrecise") { + hipLaunchKernelGGL(CompileFloatMathPrecise, dim3(1, 1, 1), + dim3(1, 1, 1), 0, 0, 1); +} diff --git a/catch/unit/deviceLib/IntegerIntrinsics.cc b/catch/unit/deviceLib/IntegerIntrinsics.cc new file mode 100644 index 0000000000..68009651bd --- /dev/null +++ b/catch/unit/deviceLib/IntegerIntrinsics.cc @@ -0,0 +1,68 @@ +/* +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 +#include +#include + +#pragma GCC diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wunused-variable" + +__device__ void integer_intrinsics() { + __brev((unsigned int)10); + __brevll((uint64_t)10); + __byte_perm((unsigned int)0, (unsigned int)0, 0); + __clz(static_cast(10)); + __clzll((int64_t)10); + __ffs(static_cast(10)); + __ffsll((long long)(10)); // NOLINT + __funnelshift_l((unsigned int)0xfacefeed, (unsigned int)0xdeadbeef, 0); + __funnelshift_lc((unsigned int)0xfacefeed, (unsigned int)0xdeadbeef, 0); + __funnelshift_r((unsigned int)0xfacefeed, (unsigned int)0xdeadbeef, 0); + __funnelshift_rc((unsigned int)0xfacefeed, (unsigned int)0xdeadbeef, 0); + __hadd(static_cast(1), static_cast(3)); + __mul24(static_cast(1), static_cast(2)); + __mul64hi((int64_t)1, (int64_t)2); + __mulhi(static_cast(1), static_cast(2)); + __popc((unsigned int)4); + __popcll((uint64_t)4); + int a = min(static_cast(4), static_cast(5)); + int b = max(static_cast(4), static_cast(5)); + __rhadd(static_cast(1), static_cast(2)); + __sad(static_cast(1), static_cast(2), 0); + __uhadd((unsigned int)1, (unsigned int)3); + __umul24((unsigned int)1, (unsigned int)2); + __umul64hi((uint64_t)1, (uint64_t)2); + __umulhi((unsigned int)1, (unsigned int)2); + __urhadd((unsigned int)1, (unsigned int)2); + __usad((unsigned int)1, (unsigned int)2, 0); + + assert(1); +} + +__global__ void compileIntegerIntrinsics(int) { + integer_intrinsics(); +} + +TEST_CASE("Unit_IntegerIntrinsics") { + hipLaunchKernelGGL(compileIntegerIntrinsics, dim3(1, 1, 1), + dim3(1, 1, 1), 0, 0, 1); +} diff --git a/catch/unit/deviceLib/SimpleAtomicsTest.cc b/catch/unit/deviceLib/SimpleAtomicsTest.cc new file mode 100644 index 0000000000..db481e1e22 --- /dev/null +++ b/catch/unit/deviceLib/SimpleAtomicsTest.cc @@ -0,0 +1,298 @@ +/* +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 +#include +#include + +#include +#include + +using namespace std; +//////////////////////////////////////////////////////////////////////////////// +// Auto-Verification Code +//////////////////////////////////////////////////////////////////////////////// + +bool verifyBitwise(...) { + return true; +} + +template{}>::type* = nullptr> +bool verifyBitwise(T* gpuData, int len) { + // Atomic and + T val = 0xff; + for (int i = 0; i < len; ++i) { + // 9th element should be 1 + val &= (2 * i + 7); + } + REQUIRE(val == gpuData[8]); + + // atomic Or + val = 0; + for (int i = 0; i < len; ++i) { + // 10th element should be 0xff + val |= (1 << i); + } + REQUIRE(val == gpuData[9]); + + // atomic Xor + val = 0xff; + + for (int i = 0; i < len; ++i) { + // 11th element should be 0xff + val ^= i; + } + + REQUIRE(val == gpuData[10]); + return true; +} + +bool verifySub(...) { + return true; +} + +template< + typename T, + typename enable_if< + is_same{} || is_same{}>::type* = nullptr> +bool verifySub(T* gpuData, int len) { + T val = 0; + + for (int i = 0; i < len; ++i) { + val -= 10; + } + + REQUIRE(val == gpuData[1]); + return true; +} + +bool verifyExch(...) { + return true; +} + +template {}>::type* = nullptr> // NOLINT +bool computeExchExch(T* gpuData, int len) { + T val = 0; + + for (T i = 0; i < len; ++i) { + if (i == gpuData[2]) { + return true; + break; + } + } +} + +bool VerifyIntegral(...) { + return true; +} + +template{}>::type* = nullptr> +bool VerifyIntegral(T* gpuData, int len) { + // atomic Max + T val = 0; + for (T i = 0; i < len; ++i) { + // fourth element should be len-1 + val = max(val, i); + } + + REQUIRE(val == gpuData[3]); + + // atomic Min + val = 1 << 8; + + for (T i = 0; i < len; ++i) { + val = min(val, i); + } + + REQUIRE(val == gpuData[4]); + + // atomic Inc + int limit = 17; + val = 0; + + for (int i = 0; i < len; ++i) { + val = (val >= limit) ? 0 : val + 1; + } + + REQUIRE(val == gpuData[5]); + + // atomic Dec + limit = 137; + val = 0; + + for (int i = 0; i < len; ++i) { + val = ((val == 0) || (val > limit)) ? limit : val - 1; + } + + REQUIRE(val == gpuData[6]); + + // atomic CAS + for (T i = 0; i < len; ++i) { + // eighth element should be a member of [0, len) + if (i == gpuData[7]) { + return true; + break; + } + } + return verifyBitwise(gpuData, len) && verifySub(gpuData, len); +} + +template +bool verifyData(T* gpuData, int len) { + T val = 0; + for (int i = 0; i < len; ++i) { + val += 10; + } + + REQUIRE(val == gpuData[0]); + return VerifyIntegral(gpuData, len) && verifyExch(gpuData, len); +} + +__device__ +void testKernelExch(...) {} + +template{}>::type* = nullptr> +__device__ +void testKernelExch(T* g_odata) { + // access thread id + const T tid = blockDim.x * blockIdx.x + threadIdx.x; + + // Atomic exchange + atomicExch(&g_odata[2], tid); +} + +__device__ +void testKernelSub(...) {} + +template< + typename T, + typename enable_if< + is_same{} || is_same{}>::type* = nullptr> +__device__ +void testKernelSub(T* g_odata) { + // Atomic subtraction (final should be 0) + atomicSub(&g_odata[1], 10); +} + +__device__ +void testKernelIntegral(...) {} + +template{}>::type* = nullptr> +__device__ +void testKernelIntegral(T* g_odata) { + // access thread id + const T tid = blockDim.x * blockIdx.x + threadIdx.x; + + // Atomic maximum + atomicMax(&g_odata[3], tid); + + // Atomic minimum + atomicMin(&g_odata[4], tid); + + // Atomic increment (modulo 17+1) + atomicInc((unsigned int*)&g_odata[5], 17); + + // Atomic decrement + atomicDec((unsigned int*)&g_odata[6], 137); + + // Atomic compare-and-swap + atomicCAS(&g_odata[7], tid - 1, tid); + + // Bitwise atomic instructions + + // Atomic AND + atomicAnd(&g_odata[8], 2 * tid + 7); + + // Atomic OR + atomicOr(&g_odata[9], 1 << tid); + + // Atomic XOR + atomicXor(&g_odata[10], tid); + + testKernelSub(g_odata); +} + +template +__global__ void testKernel(T* g_odata) { + // Atomic addition + atomicAdd(&g_odata[0], 10); + testKernelIntegral(g_odata); + testKernelExch(g_odata); +} + +template +static void runTest() { + bool testResult = true; + unsigned int numThreads = 256; + unsigned int numBlocks = 64; + unsigned int numData = 11; + unsigned int memSize = sizeof(T) * numData; + + // allocate mem for the result on host side + T* hOData = reinterpret_cast(malloc(memSize)); + + // initialize the memory + for (unsigned int i = 0; i < numData; i++) { + hOData[i] = 0; + } + // To make the AND and XOR tests generate something other than 0... + hOData[8] = hOData[10] = 0xff; + + // allocate device memory for result + T* dOData; + HIP_CHECK(hipMalloc(reinterpret_cast(&dOData), memSize)); + // copy host memory to device to initialize to zero + HIP_CHECK(hipMemcpy(dOData, hOData, memSize, hipMemcpyHostToDevice)); + + // execute the kernel + hipLaunchKernelGGL( + testKernel, dim3(numBlocks), dim3(numThreads), 0, 0, dOData); + + // Copy result from device to host + HIP_CHECK(hipMemcpy(hOData, dOData, memSize, hipMemcpyDeviceToHost)); + + // Compute reference solution + REQUIRE(testResult == verifyData(hOData, numThreads * numBlocks)); + + // Cleanup memory + free(hOData); + HIP_CHECK(hipFree(dOData)); +} + +TEST_CASE("Unit_SimpleAtomicsTest") { + SECTION("test for int") { + runTest(); + } + SECTION("test for unsigned int") { + runTest(); + } + SECTION("test for float") { + runTest(); + } + #if HT_AMD + SECTION("test for unsigned long long") { + runTest(); + } + SECTION("test for double") { + runTest(); + } + #endif +} diff --git a/catch/unit/deviceLib/SinglePrecisionIntrinsics.cc b/catch/unit/deviceLib/SinglePrecisionIntrinsics.cc new file mode 100644 index 0000000000..fb8bebdaa5 --- /dev/null +++ b/catch/unit/deviceLib/SinglePrecisionIntrinsics.cc @@ -0,0 +1,101 @@ +/* +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 +#include + +#pragma GCC diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wunused-variable" + +__device__ void single_precision_intrinsics() { + float fX, fY; + + __cosf(0.0f); + __exp10f(0.0f); + __expf(0.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fadd_rd(0.0f, 1.0f); +#endif + __fadd_rn(0.0f, 1.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fadd_ru(0.0f, 1.0f); + __fadd_rz(0.0f, 1.0f); + __fdiv_rd(4.0f, 2.0f); +#endif + __fdiv_rn(4.0f, 2.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fdiv_ru(4.0f, 2.0f); + __fdiv_rz(4.0f, 2.0f); +#endif + __fdividef(4.0f, 2.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fmaf_rd(1.0f, 2.0f, 3.0f); +#endif + __fmaf_rn(1.0f, 2.0f, 3.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fmaf_ru(1.0f, 2.0f, 3.0f); + __fmaf_rz(1.0f, 2.0f, 3.0f); + __fmul_rd(1.0f, 2.0f); +#endif + __fmul_rn(1.0f, 2.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fmul_ru(1.0f, 2.0f); + __fmul_rz(1.0f, 2.0f); + __frcp_rd(2.0f); +#endif + __frcp_rn(2.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __frcp_ru(2.0f); + __frcp_rz(2.0f); +#endif + __frsqrt_rn(4.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fsqrt_rd(4.0f); +#endif + __fsqrt_rn(4.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fsqrt_ru(4.0f); + __fsqrt_rz(4.0f); + __fsub_rd(2.0f, 1.0f); +#endif + __fsub_rn(2.0f, 1.0f); +#if defined OCML_BASIC_ROUNDED_OPERATIONS + __fsub_ru(2.0f, 1.0f); + __fsub_rz(2.0f, 1.0f); +#endif + __log10f(1.0f); + __log2f(1.0f); + __logf(1.0f); + __powf(1.0f, 0.0f); + __saturatef(0.1f); + __sincosf(0.0f, &fX, &fY); + __sinf(0.0f); + __tanf(0.0f); +} + +__global__ void compileSinglePrecisionIntrinsics(int) { + single_precision_intrinsics(); +} + +TEST_CASE("Unit_SinglePrecisionIntrinsics") { + hipLaunchKernelGGL(compileSinglePrecisionIntrinsics, dim3(1, 1, 1), + dim3(1, 1, 1), 0, 0, 1); +} diff --git a/catch/unit/deviceLib/SinglePrecisionMathDevice.cc b/catch/unit/deviceLib/SinglePrecisionMathDevice.cc new file mode 100644 index 0000000000..e7bbdc180e --- /dev/null +++ b/catch/unit/deviceLib/SinglePrecisionMathDevice.cc @@ -0,0 +1,123 @@ +/* +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 +#include + +#pragma GCC diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wunused-variable" + +__device__ void single_precision_math_functions() { + int iX; + float fX, fY; + + acosf(1.0f); + acoshf(1.0f); + asinf(0.0f); + asinhf(0.0f); + atan2f(0.0f, 1.0f); + atanf(0.0f); + atanhf(0.0f); + cbrtf(0.0f); + ceilf(0.0f); + copysignf(1.0f, -2.0f); + cosf(0.0f); + coshf(0.0f); + cospif(0.0f); + erfcf(0.0f); + erfcinvf(2.0f); + erfcxf(0.0f); + erff(0.0f); + erfinvf(1.0f); + exp10f(0.0f); + exp2f(0.0f); + expf(0.0f); + expm1f(0.0f); + fabsf(1.0f); + fdimf(1.0f, 0.0f); + fdividef(0.0f, 1.0f); + floorf(0.0f); + fmaf(1.0f, 2.0f, 3.0f); + fmaxf(0.0f, 0.0f); + fminf(0.0f, 0.0f); + fmodf(0.0f, 1.0f); + frexpf(0.0f, &iX); + hypotf(1.0f, 0.0f); + ilogbf(1.0f); + isfinite(0.0f); + isinf(0.0f); + isnan(0.0f); + j0f(0.0f); + j1f(0.0f); + jnf(-1.0f, 1.0f); + ldexpf(0.0f, 0); + llrintf(0.0f); + llroundf(0.0f); + log10f(1.0f); + log1pf(-1.0f); + log2f(1.0f); + logbf(1.0f); + logf(1.0f); + lrintf(0.0f); + lroundf(0.0f); + nanf("1"); + nearbyintf(0.0f); + norm3df(1.0f, 0.0f, 0.0f); + norm4df(1.0f, 0.0f, 0.0f, 0.0f); + normcdff(0.0f); + normcdfinvf(1.0f); + fX = 1.0f; + normf(1, &fX); + powf(1.0f, 0.0f); + remainderf(2.0f, 1.0f); + rhypotf(0.0f, 1.0f); + rintf(1.0f); + rnorm3df(0.0f, 0.0f, 1.0f); + rnorm4df(0.0f, 0.0f, 0.0f, 1.0f); + fX = 1.0f; + rnormf(1, &fX); + roundf(0.0f); + rsqrtf(1.0f); + signbit(1.0f); + sincosf(0.0f, &fX, &fY); + sincospif(0.0f, &fX, &fY); + sinf(0.0f); + sinhf(0.0f); + sinpif(0.0f); + sqrtf(0.0f); + tanf(0.0f); + tanhf(0.0f); + tgammaf(2.0f); + truncf(0.0f); + y0f(1.0f); + y1f(1.0f); + ynf(1, 1.0f); +} + +__global__ void compileSinglePrecisionMathOnDevice(int) { + single_precision_math_functions(); +} + +TEST_CASE("Unit_SinglePrecisionMathDevice") { + hipLaunchKernelGGL(compileSinglePrecisionMathOnDevice, dim3(1, 1, 1), + dim3(1, 1, 1), 0, 0, 1); +} diff --git a/catch/unit/deviceLib/SinglePrecisionMathHost.cc b/catch/unit/deviceLib/SinglePrecisionMathHost.cc new file mode 100644 index 0000000000..85407560cb --- /dev/null +++ b/catch/unit/deviceLib/SinglePrecisionMathHost.cc @@ -0,0 +1,113 @@ +/* +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 + +#pragma GCC diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wunused-variable" + +__host__ static void single_precision_math_functions() { + int iX; + float fX, fY; + + acosf(1.0f); + acoshf(1.0f); + asinf(0.0f); + asinhf(0.0f); + atan2f(0.0f, 1.0f); + atanf(0.0f); + atanhf(0.0f); + cbrtf(0.0f); + ceilf(0.0f); + copysignf(1.0f, -2.0f); + cosf(0.0f); + coshf(0.0f); + erfcf(0.0f); + erff(0.0f); + #ifdef __unix__ + exp10f(0.0f); + #endif + exp2f(0.0f); + expf(0.0f); + expm1f(0.0f); + fabsf(1.0f); + fdimf(1.0f, 0.0f); + floorf(0.0f); + fmaf(1.0f, 2.0f, 3.0f); + fmaxf(0.0f, 0.0f); + fminf(0.0f, 0.0f); + fmodf(0.0f, 1.0f); + frexpf(0.0f, &iX); + hypotf(1.0f, 0.0f); + ilogbf(1.0f); + std::isfinite(0.0f); + std::isinf(0.0f); + std::isnan(0.0f); + #ifdef __unix__ + j0f(0.0f); + j1f(0.0f); + jnf(-1.0f, 1.0f); + #endif + ldexpf(0.0f, 0); + lgammaf(1.0f); + llrintf(0.0f); + llroundf(0.0f); + log10f(1.0f); + log1pf(-1.0f); + log2f(1.0f); + logbf(1.0f); + logf(1.0f); + lrintf(0.0f); + lroundf(0.0f); + modff(0.0f, &fX); + nanf("1"); + nearbyintf(0.0f); + powf(1.0f, 0.0f); + remainderf(2.0f, 1.0f); + remquof(1.0f, 2.0f, &iX); + rintf(1.0f); +#if HT_AMD + fX = 1.0f; +#endif + roundf(0.0f); + /// rsqrtf(1.0f); + scalblnf(0.0f, 1); + scalbnf(0.0f, 1); + std::signbit(1.0f); + #ifdef __unix__ + sincosf(0.0f, &fX, &fY); + #endif + sinf(0.0f); + sinhf(0.0f); + sqrtf(0.0f); + tanf(0.0f); + tanhf(0.0f); + tgammaf(2.0f); + truncf(0.0f); + #ifdef __unix__ + y0f(1.0f); + y1f(1.0f); + ynf(1, 1.0f); + #endif +} + +TEST_CASE("Unit_SinglePrecisionMathHost") { + single_precision_math_functions(); +} diff --git a/catch/unit/deviceLib/hipStdComplex.cc b/catch/unit/deviceLib/hipStdComplex.cc new file mode 100644 index 0000000000..8cde8de8ed --- /dev/null +++ b/catch/unit/deviceLib/hipStdComplex.cc @@ -0,0 +1,150 @@ +/* +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 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 +#include +#include + +// Tolerance for error +const double tolerance = 1e-6; +const bool verbose = false; + +#define LEN 64 + +#define ALL_FUN \ + OP(add) \ + OP(sub) \ + OP(mul) \ + OP(div) \ + OP(abs) \ + OP(arg) \ + OP(sin) \ + OP(cos) + +#define OP(x) CK_##x, +enum CalcKind { + ALL_FUN +}; +#undef OP + +#define OP(x) case CK_##x: return #x; +std::string getName(enum CalcKind CK) { + switch (CK) { + ALL_FUN + } + return ""; // To prevent compile warning +} +#undef OP + +// Calculates function. +// If the function has one argument, B is ignored. +// If the function returns real number, converts it to a complex number. +#define ONE_ARG(func) \ + case CK_##func: \ + return std::complex(func(A)); + +template +__device__ __host__ std::complex calc(std::complex A, + std::complex B, + enum CalcKind CK) { + switch (CK) { + case CK_add: + return A + B; + case CK_sub: + return A - B; + case CK_mul: + return A * B; + case CK_div: + return A / B; + + ONE_ARG(abs) + ONE_ARG(arg) + ONE_ARG(sin) + ONE_ARG(cos) + } + return A; // To prevent compile warning +} + +template +__global__ void kernel(std::complex* A, + std::complex* B, std::complex* C, + enum CalcKind CK) { + int tx = threadIdx.x + blockIdx.x * blockDim.x; + C[tx] = calc(A[tx], B[tx], CK); +} + +template +void test() { + typedef std::complex ComplexT; + + ComplexT *A, *Ad, *B, *Bd, *C, *Cd, *D; + A = new ComplexT[LEN]; + B = new ComplexT[LEN]; + C = new ComplexT[LEN]; + D = new ComplexT[LEN]; + HIP_CHECK(hipMalloc(reinterpret_cast(&Ad), sizeof(ComplexT)*LEN)); + HIP_CHECK(hipMalloc(reinterpret_cast(&Bd), sizeof(ComplexT)*LEN)); + HIP_CHECK(hipMalloc(reinterpret_cast(&Cd), sizeof(ComplexT)*LEN)); + + for (uint32_t i = 0; i < LEN; i++) { + A[i] = ComplexT((i + 1) * 1.0f, (i + 2) * 1.0f); + B[i] = A[i]; + C[i] = A[i]; + } + HIP_CHECK(hipMemcpy(Ad, A, sizeof(ComplexT)*LEN, hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(Bd, B, sizeof(ComplexT)*LEN, hipMemcpyHostToDevice)); + + // Run kernel for a calculation kind and verify by comparing with host + // calculation result. Returns false if fails. + auto test_fun = [&](enum CalcKind CK) { + hipLaunchKernelGGL(kernel, dim3(1), dim3(LEN), 0, 0, + Ad, Bd, Cd, CK); + HIP_CHECK(hipMemcpy(C, Cd, sizeof(ComplexT)*LEN, hipMemcpyDeviceToHost)); + for (int i = 0; i < LEN; i++) { + ComplexT Expected = calc(A[i], B[i], CK); + FloatT error = abs(C[i] - Expected); + if (abs(Expected) > tolerance) + error /= abs(Expected); + bool pass = error < tolerance; + } + return true; + }; + +#define OP(x) assert(test_fun(CK_##x)); + ALL_FUN +#undef OP + + HIP_CHECK(hipFree(Ad)); + HIP_CHECK(hipFree(Bd)); + HIP_CHECK(hipFree(Cd)); + delete[] A; + delete[] B; + delete[] C; + delete[] D; +} + +TEST_CASE("Unit_StdComplex") { + SECTION("Test run with float") { + test(); + } + SECTION("Test run with double") { + test(); + } +} diff --git a/catch/unit/deviceLib/hipTestAtomicAdd.cc b/catch/unit/deviceLib/hipTestAtomicAdd.cc new file mode 100644 index 0000000000..4834965942 --- /dev/null +++ b/catch/unit/deviceLib/hipTestAtomicAdd.cc @@ -0,0 +1,222 @@ +/* +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 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. +*/ + +/** +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) { + size_t tid = (blockIdx.x * blockDim.x + threadIdx.x); + atomicAddNoRet(C_d, INCREMENT_VALUE); +} + +template +__global__ void atomic_manywaves(T* C_d) { + size_t tid = (hipBlockIdx_x * hipBlockDim_x + hipThreadIdx_x); + 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+ + (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; +} + +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)); + } +} diff --git a/catch/unit/deviceLib/hipTestClock.cc b/catch/unit/deviceLib/hipTestClock.cc new file mode 100644 index 0000000000..26dd29c76c --- /dev/null +++ b/catch/unit/deviceLib/hipTestClock.cc @@ -0,0 +1,51 @@ +/* +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 LEN 512 +#define SIZE (LEN * sizeof(int64_t)) + +static __global__ void kernel1(int64_t* Ad) { + int tid = threadIdx.x + blockIdx.x * blockDim.x; + Ad[tid] = clock() + clock64() + __clock() + __clock64(); +} + +static __global__ void kernel2(int64_t* Ad) { + int tid = threadIdx.x + blockIdx.x * blockDim.x; + Ad[tid] = clock() + clock64() + __clock() + __clock64() - Ad[tid]; +} + +TEST_CASE("Unit_hipTestClock") { + int64_t *A, *Ad; + A = new int64_t[LEN]; + for (unsigned i = 0; i < LEN; i++) { + A[i] = 0; + } + HIP_CHECK(hipMalloc(reinterpret_cast(&Ad), SIZE)); + HIP_CHECK(hipMemcpy(Ad, A, SIZE, hipMemcpyHostToDevice)); + hipLaunchKernelGGL(kernel1, dim3(1, 1, 1), dim3(LEN, 1, 1), 0, 0, Ad); + hipLaunchKernelGGL(kernel2, dim3(1, 1, 1), dim3(LEN, 1, 1), 0, 0, Ad); + HIP_CHECK(hipMemcpy(A, Ad, SIZE, hipMemcpyDeviceToHost)); + for (unsigned i = 0; i < LEN; i++) { + assert(0 != A[i]); + } +} From e4ef99f52a9bca5ef7217f60d9580ddd750a856f Mon Sep 17 00:00:00 2001 From: ROCm CI Service Account <66695075+rocm-ci@users.noreply.github.com> Date: Mon, 6 Mar 2023 19:34:49 +0530 Subject: [PATCH 10/13] SWDEV-364620 - fix Unit_hipGraphAddHostNode_ClonedGraphwithHostNode by adding dependencies for cloned graph correctly (#186) Change-Id: I87bbf2536ccf5aa896478bfdca0d7d76fd5b4a74 --- catch/unit/graph/hipGraphAddHostNode.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/catch/unit/graph/hipGraphAddHostNode.cc b/catch/unit/graph/hipGraphAddHostNode.cc index f66e813352..9da6e3b223 100644 --- a/catch/unit/graph/hipGraphAddHostNode.cc +++ b/catch/unit/graph/hipGraphAddHostNode.cc @@ -136,6 +136,8 @@ TEST_CASE("Unit_hipGraphAddHostNode_ClonedGraphwithHostNode") { HIP_CHECK(hipGraphCreate(&graph, 0)); hipGraphNode_t memcpyH2D_A, memcpyH2D_C, memcpyD2H_AC; + hipGraphNode_t cloned_memcpyH2D_A, cloned_memcpyH2D_C, + cloned_memcpyD2H_AC; hipStream_t streamForGraph; HIP_CHECK(hipStreamCreate(&streamForGraph)); HIP_CHECK(hipGraphAddMemcpyNode1D(&memcpyH2D_A, graph, nullptr, @@ -151,6 +153,13 @@ TEST_CASE("Unit_hipGraphAddHostNode_ClonedGraphwithHostNode") { hipGraph_t clonedgraph; HIP_CHECK(hipGraphClone(&clonedgraph, graph)); + HIP_CHECK(hipGraphNodeFindInClone(&cloned_memcpyH2D_A, memcpyH2D_A, + clonedgraph)); + HIP_CHECK(hipGraphNodeFindInClone(&cloned_memcpyH2D_C, memcpyH2D_C, + clonedgraph)); + HIP_CHECK(hipGraphNodeFindInClone(&cloned_memcpyD2H_AC, memcpyD2H_AC, + clonedgraph)); + hipGraphNode_t hostNode; hipHostNodeParams hostParams = {0, 0}; hostParams.fn = callbackfunc; @@ -159,10 +168,12 @@ TEST_CASE("Unit_hipGraphAddHostNode_ClonedGraphwithHostNode") { nullptr, 0, &hostParams)); - HIP_CHECK(hipGraphAddDependencies(graph, &memcpyH2D_A, - &memcpyD2H_AC, 1)); - HIP_CHECK(hipGraphAddDependencies(graph, &memcpyH2D_C, - &memcpyD2H_AC, 1)); + HIP_CHECK(hipGraphAddDependencies(clonedgraph, &cloned_memcpyH2D_A, + &cloned_memcpyD2H_AC, 1)); + HIP_CHECK(hipGraphAddDependencies(clonedgraph, &cloned_memcpyH2D_C, + &cloned_memcpyD2H_AC, 1)); + HIP_CHECK(hipGraphAddDependencies(clonedgraph, &cloned_memcpyD2H_AC, + &hostNode, 1)); // Instantiate and launch the cloned graph HIP_CHECK(hipGraphInstantiate(&graphExec, clonedgraph, nullptr, nullptr, 0)); From c678dde410f77de0a6d9bfd0f31e72fe3fba0a41 Mon Sep 17 00:00:00 2001 From: ROCm CI Service Account <66695075+rocm-ci@users.noreply.github.com> Date: Mon, 6 Mar 2023 19:35:29 +0530 Subject: [PATCH 11/13] SWDEV-380340 - [catch2][dtest] Context tests migrated from direct to catch2 (#187) Change-Id: I6cf69089343cfcddfa9d0ac1a8e2c73381ff70e8 --- catch/unit/CMakeLists.txt | 3 +- catch/unit/context/CMakeLists.txt | 28 ++++++++++++ catch/unit/context/hipDrvGetPCIBusId.cc | 29 ++++++++++++ catch/unit/context/hipDrvMemcpy.cc | 60 +++++++++++++++++++++++++ catch/unit/context/hipMemsetD8.cc | 42 +++++++++++++++++ 5 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 catch/unit/context/CMakeLists.txt create mode 100644 catch/unit/context/hipDrvGetPCIBusId.cc create mode 100644 catch/unit/context/hipDrvMemcpy.cc create mode 100644 catch/unit/context/hipMemsetD8.cc diff --git a/catch/unit/CMakeLists.txt b/catch/unit/CMakeLists.txt index 63bbeda241..31bd37da9b 100644 --- a/catch/unit/CMakeLists.txt +++ b/catch/unit/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 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 @@ -34,6 +34,7 @@ add_subdirectory(multiThread) add_subdirectory(compiler) add_subdirectory(errorHandling) add_subdirectory(cooperativeGrps) +add_subdirectory(context) if(HIP_PLATFORM STREQUAL "amd") add_subdirectory(callback) #add_subdirectory(clock) diff --git a/catch/unit/context/CMakeLists.txt b/catch/unit/context/CMakeLists.txt new file mode 100644 index 0000000000..0d20df1bfa --- /dev/null +++ b/catch/unit/context/CMakeLists.txt @@ -0,0 +1,28 @@ +# 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 + hipDrvGetPCIBusId.cc + hipDrvMemcpy.cc + hipMemsetD8.cc +) +hip_add_exe_to_target(NAME Context + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests) diff --git a/catch/unit/context/hipDrvGetPCIBusId.cc b/catch/unit/context/hipDrvGetPCIBusId.cc new file mode 100644 index 0000000000..7be51441c6 --- /dev/null +++ b/catch/unit/context/hipDrvGetPCIBusId.cc @@ -0,0 +1,29 @@ +/*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 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 + +TEST_CASE("Unit_hipDeviceGetPCIBusId_Functional") { + HIP_CHECK(hipInit(0)); + hipDevice_t device; + HIP_CHECK(hipDeviceGet(&device, 0)); + char pciBusId[13]; + memset(pciBusId, 0, 13); + HIP_CHECK(hipDeviceGetPCIBusId(pciBusId, 13, device)); + REQUIRE(pciBusId[0] != '\0'); +} + diff --git a/catch/unit/context/hipDrvMemcpy.cc b/catch/unit/context/hipDrvMemcpy.cc new file mode 100644 index 0000000000..9e0b1e8062 --- /dev/null +++ b/catch/unit/context/hipDrvMemcpy.cc @@ -0,0 +1,60 @@ +/* +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 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 +#define LEN 1024 +#define SIZE (LEN << 2) + +TEST_CASE("Unit_hipDrvMemcpy_Functional") { + int *A, *B; + hipDeviceptr_t Ad, Bd; + A = new int[LEN]; + B = new int[LEN]; + + for (int i = 0; i < LEN; i++) { + A[i] = i; + } + + HIP_CHECK(hipMalloc(reinterpret_cast(&Ad), SIZE)); + HIP_CHECK(hipMalloc(reinterpret_cast(&Bd), SIZE)); + + HIP_CHECK(hipMemcpyHtoD(Ad, A, SIZE)); + HIP_CHECK(hipMemcpyDtoD(Bd, Ad, SIZE)); + HIP_CHECK(hipMemcpyDtoH(B, Bd, SIZE)); + + for (int i = 0; i < 16; i++) { + REQUIRE(A[i] == B[i]); + } + + int *Ah, *Bh; + HIP_CHECK(hipHostMalloc(&Ah, SIZE, 0)); + HIP_CHECK(hipHostMalloc(&Bh, SIZE, 0)); + memcpy(Ah, A, SIZE); + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + + HIP_CHECK(hipMemcpyHtoDAsync(Ad, Ah, SIZE, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipMemcpyDtoDAsync(Bd, Ad, SIZE, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipMemcpyDtoHAsync(Bh, Bd, SIZE, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + REQUIRE(Ah[10] == Bh[10]); + delete[] A; + delete[] B; +} diff --git a/catch/unit/context/hipMemsetD8.cc b/catch/unit/context/hipMemsetD8.cc new file mode 100644 index 0000000000..2b90099e0f --- /dev/null +++ b/catch/unit/context/hipMemsetD8.cc @@ -0,0 +1,42 @@ +/* +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 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 +#define N 1024 +constexpr char memsetval = 'b'; + +TEST_CASE("Unit_hipMemsetD8_Functional") { + size_t Nbytes = N * sizeof(char); + char* A_h = new char[Nbytes];; + + hipDeviceptr_t A_d; + HIP_CHECK(hipMalloc(reinterpret_cast(&A_d), Nbytes)); + + HIP_CHECK(hipMemsetD8(A_d, memsetval, Nbytes)); + + HIP_CHECK(hipMemcpy(A_h, reinterpret_cast(A_d), Nbytes, + hipMemcpyDeviceToHost)); + + for (int i = 0; i < N; i++) { + REQUIRE(A_h[i] == memsetval); + } + + HIP_CHECK(hipFree(reinterpret_cast(A_d))); + delete[] A_h; +} + From ac37f017e6e77fdf7fadc70d13c9c61129405985 Mon Sep 17 00:00:00 2001 From: Maneesh Gupta Date: Tue, 7 Mar 2023 09:50:36 +0530 Subject: [PATCH 12/13] Disable newly added failing tests --- catch/hipTestMain/config/config_amd_linux_common.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index d14b0971fe..0c61430a44 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -19,7 +19,7 @@ "Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Positive_Basic", "Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Positive_Basic", "Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Positive_Basic", - "Unit_hipKernelNameRef_Negative_Parameters", + "Unit_hipKernelNameRef_Negative_Parameters", "Unit_hipMemAdvise_AccessedBy_All_Devices", "Unit_hipMemAdvise_No_Flag_Interference", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep", @@ -40,8 +40,13 @@ "Note: Following four tests disabled due to defect - EXSWHTEC-203", "Unit_hipGraphAddMemsetNode_Positive_Basic - uint16_t", "Unit_hipGraphAddMemsetNode_Positive_Basic - uint32_t", + "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint8_t", "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint16_t", "Unit_hipGraphMemsetNodeSetParams_Positive_Basic - uint32_t", + "Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic - uint8_t", + "Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic - uint16_t", + "Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic - uint32_t", + "Unit_hipStreamSetCaptureDependencies_Positive_Functional", "Note: Test disabled due to defect - EXSWHTEC-207", "Unit_hipGraphExecMemsetNodeSetParams_Negative_Updating_Non1D_Node" ] From a25ea2ca4053c24185bde36130158ee4c289ec64 Mon Sep 17 00:00:00 2001 From: Maneesh Gupta Date: Wed, 8 Mar 2023 12:22:28 +0530 Subject: [PATCH 13/13] Update config_amd_linux_common.json --- catch/hipTestMain/config/config_amd_linux_common.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index 0c61430a44..7b26c484d0 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -48,6 +48,7 @@ "Unit_hipGraphExecMemsetNodeSetParams_Positive_Basic - uint32_t", "Unit_hipStreamSetCaptureDependencies_Positive_Functional", "Note: Test disabled due to defect - EXSWHTEC-207", - "Unit_hipGraphExecMemsetNodeSetParams_Negative_Updating_Non1D_Node" + "Unit_hipGraphExecMemsetNodeSetParams_Negative_Updating_Non1D_Node", + "Unit_hipIpcGetMemHandle_Positive_Unique_Handles_Separate_Allocations" ] }