EXSWHTEC-192 - Implement new and update existing tests for the hipGraph*MemcpyNode[From|To]Symbol family of APIs (#13)

* EXSWHTEC-192 - Implement new and update existing tests for the hipGraph*MemcpyNode[From|To]Symbol family of APIs
- Generalize from symbol positive test to work for different types
- Implement negative parameter tests
- Extract common code for to/from copy
- Reorganize files

[ROCm/hip-tests commit: f1151e3c6d]
Αυτή η υποβολή περιλαμβάνεται σε:
music-dino
2023-03-06 04:30:35 +01:00
υποβλήθηκε από GitHub
γονέας e622569dc7
υποβολή c26ed2c1ea
17 αρχεία άλλαξαν με 3139 προσθήκες και 1802 διαγραφές
@@ -15,6 +15,10 @@
"Unit_hipMemset_Negative_OutOfBoundsPtr",
"Unit_hipDeviceReset_Positive_Basic",
"Unit_hipDeviceReset_Positive_Threaded",
"Unit_hipGraphMemcpyNodeSetParamsToSymbol_Positive_Basic",
"Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Positive_Basic",
"Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Positive_Basic",
"Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Positive_Basic",
"Unit_hipKernelNameRef_Negative_Parameters",
"Unit_hipMemAdvise_AccessedBy_All_Devices",
"Unit_hipMemAdvise_No_Flag_Interference",
@@ -95,6 +95,10 @@
"Unit_hipStreamSynchronize_NullStreamAndStreamPerThread",
"Note: intermittent Seg fault failure ",
"Unit_hipGraphAddEventRecordNode_Functional_WithoutFlags",
"Unit_hipGraphMemcpyNodeSetParamsToSymbol_Positive_Basic",
"Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Positive_Basic",
"Unit_hipGraphMemcpyNodeSetParamsFromSymbol_Positive_Basic",
"Unit_hipGraphExecMemcpyNodeSetParamsFromSymbol_Positive_Basic",
"Unit_hipKernelNameRef_Negative_Parameters",
"Unit_hipKernelNameRef_Positive_Basic",
"Unit_hipMemAdvise_AccessedBy_All_Devices",
@@ -30,12 +30,15 @@ set(TEST_SRC
hipGraphClone.cc
hipGraphInstantiateWithFlags.cc
hipGraphAddHostNode.cc
hipGraphAddMemcpyNodeFromSymbol_old.cc
hipGraphAddMemcpyNodeFromSymbol.cc
hipGraphChildGraphNodeGetGraph.cc
hipGraphNodeFindInClone.cc
hipGraphExecHostNodeSetParams.cc
hipGraphAddMemcpyNodeToSymbol_old.cc
hipGraphAddMemcpyNodeToSymbol.cc
hipGraphExecMemsetNodeSetParams.cc
hipGraphMemcpyNodeSetParamsToSymbol_old.cc
hipGraphMemcpyNodeSetParamsToSymbol.cc
hipGraphDestroyNode.cc
hipGraphGetNodes.cc
@@ -53,6 +56,7 @@ set(TEST_SRC
hipGraphEventWaitNodeSetEvent.cc
hipGraphMemsetNodeGetParams.cc
hipGraphMemsetNodeSetParams.cc
hipGraphExecMemcpyNodeSetParamsFromSymbol_old.cc
hipGraphExecMemcpyNodeSetParamsFromSymbol.cc
hipGraphEventRecordNodeGetEvent.cc
hipGraphEventRecordNodeSetEvent.cc
@@ -62,6 +66,7 @@ set(TEST_SRC
hipStreamIsCapturing.cc
hipStreamGetCaptureInfo.cc
hipStreamEndCapture.cc
hipGraphMemcpyNodeSetParamsFromSymbol_old.cc
hipGraphMemcpyNodeSetParamsFromSymbol.cc
hipGraphExecEventWaitNodeSetEvent.cc
hipGraphAddMemsetNode.cc
@@ -73,6 +78,7 @@ set(TEST_SRC
hipGraphExecKernelNodeSetParams.cc
hipGraphLaunch.cc
hipGraphMemcpyNodeSetParams1D.cc
hipGraphExecMemcpyNodeSetParamsToSymbol_old.cc
hipGraphExecMemcpyNodeSetParamsToSymbol.cc
hipGraphNodeGetDependentNodes.cc
hipGraphNodeGetDependencies.cc
@@ -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,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
}
@@ -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,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,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);
}
}