EXSWHTEC-92 - Implement tests for async memcpy of 2D hipArray (#31)

- Implement tests for hipMemcpy2DFromArrayAsync using resource guards and templates
- Implement tests for hipMemcpy2DToArrayAsync using resource guards and templates

[ROCm/hip-tests commit: 818923bfbc]
This commit is contained in:
nives-vukovic
2023-01-17 12:55:54 +01:00
committed by GitHub
parent dfe0430673
commit 8a141b0b91
6 changed files with 1681 additions and 670 deletions
@@ -26,6 +26,7 @@ set(TEST_SRC
malloc.cc
hipMemcpy2DToArray.cc
hipMemcpy2DToArrayAsync.cc
hipMemcpy2DToArrayAsync_old.cc
hipMemcpy3D.cc
hipMemcpy3DAsync.cc
hipMemcpyParam2D.cc
@@ -34,6 +35,7 @@ set(TEST_SRC
hipMemcpy2DAsync.cc
hipMemcpy2DFromArray.cc
hipMemcpy2DFromArrayAsync.cc
hipMemcpy2DFromArrayAsync_old.cc
hipMemcpyAtoH.cc
hipMemcpyHtoA.cc
hipMemcpyAllApiNegative.cc
@@ -111,6 +113,7 @@ set(TEST_SRC
malloc.cc
hipMemcpy2DToArray.cc
hipMemcpy2DToArrayAsync.cc
hipMemcpy2DToArrayAsync_old.cc
hipMemcpy3D.cc
hipMemcpy3DAsync.cc
hipMemcpyParam2D.cc
@@ -119,6 +122,7 @@ set(TEST_SRC
hipMemcpy2DAsync.cc
hipMemcpy2DFromArray.cc
hipMemcpy2DFromArrayAsync.cc
hipMemcpy2DFromArrayAsync_old.cc
hipMemcpyAtoH.cc
hipMemcpyHtoA.cc
hipMemcpyAllApiNegative.cc
@@ -0,0 +1,442 @@
/*
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 <functional>
#include <hip_test_common.hh>
#include <hip/hip_runtime_api.h>
#include <utils.hh>
#include <resource_guards.hh>
#include "hipArrayCommon.hh"
/* Array -> Host */
template <bool should_synchronize, typename T, typename F>
void MemcpyAtoHShell(F memcpy_func, size_t width, const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t allocation_size = width * sizeof(T);
LinearAllocGuard<T> host_allocation(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<T> array_allocation(make_hipExtent(width, 0, 0), flag);
const auto element_count = allocation_size / sizeof(T);
constexpr int fill_value = 42;
std::fill_n(host_allocation.host_ptr(), element_count, fill_value);
HIP_CHECK(hipMemcpy2DToArray(array_allocation.ptr(), 0, 0, host_allocation.host_ptr(),
sizeof(T) * width, sizeof(T) * width, 1, hipMemcpyHostToDevice));
std::fill_n(host_allocation.host_ptr(), element_count, 0);
HIP_CHECK(memcpy_func(host_allocation.host_ptr(), array_allocation.ptr()));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(kernel_stream));
}
ArrayFindIfNot(host_allocation.host_ptr(), fill_value, element_count);
}
template <bool should_synchronize, typename T, typename F>
void Memcpy2DHostFromAShell(F memcpy_func, size_t width, size_t height,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t allocation_size = width * height * sizeof(T);
LinearAllocGuard<T> host_allocation(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<T> array_allocation(make_hipExtent(width, height, 0), flag);
const auto element_count = allocation_size / sizeof(T);
constexpr int fill_value = 42;
std::fill_n(host_allocation.host_ptr(), element_count, fill_value);
HIP_CHECK(hipMemcpy2DToArray(array_allocation.ptr(), 0, 0, host_allocation.host_ptr(),
sizeof(T) * width, sizeof(T) * width, height,
hipMemcpyHostToDevice));
std::fill_n(host_allocation.host_ptr(), element_count, 0);
HIP_CHECK(memcpy_func(host_allocation.host_ptr(), sizeof(T) * width, array_allocation.ptr()));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(kernel_stream));
}
ArrayFindIfNot(host_allocation.host_ptr(), fill_value, element_count);
}
/* Array -> Device */
template <bool should_synchronize, bool enable_peer_access, typename T, typename F>
void Memcpy2DDeviceFromAShell(F memcpy_func, size_t width, size_t height,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t allocation_size = width * height * sizeof(T);
const auto device_count = HipTest::getDeviceCount();
const auto src_device = GENERATE_COPY(range(0, device_count));
const auto dst_device = GENERATE_COPY(range(0, device_count));
INFO("Src device: " << src_device << ", Dst device: " << dst_device);
HIP_CHECK(hipSetDevice(src_device));
if constexpr (enable_peer_access) {
if (src_device == dst_device) {
return;
}
int can_access_peer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device));
if (!can_access_peer) {
INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device);
REQUIRE(can_access_peer);
}
HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0));
}
LinearAllocGuard<T> host_allocation(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<T> array_allocation(make_hipExtent(width, height, 0), flag);
HIP_CHECK(hipSetDevice(dst_device));
LinearAllocGuard2D<T> device_allocation(width, height);
HIP_CHECK(hipSetDevice(src_device));
const auto element_count = allocation_size / sizeof(T);
constexpr int fill_value = 42;
std::fill_n(host_allocation.host_ptr(), element_count, fill_value);
HIP_CHECK(hipMemcpy2DToArray(array_allocation.ptr(), 0, 0, host_allocation.host_ptr(),
sizeof(T) * width, sizeof(T) * width, height,
hipMemcpyHostToDevice));
std::fill_n(host_allocation.host_ptr(), element_count, 0);
HIP_CHECK(
memcpy_func(device_allocation.ptr(), device_allocation.pitch(), array_allocation.ptr()));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(kernel_stream));
}
HIP_CHECK(hipMemcpy2D(host_allocation.host_ptr(), sizeof(T) * width, device_allocation.ptr(),
device_allocation.pitch(), sizeof(T) * width, height,
hipMemcpyDeviceToHost));
if constexpr (enable_peer_access) {
// If we've gotten this far, EnablePeerAccess must have succeeded, so we only need to check this
// condition
HIP_CHECK(hipDeviceDisablePeerAccess(dst_device));
}
ArrayFindIfNot(host_allocation.host_ptr(), fill_value, element_count);
}
/* Host -> Array */
template <bool should_synchronize, typename T, typename F>
void MemcpyHtoAShell(F memcpy_func, size_t width, const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t allocation_size = width * sizeof(T);
LinearAllocGuard<T> host_allocation(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<T> array_allocation(make_hipExtent(width, 0, 0), flag);
const auto element_count = allocation_size / sizeof(T);
constexpr int fill_value = 41;
std::fill_n(host_allocation.host_ptr(), element_count, fill_value);
HIP_CHECK(memcpy_func(array_allocation.ptr(), host_allocation.host_ptr()));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(kernel_stream));
}
std::fill_n(host_allocation.host_ptr(), element_count, 0);
HIP_CHECK(hipMemcpy2DFromArray(host_allocation.host_ptr(), sizeof(T) * width,
array_allocation.ptr(), 0, 0, sizeof(T) * width, 1,
hipMemcpyDeviceToHost));
ArrayFindIfNot(host_allocation.host_ptr(), fill_value, element_count);
}
template <bool should_synchronize, typename T, typename F>
void Memcpy2DHosttoAShell(F memcpy_func, size_t width, size_t height,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
;
size_t allocation_size = width * height * sizeof(T);
LinearAllocGuard<T> host_allocation(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<T> array_allocation(make_hipExtent(width, height, 0), flag);
const auto element_count = allocation_size / sizeof(T);
constexpr int fill_value = 41;
std::fill_n(host_allocation.host_ptr(), element_count, fill_value);
HIP_CHECK(memcpy_func(array_allocation.ptr(), host_allocation.host_ptr(), sizeof(T) * width));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(kernel_stream));
}
std::fill_n(host_allocation.host_ptr(), element_count, 0);
HIP_CHECK(hipMemcpy2DFromArray(host_allocation.host_ptr(), sizeof(T) * width,
array_allocation.ptr(), 0, 0, sizeof(T) * width, height,
hipMemcpyDeviceToHost));
ArrayFindIfNot(host_allocation.host_ptr(), fill_value, element_count);
}
/* Device -> Array */
template <bool should_synchronize, bool enable_peer_access, typename T, typename F>
void Memcpy2DDevicetoAShell(F memcpy_func, size_t width, size_t height,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t allocation_size = width * height * sizeof(T);
const auto device_count = HipTest::getDeviceCount();
const auto src_device = GENERATE_COPY(range(0, device_count));
const auto dst_device = GENERATE_COPY(range(0, device_count));
INFO("Src device: " << src_device << ", Dst device: " << dst_device);
HIP_CHECK(hipSetDevice(src_device));
if constexpr (enable_peer_access) {
if (src_device == dst_device) {
return;
}
int can_access_peer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device));
if (!can_access_peer) {
INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device);
REQUIRE(can_access_peer);
}
HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0));
}
LinearAllocGuard<T> host_allocation(LinearAllocs::hipHostMalloc, allocation_size);
LinearAllocGuard2D<T> device_allocation(width, height);
HIP_CHECK(hipSetDevice(dst_device));
ArrayAllocGuard<T> array_allocation(make_hipExtent(width, height, 0), flag);
HIP_CHECK(hipSetDevice(src_device));
const auto element_count = allocation_size / sizeof(T);
constexpr int fill_value = 41;
std::fill_n(host_allocation.host_ptr(), element_count, fill_value);
HIP_CHECK(hipMemcpy2D(device_allocation.ptr(), device_allocation.pitch(),
host_allocation.host_ptr(), sizeof(T) * width, sizeof(T) * width, height,
hipMemcpyHostToDevice));
HIP_CHECK(
memcpy_func(array_allocation.ptr(), device_allocation.ptr(), device_allocation.pitch()));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(kernel_stream));
}
std::fill_n(host_allocation.host_ptr(), element_count, 0);
HIP_CHECK(hipMemcpy2DFromArray(host_allocation.host_ptr(), sizeof(T) * width,
array_allocation.ptr(), 0, 0, sizeof(T) * width, height,
hipMemcpyDeviceToHost));
if constexpr (enable_peer_access) {
// If we've gotten this far, EnablePeerAccess must have succeeded, so we only need to check this
// condition
HIP_CHECK(hipDeviceDisablePeerAccess(dst_device));
}
ArrayFindIfNot(host_allocation.host_ptr(), fill_value, element_count);
}
// Synchronization behavior checks
template <typename F>
void MemcpyArraySyncBehaviorCheck(F memcpy_func, const bool should_sync,
const hipStream_t kernel_stream) {
LaunchDelayKernel(std::chrono::milliseconds{100}, kernel_stream);
HIP_CHECK(memcpy_func());
if (should_sync) {
HIP_CHECK(hipStreamQuery(kernel_stream));
} else {
HIP_CHECK_ERROR(hipStreamQuery(kernel_stream), hipErrorNotReady);
}
}
/* Host -> Array Sync check */
template <typename F>
void MemcpyHtoASyncBehavior(F memcpy_func, size_t width, size_t height, const bool should_sync,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t num_h = (height == 0) ? 1 : height;
size_t allocation_size = width * num_h * sizeof(int);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<int> array_allocation(make_hipExtent(width, height, 0), flag);
MemcpyArraySyncBehaviorCheck(std::bind(memcpy_func, array_allocation.ptr(), host_alloc.ptr()),
should_sync, kernel_stream);
}
/* Array -> Host sync check */
template <typename F>
void MemcpyAtoHPageableSyncBehavior(F memcpy_func, size_t width, size_t height,
const bool should_sync,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t num_h = (height == 0) ? 1 : height;
size_t allocation_size = width * num_h * sizeof(int);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<int> array_allocation(make_hipExtent(width, height, 0), flag);
MemcpyArraySyncBehaviorCheck(std::bind(memcpy_func, host_alloc.ptr(), array_allocation.ptr()),
should_sync, kernel_stream);
}
template <typename F>
void MemcpyAtoHPinnedSyncBehavior(F memcpy_func, size_t width, size_t height,
const bool should_sync,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
size_t num_h = (height == 0) ? 1 : height;
size_t allocation_size = width * num_h * sizeof(int);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, allocation_size);
ArrayAllocGuard<int> array_allocation(make_hipExtent(width, height, 0), flag);
MemcpyArraySyncBehaviorCheck(std::bind(memcpy_func, host_alloc.ptr(), array_allocation.ptr()),
should_sync, kernel_stream);
}
/* Device -> Array sync check */
template <typename F>
void MemcpyDtoASyncBehavior(F memcpy_func, size_t width, size_t height, const bool should_sync,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
ArrayAllocGuard<int> array_allocation(make_hipExtent(width, height, 0), flag);
LinearAllocGuard2D<int> device_allocation(width, height);
MemcpyArraySyncBehaviorCheck(std::bind(memcpy_func, array_allocation.ptr(),
device_allocation.ptr(), device_allocation.pitch()),
should_sync, kernel_stream);
}
/* Array -> Device sync check */
template <typename F>
void MemcpyAtoDSyncBehavior(F memcpy_func, size_t width, size_t height, const bool should_sync,
const hipStream_t kernel_stream = nullptr) {
const unsigned int flag = hipArrayDefault;
LinearAllocGuard2D<int> device_allocation(width, height);
ArrayAllocGuard<int> array_allocation(make_hipExtent(width, height, 0), flag);
MemcpyArraySyncBehaviorCheck(std::bind(memcpy_func, device_allocation.ptr(),
device_allocation.pitch(), array_allocation.ptr()),
should_sync, kernel_stream);
}
/* Array -> Host/Device zero copy */
template <bool should_synchronize, typename F>
void Memcpy2DFromArrayZeroWidthHeight(F memcpy_func, size_t width, size_t height,
const hipStream_t stream = nullptr) {
const unsigned int flag = hipArrayDefault;
const auto element_count = width * height;
SECTION("Device to Host") {
ArrayAllocGuard<int> array_alloc(make_hipExtent(width, height, 0), flag);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, width * height * sizeof(int));
int fill_value = 42;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.host_ptr(),
sizeof(int) * width, sizeof(int) * width, height,
hipMemcpyHostToDevice));
fill_value = 41;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(memcpy_func(host_alloc.host_ptr(), sizeof(int) * width, array_alloc.ptr()));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(stream));
}
ArrayFindIfNot(host_alloc.host_ptr(), fill_value, element_count);
}
SECTION("Device to Device") {
ArrayAllocGuard<int> array_alloc(make_hipExtent(width, height, 0), flag);
LinearAllocGuard2D<int> device_alloc(width, height);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, width * height * sizeof(int));
int fill_value = 42;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.host_ptr(),
sizeof(int) * width, sizeof(int) * width, height,
hipMemcpyHostToDevice));
fill_value = 41;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(hipMemcpy2D(device_alloc.ptr(), device_alloc.pitch(), host_alloc.host_ptr(),
sizeof(int) * width, sizeof(int) * width, height, hipMemcpyHostToDevice));
HIP_CHECK(memcpy_func(device_alloc.ptr(), device_alloc.pitch(), array_alloc.ptr()));
if constexpr (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(stream));
}
HIP_CHECK(hipMemcpy2D(host_alloc.host_ptr(), sizeof(int) * width, device_alloc.ptr(),
device_alloc.pitch(), sizeof(int) * width, height,
hipMemcpyDeviceToHost));
ArrayFindIfNot(host_alloc.host_ptr(), fill_value, element_count);
}
}
/* Host/Device -> Array zero copy */
template <bool should_synchronize, typename F>
void Memcpy2DToArrayZeroWidthHeight(F memcpy_func, size_t width, size_t height,
const hipStream_t stream = nullptr) {
const unsigned int flag = hipArrayDefault;
const auto element_count = width * height;
SECTION("Host to Device") {
ArrayAllocGuard<int> array_alloc(make_hipExtent(width, height, 0), flag);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, width * height * sizeof(int));
int fill_value = 42;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.host_ptr(),
sizeof(int) * width, sizeof(int) * width, height,
hipMemcpyHostToDevice));
fill_value = 41;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(memcpy_func(array_alloc.ptr(), host_alloc.host_ptr(), sizeof(int) * width));
if (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(stream));
}
HIP_CHECK(hipMemcpy2DFromArray(host_alloc.host_ptr(), sizeof(int) * width, array_alloc.ptr(), 0,
0, sizeof(int) * width, height, hipMemcpyDeviceToHost));
ArrayFindIfNot(host_alloc.host_ptr(), 42, element_count);
}
SECTION("Device to Device") {
ArrayAllocGuard<int> array_alloc(make_hipExtent(width, height, 0), flag);
LinearAllocGuard2D<int> device_alloc(width, height);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, width * height * sizeof(int));
int fill_value = 42;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.host_ptr(),
sizeof(int) * width, sizeof(int) * width, height,
hipMemcpyHostToDevice));
fill_value = 41;
std::fill_n(host_alloc.host_ptr(), width * height, fill_value);
HIP_CHECK(hipMemcpy2D(device_alloc.ptr(), device_alloc.pitch(), host_alloc.host_ptr(),
sizeof(int) * width, sizeof(int) * width, height, hipMemcpyHostToDevice));
HIP_CHECK(memcpy_func(array_alloc.ptr(), device_alloc.ptr(), device_alloc.pitch()));
if constexpr (should_synchronize) {
HIP_CHECK(hipStreamSynchronize(stream));
}
HIP_CHECK(hipMemcpy2DFromArray(host_alloc.host_ptr(), sizeof(int) * width, array_alloc.ptr(), 0,
0, sizeof(int) * width, height, hipMemcpyDeviceToHost));
ArrayFindIfNot(host_alloc.host_ptr(), 42, element_count);
}
}
@@ -1,5 +1,5 @@
/*
Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@@ -16,357 +16,270 @@ 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.
*/
/*
This file verifies the following scenarios of hipMemcpy2DFromArrayAsync API
1. Negative Scenarios
2. Extent Validation Scenarios
3. hipMemcpy2DFromArrayAsync Basic Scenario
4. Pinned Memory scenarios on same and peer GPU
5. Device Context change scenario where memory is allocated in
one GPU and stream is created in peer GPU.
Testcase Scenarios :
Unit_hipMemcpy2DFromArrayAsync_Positive_Default - Test basic async memcpy
between 2D array and host/device with hipMemcpy2DFromArrayAsync api
Unit_hipMemcpy2DFromArrayAsync_Positive_Synchronization_Behavior - Test
synchronization behavior for hipMemcpy2DFromArrayAsync api
Unit_hipMemcpy2DFromArrayAsync_Positive_ZeroWidthHeight - Test that no data is
copied when width/height is set to 0
Unit_hipMemcpy2DFromArrayAsync_Negative_Parameters - Test unsuccessful execution
of hipMemcpy2DFromArrayAsync api when parameters are invalid
*/
#include "array_memcpy_tests_common.hh"
#include <hip/hip_runtime_api.h>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <resource_guards.hh>
#include <utils.hh>
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Positive_Default") {
using namespace std::placeholders;
static constexpr auto NUM_W{10};
static constexpr auto NUM_H{10};
const auto stream_type = GENERATE(Streams::nullstream, Streams::perThread, Streams::created);
const StreamGuard stream_guard(stream_type);
const hipStream_t stream = stream_guard.stream();
/*
* This testcase copies the data from host to device of
hipMemcpy2DFromArrayAsync API
* INPUT: Copying Host variable hData(Initialized with value Phi(1.618))
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Basic") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
const auto width = GENERATE(16, 32, 48);
const auto height = GENERATE(1, 16, 32, 48);
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width,
width, NUM_H,
hipMemcpyHostToDevice));
SECTION("Calling hipMemcpy2DFromArrayAsync() with user declared stream obj") {
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
}
SECTION("Calling hipMemcpy2DFromArrayAsync() with hipStreamPerThread") {
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, hipStreamPerThread));
HIP_CHECK(hipStreamSynchronize(hipStreamPerThread));
}
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}
/*
* This testcase verifies the extent validation scenarios
* of hipMemcpy2DFromArrayAsync API
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_ExtentValidation") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr}, *valData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
nullptr, &valData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HIP_CHECK(hipStreamCreate(&stream));
SECTION("Destination width is 0") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, 0, A_d,
0, 0, NUM_W*sizeof(float),
NUM_H, hipMemcpyDeviceToHost, stream)
!= hipSuccess);
}
// hipMemcpy2DFromArrayAsync API would return success for
// width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying A_d-->hData variable
// with height 0(copy will not be performed)
// 3 validating hData<-->A_h which will not be equal as copy is not done.
SECTION("Height is 0") {
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0,
A_h, width, width,
NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(hData, width, A_d,
0, 0, NUM_W*sizeof(float),
0, hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true);
}
// hipMemcpy2DFromArrayAsync API would return success for
// width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying A_d-->hData variable
// with width 0(copy will not be performed)
// 3 validating hData<-->A_h which will not be equal as copy is not done.
SECTION("Width is 0") {
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0,
A_h, width, width,
NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(hData, width, A_d,
0, 0, 0,
NUM_H, hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true);
SECTION("Array to host") {
Memcpy2DHostFromAShell<true, int>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), height,
hipMemcpyDeviceToHost, stream),
width, height, stream);
}
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
nullptr, valData, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DFromArrayAsync API by copying the
* data from pinned host memory to device on same GPU
* INPUT: Copying Host variable PinnMem(Initialized with value "10" )
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with PinnedMem[0](i.e., 10)
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_PinnedHostMemSameGpu") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
constexpr auto def_val{10};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *PinnMem{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&PinnMem), width * NUM_H));
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
for (int i = 0; i < NUM_W*NUM_H; i++) {
PinnMem[i] = def_val + i;
SECTION("Array to host with default kind") {
Memcpy2DHostFromAShell<true, int>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), height,
hipMemcpyDefault, stream),
width, height, stream);
}
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, PinnMem,
width, width, NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(A_h, PinnMem, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(PinnMem));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DFromArrayAsync API by copying the
* data from pinned host memory to device from Peer GPU.
* Device Memory is allocated in GPU 0 and the API is trigerred from GPU1
* INPUT: Initialize data, A_h --> A_d device variable
* whose memory is allocated in GPU 0
then A_d-->E_h in GPU1
* OUTPUT: validating the result by comparing A_h and E_h
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_multiDevicePinnedHostMem") {
int numDevices = 0;
constexpr auto def_val{10};
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *E_h{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&E_h), width * NUM_H));
for (int i = 0; i < NUM_W*NUM_H; i++) {
E_h[i] = def_val + i;
}
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, A_h, width,
width, NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipMemcpy2DFromArrayAsync(E_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(A_h, E_h, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(E_h));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
} else {
SUCCEED("Device Does not have P2P capability");
#if HT_NVIDIA // EXSWHTEC-213
SECTION("Array to device") {
SECTION("Peer access disabled") {
Memcpy2DDeviceFromAShell<true, false, int>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), height,
hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/*
* This scenario verifies the hipMemcpy2DFromArrayAsync API in case of device
* context change.
* Memory is allocated in GPU-0 and the API is triggered from GPU-1
* INPUT: Copying Host variable hData(Initial value Phi)
* --> A_d device variable
* whose memory is allocated in GPU 0
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
* */
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_multiDeviceContextChange") {
int numDevices = 0;
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, width,
NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
} else {
SUCCEED("Device Does not have P2P capability");
SECTION("Peer access enabled") {
Memcpy2DDeviceFromAShell<true, true, int>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), height,
hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/* This testcase verifies the negative scenarios
* of hipMemcpy2DFromArrayAsync API
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Negative") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
SECTION("Nullptr to destination") {
REQUIRE(hipMemcpy2DFromArrayAsync(nullptr, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
}
SECTION("Nullptr to source") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, width, nullptr,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
SECTION("Array to device with default kind") {
SECTION("Peer access disabled") {
Memcpy2DDeviceFromAShell<true, false, int>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), height,
hipMemcpyDefault, stream),
width, height, stream);
}
SECTION("Peer access enabled") {
Memcpy2DDeviceFromAShell<true, true, int>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), height,
hipMemcpyDefault, stream),
width, height, stream);
}
}
SECTION("Passing offset more than 0") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, width, A_d, 1,
1, width, NUM_H,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
}
SECTION("Passing array more than allocated") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, width, A_d, 0,
0, width+2, NUM_H+2,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
}
// Cleaning of Memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
#endif
}
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Positive_Synchronization_Behavior") {
using namespace std::placeholders;
HIP_CHECK(hipDeviceSynchronize());
SECTION("Array to host") {
const auto width = GENERATE(16, 32, 48);
const auto height = GENERATE(16, 32, 48);
MemcpyAtoHPageableSyncBehavior(
std::bind(hipMemcpy2DFromArrayAsync, _1, width * sizeof(int), _2, 0, 0, width * sizeof(int),
height, hipMemcpyDeviceToHost, nullptr),
width, height, false);
MemcpyAtoHPinnedSyncBehavior(
std::bind(hipMemcpy2DFromArrayAsync, _1, width * sizeof(int), _2, 0, 0, width * sizeof(int),
height, hipMemcpyDeviceToHost, nullptr),
width, height, false);
}
SECTION("Array to device") {
const auto width = GENERATE(16, 32, 48);
const auto height = GENERATE(16, 32, 48);
MemcpyAtoDSyncBehavior(std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0,
width * sizeof(int), height, hipMemcpyDeviceToDevice, nullptr),
width, height, false);
}
}
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Positive_ZeroWidthHeight") {
using namespace std::placeholders;
const auto stream_type = GENERATE(Streams::nullstream, Streams::perThread, Streams::created);
const StreamGuard stream_guard(stream_type);
const hipStream_t stream = stream_guard.stream();
const auto width = 16;
const auto height = 16;
SECTION("Array to host") {
SECTION("Height is 0") {
Memcpy2DFromArrayZeroWidthHeight<true>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), 0,
hipMemcpyDeviceToHost, stream),
width, height, stream);
}
SECTION("Width is 0") {
Memcpy2DFromArrayZeroWidthHeight<true>(std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0,
0, height, hipMemcpyDeviceToHost, stream),
width, height, stream);
}
}
SECTION("Array to device") {
SECTION("Height is 0") {
Memcpy2DFromArrayZeroWidthHeight<true>(
std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), 0,
hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
SECTION("Width is 0") {
Memcpy2DFromArrayZeroWidthHeight<true>(std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0,
0, height, hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
}
}
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Negative_Parameters") {
using namespace std::placeholders;
const auto width = 32;
const auto height = 32;
const auto allocation_size = 2 * width * height * sizeof(int);
const unsigned int flag = hipArrayDefault;
constexpr auto InvalidStream = [] {
StreamGuard sg(Streams::created);
return sg.stream();
};
ArrayAllocGuard<int> array_alloc(make_hipExtent(width, height, 0), flag);
LinearAllocGuard2D<int> device_alloc(width, height);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, allocation_size);
SECTION("Array to host") {
SECTION("dst == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DFromArrayAsync(nullptr, 2 * width * sizeof(int), array_alloc.ptr(), 0, 0,
width * sizeof(int), height, hipMemcpyDeviceToHost, nullptr),
hipErrorInvalidValue);
}
SECTION("src == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DFromArrayAsync(host_alloc.ptr(), 2 * width * sizeof(int), nullptr, 0, 0,
width * sizeof(int), height, hipMemcpyDeviceToHost, nullptr),
hipErrorInvalidHandle);
}
#if HT_NVIDIA // EXSWHTEC-212
SECTION("dpitch < width") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(host_alloc.ptr(), width * sizeof(int) - 10,
array_alloc.ptr(), 0, 0, width * sizeof(int),
height, hipMemcpyDeviceToHost, nullptr),
hipErrorInvalidPitchValue);
}
SECTION("Offset + width/height overflows") {
HIP_CHECK_ERROR(
hipMemcpy2DFromArrayAsync(host_alloc.ptr(), 2 * width * sizeof(int), array_alloc.ptr(), 1,
0, width * sizeof(int), height, hipMemcpyDeviceToHost, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(
hipMemcpy2DFromArrayAsync(host_alloc.ptr(), 2 * width * sizeof(int), array_alloc.ptr(), 0,
1, width * sizeof(int), height, hipMemcpyDeviceToHost, nullptr),
hipErrorInvalidValue);
}
SECTION("Width/height overflows") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(host_alloc.ptr(), 2 * width * sizeof(int),
array_alloc.ptr(), 0, 0, width * sizeof(int) + 1,
height, hipMemcpyDeviceToHost, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(host_alloc.ptr(), 2 * width * sizeof(int),
array_alloc.ptr(), 0, 0, width * sizeof(int),
height + 1, hipMemcpyDeviceToHost, nullptr),
hipErrorInvalidValue);
}
SECTION("Memcpy kind is invalid") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(host_alloc.ptr(), 2 * width * sizeof(int),
array_alloc.ptr(), 0, 0, width * sizeof(int),
height, static_cast<hipMemcpyKind>(-1), nullptr),
hipErrorInvalidMemcpyDirection);
}
SECTION("Invalid stream") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(host_alloc.ptr(), 2 * width * sizeof(int),
array_alloc.ptr(), 0, 0, width * sizeof(int),
height, hipMemcpyDeviceToHost, InvalidStream()),
hipErrorContextIsDestroyed);
}
#endif
}
SECTION("Array to device") {
SECTION("dst == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DFromArrayAsync(nullptr, device_alloc.pitch(), array_alloc.ptr(), 0, 0,
width * sizeof(int), height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
}
SECTION("src == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DFromArrayAsync(device_alloc.ptr(), device_alloc.pitch(), nullptr, 0, 0,
width * sizeof(int), height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidHandle);
}
#if HT_NVIDIA // EXSWHTEC-212
SECTION("dpitch < width") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(device_alloc.ptr(), width * sizeof(int) - 10,
array_alloc.ptr(), 0, 0, width * sizeof(int),
height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidPitchValue);
}
SECTION("Offset + width/height overflows") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(device_alloc.ptr(), device_alloc.pitch(),
array_alloc.ptr(), 1, 0, width * sizeof(int),
height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(device_alloc.ptr(), device_alloc.pitch(),
array_alloc.ptr(), 0, 1, width * sizeof(int),
height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
}
SECTION("Width/height overflows") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(device_alloc.ptr(), device_alloc.pitch(),
array_alloc.ptr(), 0, 0, width * sizeof(int) + 1,
height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(device_alloc.ptr(), device_alloc.pitch(),
array_alloc.ptr(), 0, 0, width * sizeof(int),
height + 1, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
}
SECTION("Memcpy kind is invalid") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(device_alloc.ptr(), device_alloc.pitch(),
array_alloc.ptr(), 0, 0, width * sizeof(int),
height, static_cast<hipMemcpyKind>(-1), nullptr),
hipErrorInvalidMemcpyDirection);
}
SECTION("Invalid stream") {
HIP_CHECK_ERROR(hipMemcpy2DFromArrayAsync(device_alloc.ptr(), device_alloc.pitch(),
array_alloc.ptr(), 0, 0, width * sizeof(int),
height, hipMemcpyDeviceToDevice, InvalidStream()),
hipErrorContextIsDestroyed);
}
#endif
}
}
@@ -0,0 +1,372 @@
/*
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 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.
*/
/*
This file verifies the following scenarios of hipMemcpy2DFromArrayAsync API
1. Negative Scenarios
2. Extent Validation Scenarios
3. hipMemcpy2DFromArrayAsync Basic Scenario
4. Pinned Memory scenarios on same and peer GPU
5. Device Context change scenario where memory is allocated in
one GPU and stream is created in peer GPU.
*/
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
static constexpr auto NUM_W{10};
static constexpr auto NUM_H{10};
/*
* This testcase copies the data from host to device of
hipMemcpy2DFromArrayAsync API
* INPUT: Copying Host variable hData(Initialized with value Phi(1.618))
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Basic") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width,
width, NUM_H,
hipMemcpyHostToDevice));
SECTION("Calling hipMemcpy2DFromArrayAsync() with user declared stream obj") {
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
}
SECTION("Calling hipMemcpy2DFromArrayAsync() with hipStreamPerThread") {
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, hipStreamPerThread));
HIP_CHECK(hipStreamSynchronize(hipStreamPerThread));
}
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}
/*
* This testcase verifies the extent validation scenarios
* of hipMemcpy2DFromArrayAsync API
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_ExtentValidation") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr}, *valData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
nullptr, &valData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HIP_CHECK(hipStreamCreate(&stream));
SECTION("Destination width is 0") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, 0, A_d,
0, 0, NUM_W*sizeof(float),
NUM_H, hipMemcpyDeviceToHost, stream)
!= hipSuccess);
}
// hipMemcpy2DFromArrayAsync API would return success for
// width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying A_d-->hData variable
// with height 0(copy will not be performed)
// 3 validating hData<-->A_h which will not be equal as copy is not done.
SECTION("Height is 0") {
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0,
A_h, width, width,
NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(hData, width, A_d,
0, 0, NUM_W*sizeof(float),
0, hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true);
}
// hipMemcpy2DFromArrayAsync API would return success for
// width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying A_d-->hData variable
// with width 0(copy will not be performed)
// 3 validating hData<-->A_h which will not be equal as copy is not done.
SECTION("Width is 0") {
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0,
A_h, width, width,
NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(hData, width, A_d,
0, 0, 0,
NUM_H, hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true);
}
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
nullptr, valData, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DFromArrayAsync API by copying the
* data from pinned host memory to device on same GPU
* INPUT: Copying Host variable PinnMem(Initialized with value "10" )
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with PinnedMem[0](i.e., 10)
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_PinnedHostMemSameGpu") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
constexpr auto def_val{10};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *PinnMem{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&PinnMem), width * NUM_H));
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
for (int i = 0; i < NUM_W*NUM_H; i++) {
PinnMem[i] = def_val + i;
}
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, PinnMem,
width, width, NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(A_h, PinnMem, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(PinnMem));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DFromArrayAsync API by copying the
* data from pinned host memory to device from Peer GPU.
* Device Memory is allocated in GPU 0 and the API is trigerred from GPU1
* INPUT: Initialize data, A_h --> A_d device variable
* whose memory is allocated in GPU 0
then A_d-->E_h in GPU1
* OUTPUT: validating the result by comparing A_h and E_h
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_multiDevicePinnedHostMem") {
int numDevices = 0;
constexpr auto def_val{10};
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *E_h{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&E_h), width * NUM_H));
for (int i = 0; i < NUM_W*NUM_H; i++) {
E_h[i] = def_val + i;
}
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, A_h, width,
width, NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipMemcpy2DFromArrayAsync(E_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(A_h, E_h, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(E_h));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
} else {
SUCCEED("Device Does not have P2P capability");
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/*
* This scenario verifies the hipMemcpy2DFromArrayAsync API in case of device
* context change.
* Memory is allocated in GPU-0 and the API is triggered from GPU-1
* INPUT: Copying Host variable hData(Initial value Phi)
* --> A_d device variable
* whose memory is allocated in GPU 0
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
* */
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_multiDeviceContextChange") {
int numDevices = 0;
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, width,
NUM_H, hipMemcpyHostToDevice));
HIP_CHECK(hipMemcpy2DFromArrayAsync(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost, stream));
HIP_CHECK(hipStreamSynchronize(stream));
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
} else {
SUCCEED("Device Does not have P2P capability");
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/* This testcase verifies the negative scenarios
* of hipMemcpy2DFromArrayAsync API
*/
TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Negative") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
SECTION("Nullptr to destination") {
REQUIRE(hipMemcpy2DFromArrayAsync(nullptr, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
}
SECTION("Nullptr to source") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, width, nullptr,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
}
SECTION("Passing offset more than 0") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, width, A_d, 1,
1, width, NUM_H,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
}
SECTION("Passing array more than allocated") {
REQUIRE(hipMemcpy2DFromArrayAsync(A_h, width, A_d, 0,
0, width+2, NUM_H+2,
hipMemcpyDeviceToHost,
stream) != hipSuccess);
}
// Cleaning of Memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}
@@ -1,5 +1,5 @@
/*
Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@@ -16,354 +16,265 @@ 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.
*/
/*
This file verifies the following scenarios of hipMemcpy2DToArrayAsync API
1. Negative Scenarios
2. Extent Validation Scenarios
3. hipMemcpy2DToArrayAsync Basic Scenario
4. Pinned Memory scenarios on same and peer GPU
5. Device Context change scenario where memory is allocated in
one GPU and stream is created in peer GPU.
Testcase Scenarios :
Unit_hipMemcpy2DToArrayAsync_Positive_Default - Test basic async memcpy between
host/device and 2D array with hipMemcpy2DToArrayAsync api
Unit_hipMemcpy2DToArrayAsync_Positive_Synchronization_Behavior - Test
synchronization behavior for hipMemcpy2DToArrayAsync api
Unit_hipMemcpy2DToArrayAsync_Positive_ZeroWidthHeight - Test that no data is
copied when width/height is set to 0
Unit_hipMemcpy2DToArrayAsync_Negative_Parameters - Test unsuccessful execution
of hipMemcpy2DToArrayAsync api when parameters are invalid
*/
#include "array_memcpy_tests_common.hh"
#include <hip/hip_runtime_api.h>
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <iostream>
#include <resource_guards.hh>
#include <utils.hh>
static constexpr auto NUM_W{10};
static constexpr auto NUM_H{10};
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Positive_Default") {
using namespace std::placeholders;
/*
* This Scenario copies the data from host to device
* INPUT: Copying Host variable hData(Initialized with value Phi(1.618))
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Basic") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
const auto stream_type = GENERATE(Streams::nullstream, Streams::perThread, Streams::created);
const StreamGuard stream_guard(stream_type);
const hipStream_t stream = stream_guard.stream();
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipStreamCreate(&stream));
SECTION("Calling hipMemcpy2DToArrayAsync() with user declared stream obj") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width, NUM_H,
hipMemcpyHostToDevice, stream));
HIP_CHECK(hipStreamSynchronize(stream));
}
SECTION("Calling hipMemcpy2DToArrayAsync() with hipStreamPerThread") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width, NUM_H,
hipMemcpyHostToDevice, hipStreamPerThread));
HIP_CHECK(hipStreamSynchronize(hipStreamPerThread));
}
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
const auto width = GENERATE(16, 32, 48);
const auto height = GENERATE(1, 16, 32, 48);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}
/*
* This testcase verifies the extent validation scenarios
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_ExtentValidation") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HIP_CHECK(hipStreamCreate(&stream));
SECTION("Source width is 0") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, 0,
width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
HIP_CHECK(hipStreamSynchronize(stream));
}
// hipMemcpy2DToArray API would return success for width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying hData(Phi)-->A_d device variable
// with height 0(copy will not be performed)
// 3.copying A_d-->hData and validating it with A_h data
SECTION("Height is 0") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, A_h, width,
width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width, 0, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(hData, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(hData, A_h, NUM_W, NUM_H) == true);
}
// hipMemcpy2DToArray API would return success for width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying hData(Phi)-->A_d device variable
// with width 0(copy will not be performed)
// 3.copying A_d-->hData and validating it with A_h data
SECTION("Width is 0") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, A_h, width,
width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
0, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(hData, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(hData, A_h, NUM_W, NUM_H) == true);
SECTION("Host to Array") {
Memcpy2DHosttoAShell<true, int>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), height,
hipMemcpyHostToDevice, stream),
width, height, stream);
}
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DToArray API by copying the
* data from pinned host memory to device on same GPU
* INPUT: Copying Host variable PinnMem(Initialized with value "10" )
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with PinnedMem[0](i.e., 10)
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_PinnedHostMemSameGpu") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
constexpr auto def_val{10};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *PinnMem{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&PinnMem), width * NUM_H));
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
for (int i = 0; i < NUM_W*NUM_H; i++) {
PinnMem[i] = def_val + i;
SECTION("Host to Array with default kind") {
Memcpy2DHosttoAShell<true, int>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), height,
hipMemcpyDefault, stream),
width, height, stream);
}
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, PinnMem,
width, width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, PinnMem, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(PinnMem));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DToArray API by copying the
* data from pinned host memory to device from Peer GPU.
* Device Memory is allocated in GPU 0 and the API is trigerred from GPU1
* INPUT: Copying Host variable E_h(Initialized with value 10+i(numelements))
* --> A_d device variable
* whose memory is allocated in GPU 0
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with E_h[0]+i(i.e., 10+i)
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_multiDevicePinnedHostMem") {
int numDevices = 0;
constexpr auto def_val{10};
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *E_h{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&E_h), width * NUM_H));
for (int i = 0; i < NUM_W*NUM_H; i++) {
E_h[i] = def_val + i;
}
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, E_h, width,
width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipSetDevice(0));
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, E_h, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(E_h));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
} else {
SUCCEED("Machine Does not have P2P capability");
#if HT_NVIDIA // EXSWHTEC-213
SECTION("Device to Array") {
SECTION("Peer access disabled") {
Memcpy2DDevicetoAShell<true, false, int>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), height,
hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/*
* This scenario verifies the hipMemcpy2DToArray API in case of device
* context change.
* Memory is allocated in GPU-0 and the API is triggered from GPU-1
* INPUT: Copying Host variable hData(Initial value Phi)
* --> A_d device variable
* whose memory is allocated in GPU 0
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
* */
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_multiDeviceDeviceContextChange") {
int numDevices = 0;
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width, width,
NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
} else {
SUCCEED("Machine Does not have P2P capability");
SECTION("Peer access enabled") {
Memcpy2DDevicetoAShell<true, true, int>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), height,
hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/* This testcase verifies the negative scenarios
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Negative") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
SECTION("Nullptr to destination") {
REQUIRE(hipMemcpy2DToArrayAsync(nullptr, 0, 0, hData, width,
width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
}
SECTION("Nullptr to source") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 0, 0, nullptr,
width, width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
SECTION("Device to Array with default kind") {
SECTION("Peer access disabled") {
Memcpy2DDevicetoAShell<true, false, int>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), height,
hipMemcpyDefault, stream),
width, height, stream);
}
SECTION("Peer access enabled") {
Memcpy2DDevicetoAShell<true, true, int>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), height,
hipMemcpyDefault, stream),
width, height, stream);
}
}
SECTION("Passing offset more than 0") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 1, 1, hData, width,
width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
}
SECTION("Passing array more than allocated") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width+2, NUM_H+2, hipMemcpyHostToDevice,
stream) != hipSuccess);
}
// Cleaning of Memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
#endif
}
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Positive_Synchronization_Behavior") {
using namespace std::placeholders;
HIP_CHECK(hipDeviceSynchronize());
SECTION("Host to Array") {
const auto width = GENERATE(16, 32, 48);
const auto height = GENERATE(16, 32, 48);
MemcpyHtoASyncBehavior(std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, width * sizeof(int),
width * sizeof(int), height, hipMemcpyHostToDevice, nullptr),
width, height, false);
}
SECTION("Device to Array") {
const auto width = GENERATE(16, 32, 48);
const auto height = GENERATE(16, 32, 48);
MemcpyDtoASyncBehavior(std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int),
height, hipMemcpyDeviceToDevice, nullptr),
width, height, false);
}
}
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Positive_ZeroWidthHeight") {
using namespace std::placeholders;
const auto width = 16;
const auto height = 16;
const auto stream_type = GENERATE(Streams::nullstream, Streams::perThread, Streams::created);
const StreamGuard stream_guard(stream_type);
const hipStream_t stream = stream_guard.stream();
SECTION("Array to host") {
SECTION("Height is 0") {
Memcpy2DToArrayZeroWidthHeight<false>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), 0,
hipMemcpyHostToDevice, stream),
width, height, stream);
}
SECTION("Width is 0") {
Memcpy2DToArrayZeroWidthHeight<false>(std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, 0,
height, hipMemcpyHostToDevice, stream),
width, height, stream);
}
}
SECTION("Array to device") {
SECTION("Height is 0") {
Memcpy2DToArrayZeroWidthHeight<false>(
std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), 0,
hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
SECTION("Width is 0") {
Memcpy2DToArrayZeroWidthHeight<false>(std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, 0,
height, hipMemcpyDeviceToDevice, stream),
width, height, stream);
}
}
}
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Negative_Parameters") {
using namespace std::placeholders;
const auto width = 32;
const auto height = 32;
const auto allocation_size = 2 * width * height * sizeof(int);
const unsigned int flag = hipArrayDefault;
constexpr auto InvalidStream = [] {
StreamGuard sg(Streams::created);
return sg.stream();
};
ArrayAllocGuard<int> array_alloc(make_hipExtent(width, height, 0), flag);
LinearAllocGuard2D<int> device_alloc(width, height);
LinearAllocGuard<int> host_alloc(LinearAllocs::hipHostMalloc, allocation_size);
SECTION("Host to Array") {
SECTION("dst == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DToArrayAsync(nullptr, 0, 0, host_alloc.ptr(), 2 * width * sizeof(int),
width * sizeof(int), height, hipMemcpyHostToDevice, nullptr),
hipErrorInvalidHandle);
}
SECTION("src == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, nullptr, 2 * width * sizeof(int),
width * sizeof(int), height, hipMemcpyHostToDevice, nullptr),
hipErrorInvalidValue);
}
#if HT_NVIDIA // EXSWHTEC-212
SECTION("spitch < width") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, host_alloc.ptr(),
width * sizeof(int) - 10, width * sizeof(int), height,
hipMemcpyHostToDevice, nullptr),
hipErrorInvalidPitchValue);
}
SECTION("Offset + width/height overflows") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 1, 0, host_alloc.ptr(),
2 * width * sizeof(int), width * sizeof(int), height,
hipMemcpyHostToDevice, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 1, host_alloc.ptr(),
2 * width * sizeof(int), width * sizeof(int), height,
hipMemcpyHostToDevice, nullptr),
hipErrorInvalidValue);
}
SECTION("Width/height overflows") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, host_alloc.ptr(),
2 * width * sizeof(int), width * sizeof(int) + 1,
height, hipMemcpyHostToDevice, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, host_alloc.ptr(),
2 * width * sizeof(int), width * sizeof(int),
height + 1, hipMemcpyHostToDevice, nullptr),
hipErrorInvalidValue);
}
SECTION("Memcpy kind is invalid") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, host_alloc.ptr(),
2 * width * sizeof(int), width * sizeof(int), height,
static_cast<hipMemcpyKind>(-1), nullptr),
hipErrorInvalidMemcpyDirection);
}
SECTION("Invalid stream") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, host_alloc.ptr(),
2 * width * sizeof(int), width * sizeof(int), height,
hipMemcpyHostToDevice, InvalidStream()),
hipErrorContextIsDestroyed);
}
#endif
}
SECTION("Device to Array") {
SECTION("dst == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DToArrayAsync(nullptr, 0, 0, device_alloc.ptr(), device_alloc.pitch(),
width * sizeof(int), height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidHandle);
}
SECTION("src == nullptr") {
HIP_CHECK_ERROR(
hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, nullptr, device_alloc.pitch(),
width * sizeof(int), height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
}
#if HT_NVIDIA // EXSWHTEC-212
SECTION("spitch < width") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, device_alloc.ptr(),
width * sizeof(int) - 10, width * sizeof(int), height,
hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidPitchValue);
}
SECTION("Offset + width/height overflows") {
HIP_CHECK_ERROR(
hipMemcpy2DToArrayAsync(array_alloc.ptr(), 1, 0, device_alloc.ptr(), device_alloc.pitch(),
width * sizeof(int), height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(
hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 1, device_alloc.ptr(), device_alloc.pitch(),
width * sizeof(int), height, hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
}
SECTION("Width/height overflows") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, device_alloc.ptr(),
device_alloc.pitch(), width * sizeof(int) + 1, height,
hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, device_alloc.ptr(),
device_alloc.pitch(), width * sizeof(int), height + 1,
hipMemcpyDeviceToDevice, nullptr),
hipErrorInvalidValue);
}
SECTION("Memcpy kind is invalid") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, device_alloc.ptr(),
device_alloc.pitch(), width * sizeof(int), height,
static_cast<hipMemcpyKind>(-1), nullptr),
hipErrorInvalidMemcpyDirection);
}
SECTION("Invalid stream") {
HIP_CHECK_ERROR(hipMemcpy2DToArrayAsync(array_alloc.ptr(), 0, 0, device_alloc.ptr(),
device_alloc.pitch(), width * sizeof(int), height,
hipMemcpyDeviceToDevice, InvalidStream()),
hipErrorContextIsDestroyed);
}
#endif
}
}
@@ -0,0 +1,369 @@
/*
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 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.
*/
/*
This file verifies the following scenarios of hipMemcpy2DToArrayAsync API
1. Negative Scenarios
2. Extent Validation Scenarios
3. hipMemcpy2DToArrayAsync Basic Scenario
4. Pinned Memory scenarios on same and peer GPU
5. Device Context change scenario where memory is allocated in
one GPU and stream is created in peer GPU.
*/
#include <hip_test_common.hh>
#include <hip_test_checkers.hh>
#include <iostream>
static constexpr auto NUM_W{10};
static constexpr auto NUM_H{10};
/*
* This Scenario copies the data from host to device
* INPUT: Copying Host variable hData(Initialized with value Phi(1.618))
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Basic") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipStreamCreate(&stream));
SECTION("Calling hipMemcpy2DToArrayAsync() with user declared stream obj") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width, NUM_H,
hipMemcpyHostToDevice, stream));
HIP_CHECK(hipStreamSynchronize(stream));
}
SECTION("Calling hipMemcpy2DToArrayAsync() with hipStreamPerThread") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width, NUM_H,
hipMemcpyHostToDevice, hipStreamPerThread));
HIP_CHECK(hipStreamSynchronize(hipStreamPerThread));
}
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}
/*
* This testcase verifies the extent validation scenarios
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_ExtentValidation") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HIP_CHECK(hipStreamCreate(&stream));
SECTION("Source width is 0") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, 0,
width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
HIP_CHECK(hipStreamSynchronize(stream));
}
// hipMemcpy2DToArray API would return success for width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying hData(Phi)-->A_d device variable
// with height 0(copy will not be performed)
// 3.copying A_d-->hData and validating it with A_h data
SECTION("Height is 0") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, A_h, width,
width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width, 0, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(hData, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(hData, A_h, NUM_W, NUM_H) == true);
}
// hipMemcpy2DToArray API would return success for width and height as 0
// and does not perform any copy
// Validating the result with the initialized value
// 1.Initializing A_d with Pi value
// 2.copying hData(Phi)-->A_d device variable
// with width 0(copy will not be performed)
// 3.copying A_d-->hData and validating it with A_h data
SECTION("Width is 0") {
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, A_h, width,
width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
0, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(hData, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(hData, A_h, NUM_W, NUM_H) == true);
}
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DToArray API by copying the
* data from pinned host memory to device on same GPU
* INPUT: Copying Host variable PinnMem(Initialized with value "10" )
* --> A_d device variable
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with PinnedMem[0](i.e., 10)
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_PinnedHostMemSameGpu") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
constexpr auto def_val{10};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *PinnMem{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&PinnMem), width * NUM_H));
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
for (int i = 0; i < NUM_W*NUM_H; i++) {
PinnMem[i] = def_val + i;
}
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, PinnMem,
width, width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, PinnMem, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(PinnMem));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
}
/*
* This Scenario Verifies hipMemcpy2DToArray API by copying the
* data from pinned host memory to device from Peer GPU.
* Device Memory is allocated in GPU 0 and the API is trigerred from GPU1
* INPUT: Copying Host variable E_h(Initialized with value 10+i(numelements))
* --> A_d device variable
* whose memory is allocated in GPU 0
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with E_h[0]+i(i.e., 10+i)
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_multiDevicePinnedHostMem") {
int numDevices = 0;
constexpr auto def_val{10};
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *E_h{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, nullptr, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, nullptr, nullptr);
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&E_h), width * NUM_H));
for (int i = 0; i < NUM_W*NUM_H; i++) {
E_h[i] = def_val + i;
}
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, E_h, width,
width, NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipSetDevice(0));
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, E_h, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipHostFree(E_h));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, nullptr, nullptr, false);
} else {
SUCCEED("Machine Does not have P2P capability");
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/*
* This scenario verifies the hipMemcpy2DToArray API in case of device
* context change.
* Memory is allocated in GPU-0 and the API is triggered from GPU-1
* INPUT: Copying Host variable hData(Initial value Phi)
* --> A_d device variable
* whose memory is allocated in GPU 0
* OUTPUT: For validating the result,Copying A_d device variable
* --> A_h host variable
* and verifying A_h with Phi
* */
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_multiDeviceDeviceContextChange") {
int numDevices = 0;
HIP_CHECK(hipGetDeviceCount(&numDevices));
if (numDevices > 1) {
int canAccessPeer = 0;
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1));
if (canAccessPeer) {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
HIP_CHECK(hipSetDevice(1));
HIP_CHECK(hipStreamCreate(&stream));
HIP_CHECK(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width, width,
NUM_H, hipMemcpyHostToDevice,
stream));
HIP_CHECK(hipStreamSynchronize(stream));
HIP_CHECK(hipMemcpy2DFromArray(A_h, width, A_d,
0, 0, width, NUM_H,
hipMemcpyDeviceToHost));
REQUIRE(HipTest::checkArray(A_h, hData, NUM_W, NUM_H) == true);
// Cleaning the memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
} else {
SUCCEED("Machine Does not have P2P capability");
}
} else {
SUCCEED("Number of devices are < 2");
}
}
/* This testcase verifies the negative scenarios
*/
TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Negative") {
HIP_CHECK(hipSetDevice(0));
hipArray *A_d{nullptr};
size_t width{sizeof(float)*NUM_W};
float *A_h{nullptr}, *hData{nullptr};
hipStream_t stream;
HIP_CHECK(hipStreamCreate(&stream));
// Initialization of variables
HipTest::initArrays<float>(nullptr, nullptr, nullptr,
&A_h, &hData, nullptr,
width*NUM_H, false);
HipTest::setDefaultData<float>(width*NUM_H, A_h, hData, nullptr);
hipChannelFormatDesc desc = hipCreateChannelDesc<float>();
HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault));
SECTION("Nullptr to destination") {
REQUIRE(hipMemcpy2DToArrayAsync(nullptr, 0, 0, hData, width,
width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
}
SECTION("Nullptr to source") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 0, 0, nullptr,
width, width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
}
SECTION("Passing offset more than 0") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 1, 1, hData, width,
width, NUM_H, hipMemcpyHostToDevice,
stream) != hipSuccess);
}
SECTION("Passing array more than allocated") {
REQUIRE(hipMemcpy2DToArrayAsync(A_d, 0, 0, hData, width,
width+2, NUM_H+2, hipMemcpyHostToDevice,
stream) != hipSuccess);
}
// Cleaning of Memory
HIP_CHECK(hipFreeArray(A_d));
HIP_CHECK(hipStreamDestroy(stream));
HipTest::freeArrays<float>(nullptr, nullptr, nullptr,
A_h, hData, nullptr, false);
}