Merge 'develop' into 'amd-staging'

Change-Id: If1de40873add9a0e69513781c1d47e82a3fb2a5d
Αυτή η υποβολή περιλαμβάνεται σε:
Jenkins
2023-03-09 00:11:03 +00:00
γονέας 38ce64e60d a25ea2ca40
υποβολή 8ba8ba51e0
68 αρχεία άλλαξαν με 11424 προσθήκες και 4793 διαγραφές
@@ -15,6 +15,11 @@
"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",
"Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep",
@@ -31,6 +36,19 @@
"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 - 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",
"Unit_hipIpcGetMemHandle_Positive_Unique_Handles_Separate_Allocations"
]
}
@@ -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",
@@ -95,6 +95,12 @@
"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",
"Unit_hipMemAdvise_No_Flag_Interference",
"Unit_hipGraphAddEventRecordNode_Functional_WithoutFlags",
@@ -108,6 +114,18 @@
"Unit_hipStreamValue_Wait64_Blocking_NoMask_Nor",
"Unit_hipStreamQuery_WithFinishedWork",
"Unit_hipLaunchHostFunc_Graph",
"Unit_hipLaunchHostFunc_KernelHost"
"Unit_hipLaunchHostFunc_KernelHost",
"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"
]
}
@@ -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);
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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,7 +34,9 @@ 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)
# Vulkan interop APIs currently undefined for Nvidia
add_subdirectory(vulkan_interop)
@@ -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()
@@ -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!");
}
@@ -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 <hip_test_common.hh>
/**
* @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<std::string> 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<uint32_t>::min()), Catch::Equals(kUnknownApi));
REQUIRE_THAT(hipApiName(std::numeric_limits<uint32_t>::max()), Catch::Equals(kUnknownApi));
}
@@ -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 <resource_guards.hh>
/**
* @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<std::thread> 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);
}
@@ -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 <hip_test_common.hh>
/**
* @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);
}
@@ -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 <resource_guards.hh>
/**
* @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<const void*>(&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<const void*>(&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);
}
}
@@ -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)
@@ -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 <hip_test_common.hh>
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');
}
@@ -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 <hip_test_common.hh>
#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<void**>(&Ad), SIZE));
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&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;
}
@@ -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 <hip_test_common.hh>
#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<void**>(&A_d), Nbytes));
HIP_CHECK(hipMemsetD8(A_d, memsetval, Nbytes));
HIP_CHECK(hipMemcpy(A_h, reinterpret_cast<void*>(A_d), Nbytes,
hipMemcpyDeviceToHost));
for (int i = 0; i < N; i++) {
REQUIRE(A_h[i] == memsetval);
}
HIP_CHECK(hipFree(reinterpret_cast<void*>(A_d)));
delete[] A_h;
}
@@ -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)
@@ -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 <hip_test_kernels.hh>
#include <hip_test_checkers.hh>
#include <hip_test_common.hh>
#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);
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_checkers.hh>
#include <hip_test_common.hh>
#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);
}
@@ -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 <hip_test_common.hh>
#include <cmath>
#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();
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_checkers.hh>
#include <hip_test_common.hh>
#include <hip/math_functions.h>
__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);
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_checkers.hh>
#include <hip_test_common.hh>
#include <hip/device_functions.h>
#include <algorithm>
#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<int>(10));
__clzll((int64_t)10);
__ffs(static_cast<int>(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<int>(1), static_cast<int>(3));
__mul24(static_cast<int>(1), static_cast<int>(2));
__mul64hi((int64_t)1, (int64_t)2);
__mulhi(static_cast<int>(1), static_cast<int>(2));
__popc((unsigned int)4);
__popcll((uint64_t)4);
int a = min(static_cast<int>(4), static_cast<int>(5));
int b = max(static_cast<int>(4), static_cast<int>(5));
__rhadd(static_cast<int>(1), static_cast<int>(2));
__sad(static_cast<int>(1), static_cast<int>(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);
}
@@ -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 <string.h>
#include <math.h>
#include <hip_test_kernels.hh>
#include <hip_test_checkers.hh>
#include <hip_test_common.hh>
#include <algorithm>
#include <type_traits>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// Auto-Verification Code
////////////////////////////////////////////////////////////////////////////////
bool verifyBitwise(...) {
return true;
}
template<typename T, typename enable_if<is_integral<T>{}>::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<T, int>{} || is_same<T, unsigned int>{}>::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<typename T, typename enable_if<!is_same<T, double> {}>::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<typename T, typename enable_if<is_integral<T>{}>::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<typename T>
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<typename T, typename enable_if<!is_same<T, double>{}>::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<T, int>{} || is_same<T, unsigned int>{}>::type* = nullptr>
__device__
void testKernelSub(T* g_odata) {
// Atomic subtraction (final should be 0)
atomicSub(&g_odata[1], 10);
}
__device__
void testKernelIntegral(...) {}
template<typename T, typename enable_if<is_integral<T>{}>::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<typename T>
__global__ void testKernel(T* g_odata) {
// Atomic addition
atomicAdd(&g_odata[0], 10);
testKernelIntegral(g_odata);
testKernelExch(g_odata);
}
template<typename T>
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<T*>(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<void**>(&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<int>();
}
SECTION("test for unsigned int") {
runTest<unsigned int>();
}
SECTION("test for float") {
runTest<float>();
}
#if HT_AMD
SECTION("test for unsigned long long") {
runTest<uint64_t>();
}
SECTION("test for double") {
runTest<double>();
}
#endif
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_checkers.hh>
#include <hip_test_common.hh>
#include <hip/device_functions.h>
#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);
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_checkers.hh>
#include <hip_test_common.hh>
#include <hip/math_functions.h>
#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);
}
@@ -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 <hip_test_common.hh>
#include <cmath>
#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();
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <complex>
// 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<FloatT>(func(A));
template<typename FloatT>
__device__ __host__ std::complex<FloatT> calc(std::complex<FloatT> A,
std::complex<FloatT> 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<typename FloatT>
__global__ void kernel(std::complex<FloatT>* A,
std::complex<FloatT>* B, std::complex<FloatT>* C,
enum CalcKind CK) {
int tx = threadIdx.x + blockIdx.x * blockDim.x;
C[tx] = calc<FloatT>(A[tx], B[tx], CK);
}
template<typename FloatT>
void test() {
typedef std::complex<FloatT> 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<void**>(&Ad), sizeof(ComplexT)*LEN));
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&Bd), sizeof(ComplexT)*LEN));
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&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<FloatT>, 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<float>();
}
SECTION("Test run with double") {
test<double>();
}
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
/*
* 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 <typename T>
__device__ void atomicAddNoRet(T* x, int y) {
atomicAdd(x, static_cast<T>(y));
}
#endif
bool p_atomicNoRet = false;
template <typename T>
__global__ void atomicnoret_manywaves(T* C_d) {
size_t tid = (blockIdx.x * blockDim.x + threadIdx.x);
atomicAddNoRet(C_d, INCREMENT_VALUE);
}
template <typename T>
__global__ void atomic_manywaves(T* C_d) {
size_t tid = (hipBlockIdx_x * hipBlockDim_x + hipThreadIdx_x);
atomicAdd(C_d, INCREMENT_VALUE);
}
template <typename T>
__global__ void atomicnoret_simple(T* C_d) {
atomicAddNoRet(C_d, INCREMENT_VALUE);
}
template <typename T>
__global__ void atomic_simple(T* C_d) {
atomicAdd(C_d, INCREMENT_VALUE);
}
template <typename T>
bool atomictest_manywaves(const T& initial_val) {
unsigned int ThreadsperBlock = 10;
unsigned int numBlocks = 1;
T memSize = sizeof(T);
T* hOData = reinterpret_cast<T*>(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 <typename T>
bool atomictestnoret_manywaves(const T& initial_val) {
unsigned int ThreadsperBlock = 10;
unsigned int numBlocks = 1;
T memSize = sizeof(T);
T* hOData = reinterpret_cast<T*>(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 <typename T>
bool atomictest_simple(const T& initial_val) {
unsigned int ThreadsperBlock = 1;
unsigned int numBlocks = 1;
T memSize = sizeof(T);
T* hOData = reinterpret_cast<T*>(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 <typename T>
bool atomictestnoret_simple(const T& initial_val) {
unsigned int ThreadsperBlock = 1;
unsigned int numBlocks = 1;
T memSize = sizeof(T);
T* hOData = reinterpret_cast<T*>(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>(INT_INITIAL_VALUE));
REQUIRE(TestPassed ==
atomictest_manywaves<unsigned int>(UNSIGNED_INITIAL_VALUE));
REQUIRE(TestPassed == atomictest_manywaves<float>(FLOAT_INITIAL_VALUE));
#if HT_AMD
REQUIRE(TestPassed ==
atomictest_manywaves<uint64_t>(LONG_INITIAL_VALUE));
REQUIRE(TestPassed ==
atomictest_manywaves<double>(DOUBLE_INITIAL_VALUE));
#endif
}
SECTION("atomic tests with many waves and no return") {
REQUIRE(TestPassed ==
atomictestnoret_manywaves<float>(FLOAT_INITIAL_VALUE));
}
SECTION("simple atomic tests") {
REQUIRE(TestPassed == atomictest_simple<int>(INT_INITIAL_VALUE));
REQUIRE(TestPassed ==
atomictest_simple<unsigned int>(UNSIGNED_INITIAL_VALUE));
REQUIRE(TestPassed == atomictest_simple<float>(FLOAT_INITIAL_VALUE));
#if HT_AMD
REQUIRE(TestPassed ==
atomictest_simple<uint64_t>(LONG_INITIAL_VALUE));
REQUIRE(TestPassed == atomictest_simple<double>(DOUBLE_INITIAL_VALUE));
#endif
}
SECTION("Simple atomic test with no return") {
REQUIRE(TestPassed == atomictestnoret_simple<float>(FLOAT_INITIAL_VALUE));
}
}
@@ -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 <hip_test_kernels.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#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<void**>(&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]);
}
}
@@ -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,15 +56,21 @@ set(TEST_SRC
hipGraphEventWaitNodeSetEvent.cc
hipGraphMemsetNodeGetParams.cc
hipGraphMemsetNodeSetParams.cc
hipGraphExecMemcpyNodeSetParamsFromSymbol_old.cc
hipGraphExecMemcpyNodeSetParamsFromSymbol.cc
hipGraphEventRecordNodeGetEvent.cc
hipGraphEventRecordNodeSetEvent.cc
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
hipGraphAddMemsetNode.cc
@@ -73,12 +82,19 @@ set(TEST_SRC
hipGraphExecKernelNodeSetParams.cc
hipGraphLaunch.cc
hipGraphMemcpyNodeSetParams1D.cc
hipGraphExecMemcpyNodeSetParamsToSymbol_old.cc
hipGraphExecMemcpyNodeSetParamsToSymbol.cc
hipGraphNodeGetDependentNodes.cc
hipGraphNodeGetDependencies.cc
hipGraphHostNodeGetParams.cc
hipGraphExecChildGraphNodeSetParams.cc
hipStreamGetCaptureInfo_v2.cc
hipGraphCreate.cc
hipGraphDestroy.cc
hipStreamUpdateCaptureDependencies.cc
hipThreadExchangeStreamCaptureMode.cc
hipLaunchHostFunc.cc
hipStreamGetCaptureInfo_v2_old.cc
hipUserObjectCreate.cc
hipGraphDebugDotPrint.cc
hipGraphCloneComplx.cc
@@ -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 <stddef.h>
#include <numeric>
#include <hip/hip_runtime_api.h>
#include <resource_guards.hh>
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 <typename T, typename F>
void MemcpyFromSymbolShell(F f, const void* symbol, size_t offset, const std::vector<T> expected) {
const auto alloc_type = GENERATE(LinearAllocs::hipMalloc, LinearAllocs::hipHostMalloc);
const auto size = expected.size() * sizeof(T);
LinearAllocGuard<T> 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<T> symbol_values(expected.size());
HIP_CHECK(hipMemcpy(symbol_values.data(), dst_alloc.ptr(), size, hipMemcpyDefault));
REQUIRE_THAT(expected, Catch::Equals(symbol_values));
}
template <typename T, typename F>
void MemcpyToSymbolShell(F f, const void* symbol, size_t offset, const std::vector<T> set_values) {
const auto alloc_type = GENERATE(LinearAllocs::hipMalloc, LinearAllocs::hipHostMalloc);
const auto size = set_values.size() * sizeof(T);
LinearAllocGuard<T> 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<T> 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 <typename F>
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<hipMemcpyKind>(-1)),
hipErrorInvalidMemcpyDirection);
}
#endif
}
template <typename F>
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<hipMemcpyKind>(-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<type>{init_val}); } \
\
SECTION("Constant scalar variable") { \
f(SYMBOL(type##_const_device_var), 0, std::vector<type>{init_val}); \
} \
\
SECTION("Array") { \
const auto offset = GENERATE(0, kArraySize / 2); \
INFO("Array offset: " << offset); \
std::vector<type> 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<type> 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<type>{init_val}); \
} \
\
SECTION("Constant scalar variable") { \
f(SYMBOL(type##_const_device_var), SYMBOL(type##_alt_const_device_var), 0, \
std::vector<type>{init_val}); \
} \
\
SECTION("Array") { \
const auto offset = GENERATE(0, kArraySize / 2); \
INFO("Array offset: " << offset); \
std::vector<type> 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<type> 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)); \
}
@@ -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 <hip/hip_runtime_api.h>
#include <resource_guards.hh>
#include <utils.hh>
template <typename T, typename F> 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<T> 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(&params));
LinearAllocGuard<T> 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 <typename F> 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(&params), hipErrorInvalidValue);
}
SECTION("pMemsetParams.elementSize != 1, 2, 4") {
params.elementSize = GENERATE(0, 3, 5);
HIP_CHECK_ERROR(f(&params), hipErrorInvalidValue);
}
// Disabled on AMD due to defect - EXSWHTEC-204
#if HT_NVIDIA
SECTION("pMemsetParams.width == 0") {
params.width = 0;
HIP_CHECK_ERROR(f(&params), hipErrorInvalidValue);
}
#endif
SECTION("pMemsetParams.width > allocation size") {
params.width = params.width + 1000;
HIP_CHECK_ERROR(f(&params), hipErrorInvalidValue);
}
SECTION("pMemsetParams.height == 0") {
params.height = 0;
HIP_CHECK_ERROR(f(&params), 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(&params), 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(&params), hipErrorInvalidValue);
}
#endif
}
@@ -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 <stddef.h>
#include <hip/hip_runtime_api.h>
#include <resource_guards.hh>
template <typename F> 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
}
@@ -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));
@@ -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 <functional>
#include <vector>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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 <typename T>
void GraphMemcpyFromSymbolShell(void* symbol, size_t offset, const std::vector<T> 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<int>(&A_d, &B_d, nullptr,
&A_h, &B_h, nullptr, SIZE, false);
hipGraph_t graph;
hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A;
std::vector<hipGraphNode_t> 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<int>::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<int>(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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(MemcpyFromSymbolKernel);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(A_d, B_d, nullptr,
A_h, B_h, nullptr, false);
HIP_CHECK(hipGraphExecDestroy(graphExec));
HIP_CHECK(hipGraphDestroy(graph));
}
@@ -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 <functional>
#include <limits>
#include <vector>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#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<int>(&A_d, &B_d, nullptr,
&A_h, &B_h, nullptr, SIZE, false);
hipGraph_t graph;
hipGraphNode_t memcpyToSymbolNode, memcpyH2D_A;
std::vector<hipGraphNode_t> 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<int>::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<int>(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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(MemcpyFromSymbolKernel);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(A_d, B_d, nullptr,
A_h, B_h, nullptr, false);
HIP_CHECK(hipGraphExecDestroy(graphExec));
HIP_CHECK(hipGraphDestroy(graph));
}
@@ -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 <functional>
#include <vector>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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 <typename T>
void GraphMemcpyToSymbolShell(const void* symbol, size_t offset, const std::vector<T> 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<int>(&A_d, nullptr, nullptr,
&A_h, &B_h, nullptr, SIZE, false);
hipGraph_t graph;
hipGraphNode_t memcpyH2D_A;
std::vector<hipGraphNode_t> 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<int>::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<int>(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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(MemcpyToSymbolKernel);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(A_d, B_d, nullptr,
A_h, B_h, nullptr, false);
HIP_CHECK(hipGraphExecDestroy(graphExec));
HIP_CHECK(hipGraphDestroy(graph));
}
@@ -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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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<int>(&A_d, nullptr, nullptr,
&A_h, &B_h, nullptr, SIZE, false);
hipGraph_t graph;
hipGraphNode_t memcpyH2D_A;
std::vector<hipGraphNode_t> 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<int>::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<int>(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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(MemcpyToSymbolKernel);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(A_d, B_d, nullptr,
A_h, B_h, nullptr, false);
HIP_CHECK(hipGraphExecDestroy(graphExec));
HIP_CHECK(hipGraphDestroy(graph));
}
@@ -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 <functional>
#include <vector>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
/**
* Negative Test for API hipGraphAddMemsetNode
*/
#include <resource_guards.hh>
#include <utils.hh>
#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<TestType>(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<void*>(devData);
memsetParams.value = 0;
memsetParams.pitch = 0;
memsetParams.elementSize = sizeof(char);
memsetParams.width = 1024;
memsetParams.height = 1;
LinearAllocGuard<int> 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, &params), 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<void*>(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));
}
/*
@@ -20,25 +20,53 @@ THE SOFTWARE.
#include <hip_test_common.hh>
/**
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));
}
@@ -20,62 +20,46 @@ THE SOFTWARE.
#include <hip_test_common.hh>
/**
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<void*>(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<hipGraph_t>(nullptr)), hipErrorInvalidValue);
}
@@ -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 <functional>
#include <vector>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#define SIZE 256
#include <resource_guards.hh>
__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<int>(&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<hipGraphNode_t> dependencies;
HIP_CHECK(hipGraphCreate(&graph, 0));
template <typename T>
void GraphExecMemcpyFromSymbolSetParamsShell(const void* symbol, const void* alt_symbol,
size_t offset, const std::vector<T> 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<T*>(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<int>(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<int>(&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<hipGraphNode_t> 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<int> 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<int> 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<hipMemcpyKind>(-1)),
hipErrorInvalidValue);
}
HipTest::freeArrays<int>(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);
}
}
@@ -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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<int>(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);
}
}
@@ -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 <functional>
#include <vector>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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 <typename T>
void GraphExecMemcpyToSymbolSetParamsShell(const void* symbol, const void* alt_symbol,
size_t offset, const std::vector<T> 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<T*>(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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(MemcpyToConstSymbolExecKernel);
} else {
kernelNodeParams.func = reinterpret_cast<void *>(MemcpyToSymbolExecKernel);
}
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(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<int> 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<int> 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<hipMemcpyKind>(-1)),
hipErrorInvalidValue);
}
HIP_CHECK(hipGraphExecDestroy(graph_exec));
HIP_CHECK(hipGraphDestroy(graph));
}
@@ -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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(MemcpyToConstSymbolExecKernel);
} else {
kernelNodeParams.func = reinterpret_cast<void *>(MemcpyToSymbolExecKernel);
}
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(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);
}
}
@@ -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 <functional>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
/* 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<char *>(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<void*>(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<TestType> initial_alloc(LinearAllocs::hipMalloc, 2 * sizeof(TestType));
std::vector<hipGraphNode_t> 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<void*>(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<TestType> 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, &params));
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<TestType> 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<char *>(malloc(Nbytes));
REQUIRE(hOutputData != nullptr);
memset(hOutputData, updateVal, Nbytes);
hOutputData1 = reinterpret_cast<char *>(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<void*>(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<int> 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<hipGraphNode_t> dependencies;
dependencies.push_back(memsetNode);
hipGraphNode_t node = nullptr;
HIP_CHECK(hipGraphAddMemsetNode(&node, graph, nullptr, 0, &params))
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<void*>(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, &params), 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, &params),
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<int> new_alloc(LinearAllocs::hipMalloc, 4 * sizeof(int));
params.dst = new_alloc.ptr();
HIP_CHECK_ERROR(hipGraphExecMemsetNodeSetParams(graph_exec, node, &params),
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<int> 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, &params))
hipGraphExec_t graph_exec = nullptr;
HIP_CHECK(hipGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0));
params.width = 2;
HIP_CHECK_ERROR(hipGraphExecMemsetNodeSetParams(graph_exec, node, &params), hipErrorInvalidValue);
HIP_CHECK(hipGraphExecDestroy(graph_exec));
HIP_CHECK(hipGraphDestroy(graph));
HIP_CHECK(hipStreamDestroy(streamForGraph));
}
@@ -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 <functional>
#include <vector>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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 <typename T>
void GraphMemcpyFromSymbolSetParamsShell(const void* symbol, const void* alt_symbol, size_t offset,
const std::vector<T> 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<T*>(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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<int>(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);
}
}
@@ -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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<int>(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);
}
}
@@ -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 <functional>
#include <vector>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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 <typename T>
void GraphMemcpyToSymbolSetParamsShell(const void* symbol, const void* alt_symbol, size_t offset,
const std::vector<T> 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<T*>(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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(CpyToConstSymbolKernel);
else
kernelNodeParams.func = reinterpret_cast<void *>(CpyToSymbolKernel);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(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));
}
@@ -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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <limits>
#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<int>(&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<hipGraphNode_t> 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<int>(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<int>(&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<hipGraphNode_t> 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<void *>(CpyToConstSymbolKernel);
else
kernelNodeParams.func = reinterpret_cast<void *>(CpyToSymbolKernel);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<int>(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);
}
}
@@ -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 <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <resource_guards.hh>
/* 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<void*>(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<int> 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<void*>(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, &params), 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<void*>(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, &params));
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, &params));
HIP_CHECK(hipGraphDestroy(graph));
HIP_CHECK_ERROR(hipGraphMemsetNodeGetParams(node, &params), hipErrorInvalidValue);
}
#endif
}
@@ -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 <functional>
#include <hip_test_defgroups.hh>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
/* 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<void*>(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<void*>(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<char>(&A_d, &B_d, &C_d,
&A_h, &B_h, nullptr, N, false);
hipGraphNode_t node = nullptr;
LinearAllocGuard<TestType> 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<void*>(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<hipGraphNode_t> 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<void*>(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<void*>(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<char>(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<TestType>(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<int> 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, &params))
SECTION("node == nullptr") {
HIP_CHECK_ERROR(hipGraphMemsetNodeSetParams(nullptr, &params), hipErrorInvalidValue);
}
MemsetCommonNegative(std::bind(hipGraphMemsetNodeSetParams, node, _1), params);
HIP_CHECK(hipGraphDestroy(graph));
}
@@ -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 <hip_test_common.hh>
#include <hip_test_kernels.hh>
#include <hip_test_defgroups.hh>
#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<float**>(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<float> A_h(LinearAllocs::malloc, sizeof(float));
LinearAllocGuard<float> B_h(LinearAllocs::malloc, sizeof(float));
LinearAllocGuard<float> 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<void*>(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<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(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<void*>(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<float> A_h(LinearAllocs::malloc, sizeof(float));
LinearAllocGuard<float> B_h(LinearAllocs::malloc, sizeof(float));
LinearAllocGuard<float> 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<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(i), 1);
}
HIP_CHECK(hipGraphExecDestroy(graphExec));
HIP_CHECK(hipGraphDestroy(graph));
}
Το diff αρχείου καταστέλλεται επειδή είναι πολύ μεγάλο Φόρτωση Διαφορών
Το diff αρχείου καταστέλλεται επειδή είναι πολύ μεγάλο Φόρτωση Διαφορών
@@ -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 <hip_test_common.hh>
#include <hip_test_kernels.hh>
#include <hip_test_defgroups.hh>
#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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> 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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> 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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
REQUIRE(A_h != nullptr);
REQUIRE(B_h != nullptr);
REQUIRE(C_h != nullptr);
LinearAllocGuard<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> 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<void**>(&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<int*>(malloc(Nbytes));
REQUIRE(A_h != nullptr);
// Initialize the Host data
*A_h = 0;
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&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<int*>(malloc(Nbytes));
B_h = reinterpret_cast<int*>(malloc(Nbytes));
C_h = reinterpret_cast<int*>(malloc(Nbytes));
D_h = reinterpret_cast<int*>(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<void**>(&A_d), Nbytes));
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&B_d), Nbytes));
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&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<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(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));
}
@@ -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 <hip_test_common.hh>
#include <hip_test_kernels.hh>
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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<void**>(&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<int*>(malloc(Nbytes));
REQUIRE(A_h != nullptr);
// Initialize the Host data
*A_h = 0;
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&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<int*>(malloc(Nbytes));
B_h = reinterpret_cast<int*>(malloc(Nbytes));
C_h = reinterpret_cast<int*>(malloc(Nbytes));
D_h = reinterpret_cast<int*>(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<void**>(&A_d), Nbytes));
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&B_d), Nbytes));
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&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));
}
@@ -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 doesnt crash and returns success.
2) Pass pCaptureStatus as nullptr and verify api doesnt 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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <hip_test_defgroups.hh>
#include <hip_test_kernels.hh>
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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> 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<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(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<int> 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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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
}
@@ -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 doesnt crash and returns success.
2) Pass pCaptureStatus as nullptr and verify api doesnt 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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <hip_test_kernels.hh>
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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<int> 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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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);
}
@@ -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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <hip_test_kernels.hh>
#include <hip_test_defgroups.hh>
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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> A_d(LinearAllocs::hipMalloc, Nbytes);
LinearAllocGuard<float> 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<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(i) * static_cast<float>(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<int> 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
}
@@ -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 <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <hip_test_kernels.hh>
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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<int> 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));
}
@@ -18,419 +18,195 @@ THE SOFTWARE.
*/
#include <hip_test_common.hh>
#include <hip_test_defgroups.hh>
#include <hip_test_kernels.hh>
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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> 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<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> 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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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);
}
}
@@ -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 <hip_test_common.hh>
#include <hip_test_kernels.hh>
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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
B_h = reinterpret_cast<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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<float*>(malloc(Nbytes));
C_h = reinterpret_cast<float*>(malloc(Nbytes));
D_h = reinterpret_cast<float*>(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);
}
@@ -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 <hip_test_checkers.hh>
#include <hip_test_common.hh>
#include <hip_test_kernels.hh>
#include <hip_test_defgroups.hh>
#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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> C_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> A_d(LinearAllocs::hipMalloc, Nbytes);
LinearAllocGuard<float> B_d(LinearAllocs::hipMalloc, Nbytes);
LinearAllocGuard<float> 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<void*>(&NElem)};
kernelNodeParams.func = reinterpret_cast<void*>(HipTest::vector_square<float>);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<float>(i));
std::fill_n(C_h.host_ptr(), N, static_cast<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(i) * static_cast<float>(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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> C_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> A_d(LinearAllocs::hipMalloc, Nbytes);
LinearAllocGuard<float> B_d(LinearAllocs::hipMalloc, Nbytes);
LinearAllocGuard<float> 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<void*>(&NElem)};
kernelNodeParams.func = reinterpret_cast<void*>(vectorSet);
kernelNodeParams.gridDim = dim3(blocks);
kernelNodeParams.blockDim = dim3(threadsPerBlock);
kernelNodeParams.sharedMemBytes = 0;
kernelNodeParams.kernelParams = reinterpret_cast<void**>(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<float>(i));
std::fill_n(C_h.host_ptr(), N, static_cast<float>(i));
HIP_CHECK(hipGraphLaunch(graphExec, stream));
HIP_CHECK(hipStreamSynchronize(stream));
ArrayFindIfNot(B_h.host_ptr(), static_cast<float>(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<hipGraphNode_t> dependencies;
LinearAllocGuard<char> 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<void*>(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));
}
@@ -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 <hip_test_checkers.hh>
#include <hip_test_common.hh>
#include <hip_test_kernels.hh>
#include <hip_test_defgroups.hh>
#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<float> A_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> B_h(LinearAllocs::malloc, Nbytes);
LinearAllocGuard<float> A_d(LinearAllocs::hipMalloc, Nbytes);
LinearAllocGuard<float> 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
@@ -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 <hip_test_common.hh>
#include <hip_test_kernels.hh>
#include <utils.hh>
#include <resource_guards.hh>
namespace {
inline constexpr size_t kLaunchIters = 10;
} // anonymous namespace
template <typename T>
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 <typename T>
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 <typename T>
void captureSequenceBranched(T* hostMem1, T* devMem1, T* hostMem2, T* devMem2, size_t N,
hipStream_t captureStream, std::vector<hipStream_t>& streams,
std::vector<hipEvent_t>& 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 <typename T>
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));
}