From f5eb0da5c11aecdaed4c40d8000aa1a28b3f7913 Mon Sep 17 00:00:00 2001 From: music-dino <111048524+music-dino@users.noreply.github.com> Date: Tue, 17 Jan 2023 07:55:43 +0100 Subject: [PATCH 01/14] EXSWHTEC-74 - Implement tests for hipMemcpy and derivatives (#15) - Basic positive tests - Negative parameter tests --- catch/include/memcpy1d_tests_common.hh | 316 +++++++++ catch/unit/memory/CMakeLists.txt | 6 + catch/unit/memory/hipMemcpy.cc | 650 ++----------------- catch/unit/memory/hipMemcpyWithStream.cc | 629 ++---------------- catch/unit/memory/hipMemcpyWithStream_old.cc | 586 +++++++++++++++++ catch/unit/memory/hipMemcpy_derivatives.cc | 119 ++++ catch/unit/memory/hipMemcpy_old.cc | 619 ++++++++++++++++++ 7 files changed, 1784 insertions(+), 1141 deletions(-) create mode 100644 catch/include/memcpy1d_tests_common.hh create mode 100644 catch/unit/memory/hipMemcpyWithStream_old.cc create mode 100644 catch/unit/memory/hipMemcpy_derivatives.cc create mode 100644 catch/unit/memory/hipMemcpy_old.cc diff --git a/catch/include/memcpy1d_tests_common.hh b/catch/include/memcpy1d_tests_common.hh new file mode 100644 index 0000000000..fe0d46788b --- /dev/null +++ b/catch/include/memcpy1d_tests_common.hh @@ -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 WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +#include + +#include +#include +#include +#include + +static inline unsigned int GenerateLinearAllocationFlagCombinations( + const LinearAllocs allocation_type) { + switch (allocation_type) { + case LinearAllocs::hipHostMalloc: + return GENERATE(hipHostMallocDefault, hipHostMallocPortable, hipHostMallocMapped, + hipHostMallocWriteCombined); + case LinearAllocs::mallocAndRegister: + case LinearAllocs::hipMallocManaged: + case LinearAllocs::malloc: + case LinearAllocs::hipMalloc: + return 0u; + default: + assert("Invalid LinearAllocs enumerator"); + throw std::invalid_argument("Invalid LinearAllocs enumerator"); + } +} + +template +void MemcpyDeviceToHostShell(F memcpy_func, const hipStream_t kernel_stream = nullptr) { + using LA = LinearAllocs; + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto host_allocation_type = GENERATE(LA::malloc, LA::hipHostMalloc); + const auto host_allocation_flags = GenerateLinearAllocationFlagCombinations(host_allocation_type); + + LinearAllocGuard host_allocation(host_allocation_type, allocation_size, + host_allocation_flags); + LinearAllocGuard device_allocation(LA::hipMalloc, allocation_size); + + const auto element_count = allocation_size / sizeof(*device_allocation.ptr()); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int expected_value = 42; + VectorSet<<>>(device_allocation.ptr(), + expected_value, element_count); + HIP_CHECK(hipGetLastError()); + + HIP_CHECK(memcpy_func(host_allocation.host_ptr(), device_allocation.ptr(), allocation_size)); + if constexpr (should_synchronize) { + HIP_CHECK(hipStreamSynchronize(kernel_stream)); + } + + ArrayFindIfNot(host_allocation.host_ptr(), expected_value, element_count); +} + +template +void MemcpyHostToDeviceShell(F memcpy_func, const hipStream_t kernel_stream = nullptr) { + using LA = LinearAllocs; + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto host_allocation_type = GENERATE(LA::malloc, LA::hipHostMalloc); + const auto host_allocation_flags = GenerateLinearAllocationFlagCombinations(host_allocation_type); + + LinearAllocGuard src_host_allocation(host_allocation_type, allocation_size, + host_allocation_flags); + LinearAllocGuard dst_host_allocation(LA::hipHostMalloc, allocation_size); + LinearAllocGuard device_allocation(LA::hipMalloc, allocation_size); + + const auto element_count = allocation_size / sizeof(*device_allocation.ptr()); + constexpr int fill_value = 42; + std::fill_n(src_host_allocation.host_ptr(), element_count, fill_value); + std::fill_n(dst_host_allocation.host_ptr(), element_count, 0); + + HIP_CHECK(memcpy_func(device_allocation.ptr(), src_host_allocation.host_ptr(), allocation_size)); + if constexpr (should_synchronize) { + HIP_CHECK(hipStreamSynchronize(kernel_stream)); + } + + HIP_CHECK(hipMemcpy(dst_host_allocation.host_ptr(), device_allocation.ptr(), allocation_size, + hipMemcpyDeviceToHost)); + + ArrayFindIfNot(dst_host_allocation.host_ptr(), fill_value, element_count); +} + +template +void MemcpyHostToHostShell(F memcpy_func, const hipStream_t kernel_stream = nullptr) { + using LA = LinearAllocs; + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const auto src_allocation_type = GENERATE(LA::malloc, LA::hipHostMalloc); + const auto dst_allocation_type = GENERATE(LA::malloc, LA::hipHostMalloc); + const auto src_allocation_flags = GenerateLinearAllocationFlagCombinations(src_allocation_type); + const auto dst_allocation_flags = GenerateLinearAllocationFlagCombinations(dst_allocation_type); + + LinearAllocGuard src_allocation(src_allocation_type, allocation_size, src_allocation_flags); + LinearAllocGuard dst_allocation(dst_allocation_type, allocation_size, dst_allocation_flags); + + const auto element_count = allocation_size / sizeof(*src_allocation.host_ptr()); + constexpr auto expected_value = 42; + std::fill_n(src_allocation.host_ptr(), element_count, expected_value); + + HIP_CHECK(memcpy_func(dst_allocation.host_ptr(), src_allocation.host_ptr(), allocation_size)); + if constexpr (should_synchronize) { + HIP_CHECK(hipStreamSynchronize(kernel_stream)); + } + + ArrayFindIfNot(dst_allocation.host_ptr(), expected_value, element_count); +} + +template +void MemcpyDeviceToDeviceShell(F memcpy_func, const hipStream_t kernel_stream = nullptr) { + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + 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 src_allocation(LinearAllocs::hipMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size, hipHostMallocPortable); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_allocation(LinearAllocs::hipMalloc, allocation_size); + + const auto element_count = allocation_size / sizeof(*src_allocation.ptr()); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int expected_value = 42; + HIP_CHECK(hipSetDevice(src_device)); + VectorSet<<>>(src_allocation.ptr(), expected_value, + element_count); + HIP_CHECK(hipGetLastError()); + + HIP_CHECK(memcpy_func(dst_allocation.ptr(), src_allocation.ptr(), allocation_size)); + if constexpr (should_synchronize) { + HIP_CHECK(hipStreamSynchronize(kernel_stream)); + } + + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_allocation.ptr(), allocation_size, 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(result.host_ptr(), expected_value, element_count); +} + +template void MemcpyWithDirectionCommonTests(F memcpy_func) { + using namespace std::placeholders; + SECTION("Device to host") { + MemcpyDeviceToHostShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyDeviceToHost)); + } + + SECTION("Device to host with default kind") { + MemcpyDeviceToHostShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyDefault)); + } + + SECTION("Host to device") { + MemcpyHostToDeviceShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyHostToDevice)); + } + + SECTION("Host to device with default kind") { + MemcpyHostToDeviceShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyDefault)); + } + + SECTION("Host to host") { + MemcpyHostToHostShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyHostToHost)); + } + + SECTION("Host to host with default kind") { + MemcpyHostToHostShell(std::bind(memcpy_func, _1, _2, _3, hipMemcpyDefault)); + } + + SECTION("Device to device") { + SECTION("Peer access enabled") { + MemcpyDeviceToDeviceShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyDeviceToDevice)); + } + SECTION("Peer access disabled") { + MemcpyDeviceToDeviceShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyDeviceToDevice)); + } + } + + SECTION("Device to device with default kind") { + SECTION("Peer access enabled") { + MemcpyDeviceToDeviceShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyDefault)); + } + SECTION("Peer access disabled") { + MemcpyDeviceToDeviceShell( + std::bind(memcpy_func, _1, _2, _3, hipMemcpyDefault)); + } + } +} + +// Synchronization behavior checks +template +void MemcpySyncBehaviorCheck(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); + } +} + +template +void MemcpyHtoDSyncBehavior(F memcpy_func, const bool should_sync, + const hipStream_t kernel_stream = nullptr) { + using LA = LinearAllocs; + const auto host_alloc_type = GENERATE(LA::malloc, LA::hipHostMalloc); + LinearAllocGuard host_alloc(host_alloc_type, kPageSize); + LinearAllocGuard device_alloc(LA::hipMalloc, kPageSize); + MemcpySyncBehaviorCheck(std::bind(memcpy_func, device_alloc.ptr(), host_alloc.ptr(), kPageSize), + should_sync, kernel_stream); +} + +template +void MemcpyDtoHPageableSyncBehavior(F memcpy_func, const bool should_sync, + const hipStream_t kernel_stream = nullptr) { + LinearAllocGuard host_alloc(LinearAllocs::malloc, kPageSize); + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + MemcpySyncBehaviorCheck(std::bind(memcpy_func, host_alloc.ptr(), device_alloc.ptr(), kPageSize), + should_sync, kernel_stream); +} + +template +void MemcpyDtoHPinnedSyncBehavior(F memcpy_func, const bool should_sync, + const hipStream_t kernel_stream = nullptr) { + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + MemcpySyncBehaviorCheck(std::bind(memcpy_func, host_alloc.ptr(), device_alloc.ptr(), kPageSize), + should_sync, kernel_stream); +} + +template +void MemcpyDtoDSyncBehavior(F memcpy_func, const bool should_sync, + const hipStream_t kernel_stream = nullptr) { + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); + MemcpySyncBehaviorCheck(std::bind(memcpy_func, dst_alloc.ptr(), src_alloc.ptr(), kPageSize), + should_sync, kernel_stream); +} + +template +void MemcpyHtoHSyncBehavior(F memcpy_func, const bool should_sync, + const hipStream_t kernel_stream = nullptr) { + using LA = LinearAllocs; + const auto src_alloc_type = GENERATE(LA::malloc, LA::hipHostMalloc); + const auto dst_alloc_type = GENERATE(LA::malloc, LA::hipHostMalloc); + + LinearAllocGuard src_alloc(src_alloc_type, kPageSize); + LinearAllocGuard dst_alloc(dst_alloc_type, kPageSize); + MemcpySyncBehaviorCheck(std::bind(memcpy_func, dst_alloc.ptr(), src_alloc.ptr(), kPageSize), + should_sync, kernel_stream); +} + +// Common negative tests +template void MemcpyCommonNegativeTests(F f, void* dst, void* src, size_t count) { + SECTION("dst == nullptr") { HIP_CHECK_ERROR(f(nullptr, src, count), hipErrorInvalidValue); } + SECTION("src == nullptr") { HIP_CHECK_ERROR(f(dst, nullptr, count), hipErrorInvalidValue); } +} + +template +void MemcpyWithDirectionCommonNegativeTests(F f, void* dst, void* src, size_t count, + hipMemcpyKind kind) { + using namespace std::placeholders; + MemcpyCommonNegativeTests(std::bind(f, _1, _2, _3, kind), dst, src, count); + +// Disabled on AMD due to defect - EXSWHTEC-128 +#if HT_NVIDIA + SECTION("Invalid MemcpyKind") { + HIP_CHECK_ERROR(f(dst, src, count, static_cast(-1)), + hipErrorInvalidMemcpyDirection); + } +#endif +} \ No newline at end of file diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index 1b4cef3698..2b846d7c41 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -65,6 +65,7 @@ set(TEST_SRC hipMemPoolApi.cc hipMemcpyPeer.cc hipMemcpyPeerAsync.cc + hipMemcpyWithStream_old.cc hipMemcpyWithStream.cc hipMemcpyWithStreamMultiThread.cc hipMemsetAsyncAndKernel.cc @@ -74,7 +75,9 @@ set(TEST_SRC hipMemcpyDtoD.cc hipMemcpyDtoDAsync.cc hipHostMalloc.cc + hipMemcpy_old.cc hipMemcpy.cc + hipMemcpy_derivatives.cc hipMemcpyAsync.cc hipMemsetFunctional.cc hipMallocPitch.cc @@ -142,6 +145,7 @@ set(TEST_SRC hipMemPoolApi.cc hipMemcpyPeer.cc hipMemcpyPeerAsync.cc + hipMemcpyWithStream_old.cc hipMemcpyWithStream.cc hipMemcpyWithStreamMultiThread.cc hipMemsetAsyncAndKernel.cc @@ -151,7 +155,9 @@ set(TEST_SRC hipMemcpyDtoD.cc hipMemcpyDtoDAsync.cc hipHostMalloc.cc + hipMemcpy_old.cc hipMemcpy.cc + hipMemcpy_derivatives.cc hipMemcpyAsync.cc hipMemsetFunctional.cc hipMallocPitch.cc diff --git a/catch/unit/memory/hipMemcpy.cc b/catch/unit/memory/hipMemcpy.cc index 84af63bea0..24e11c25f3 100644 --- a/catch/unit/memory/hipMemcpy.cc +++ b/catch/unit/memory/hipMemcpy.cc @@ -1,13 +1,15 @@ /* -Copyright (c) 2021 - present 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 WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -17,603 +19,83 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* -This testcase verifies following scenarios -1. hipMemcpy API along with kernel launch with different data types -2. H2D-D2D-D2H scenarios for unpinned and pinned memory -3. Boundary checks with different sizes -4. Multithread scenario -5. device offset scenario -*/ - #include -#include -#include +#include +#include +#include +#include -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#else -#include "sys/types.h" -#include "sys/sysinfo.h" -#endif - - -static constexpr auto NUM_ELM{4*1024 * 1024}; -static unsigned blocksPerCU{6}; // to hide latency -static unsigned threadsPerBlock{256}; - -template -class DeviceMemory { - public: - explicit DeviceMemory(size_t numElements); - DeviceMemory() = delete; - ~DeviceMemory(); - T* A_d() const { return _A_d + _offset; } - T* B_d() const { return _B_d + _offset; } - T* C_d() const { return _C_d + _offset; } - T* C_dd() const { return _C_dd + _offset; } - size_t maxNumElements() const { return _maxNumElements; } - void offset(int offset) { _offset = offset; } - int offset() const { return _offset; } - private: - T* _A_d; - T* _B_d; - T* _C_d; - T* _C_dd; - size_t _maxNumElements; - int _offset; -}; - -template -DeviceMemory::DeviceMemory(size_t numElements) : - _maxNumElements(numElements), _offset(0) { - T** np = nullptr; - HipTest::initArrays(&_A_d, &_B_d, &_C_d, np, np, np, numElements, 0); - size_t sizeElements = numElements * sizeof(T); - HIP_CHECK(hipMalloc(&_C_dd, sizeElements)); -} - - -template -DeviceMemory::~DeviceMemory() { - T* np = nullptr; - HipTest::freeArrays(_A_d, _B_d, _C_d, np, np, np, 0); - HIP_CHECK(hipFree(_C_dd)); - _C_dd = NULL; -} - -template -class HostMemory { - public: - HostMemory(size_t numElements, bool usePinnedHost); - HostMemory() = delete; - void reset(size_t numElements, bool full = false); - ~HostMemory(); - T* A_h() const { return _A_h + _offset; } - T* B_h() const { return _B_h + _offset; } - T* C_h() const { return _C_h + _offset; } - - size_t maxNumElements() const { return _maxNumElements; } - void offset(int offset) { _offset = offset; } - int offset() const { return _offset; } - - // Host arrays, secondary copy - T* A_hh; - T* B_hh; - bool _usePinnedHost; - - private: - size_t _maxNumElements; - int _offset; - - // Host arrays - T* _A_h; - T* _B_h; - T* _C_h; -}; - - template -HostMemory::HostMemory(size_t numElements, bool usePinnedHost) - : _usePinnedHost(usePinnedHost), _maxNumElements(numElements), _offset(0) { - T** np = nullptr; - HipTest::initArrays(np, np, np, &_A_h, &_B_h, &_C_h, - numElements, usePinnedHost); - - A_hh = NULL; - B_hh = NULL; - - - size_t sizeElements = numElements * sizeof(T); - - if (usePinnedHost) { - HIP_CHECK(hipHostMalloc(reinterpret_cast(&A_hh), sizeElements, - hipHostMallocDefault)); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&B_hh), sizeElements, - hipHostMallocDefault)); - } else { - A_hh = reinterpret_cast(malloc(sizeElements)); - B_hh = reinterpret_cast(malloc(sizeElements)); - } - } - -template -void HostMemory::reset(size_t numElements, bool full) { - // Initialize the host data: - for (size_t i = 0; i < numElements; i++) { - (A_hh)[i] = 1097.0 + i; - (B_hh)[i] = 1492.0 + i; // Phi - - if (full) { - (_A_h)[i] = 3.146f + i; // Pi - (_B_h)[i] = 1.618f + i; // Phi - } - } -} - -template -HostMemory::~HostMemory() { - HipTest::freeArraysForHost(_A_h, _B_h, _C_h, _usePinnedHost); - - if (_usePinnedHost) { - HIP_CHECK(hipHostFree(A_hh)); - HIP_CHECK(hipHostFree(B_hh)); - - } else { - free(A_hh); - free(B_hh); - } -} - -#ifdef _WIN32 -void memcpytest2_get_host_memory(size_t *free, size_t *total) { - MEMORYSTATUSEX status; - status.dwLength = sizeof(status); - GlobalMemoryStatusEx(&status); - // Windows doesn't allow allocating more than half of system memory to the gpu - // Since the runtime also needs space for its internal allocations, - // we should not try to allocate more than 40% of reported system memory, - // otherwise we can run into OOM issues. - *free = static_cast(0.4 * status.ullAvailPhys); - *total = static_cast(0.4 * status.ullTotalPhys); -} -#else -struct sysinfo memInfo; -void memcpytest2_get_host_memory(size_t *free, size_t *total) { - sysinfo(&memInfo); - uint64_t freePhysMem = memInfo.freeram; - freePhysMem *= memInfo.mem_unit; - *free = freePhysMem; - uint64_t totalPhysMem = memInfo.totalram; - totalPhysMem *= memInfo.mem_unit; - *total = totalPhysMem; -} -#endif - -//--- -// Test many different kinds of memory copies. -// The subroutine allocates memory , copies to device, runs a vector -// add kernel, copies back, and -// checks the result. -// -// IN: numElements controls the number of elements used for allocations. -// IN: usePinnedHost : If true, allocate host with hipHostMalloc and is pinned -// else allocate host -// memory with malloc. IN: useHostToHost : If true, add an extra -// host-to-host copy. IN: -// useDeviceToDevice : If true, add an extra deviceto-device copy after -// result is produced. IN: -// useMemkindDefault : If true, use memkinddefault -// (runtime figures out direction). if false, use -// explicit memcpy direction. -// -template -void memcpytest2(DeviceMemory* dmem, HostMemory* hmem, - size_t numElements, bool useHostToHost, - bool useDeviceToDevice, bool useMemkindDefault) { - size_t sizeElements = numElements * sizeof(T); - - hmem->reset(numElements); - - assert(numElements <= dmem->maxNumElements()); - assert(numElements <= hmem->maxNumElements()); - - - if (useHostToHost) { - // Do some extra host-to-host copies here to mix things up: - HIP_CHECK(hipMemcpy(hmem->A_hh, hmem->A_h(), sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToHost)); - HIP_CHECK(hipMemcpy(hmem->B_hh, hmem->B_h(), sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToHost)); - - - HIP_CHECK(hipMemcpy(dmem->A_d(), hmem->A_hh, sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(dmem->B_d(), hmem->B_hh, sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); - } else { - HIP_CHECK(hipMemcpy(dmem->A_d(), hmem->A_h(), sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(dmem->B_d(), hmem->B_h(), sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); - } - - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), 0, 0, - static_cast(dmem->A_d()), static_cast(dmem->B_d()), - dmem->C_d(), numElements); - HIP_CHECK(hipGetLastError()); - - if (useDeviceToDevice) { - // Do an extra device-to-device copy here to mix things up: - HIP_CHECK(hipMemcpy(dmem->C_dd(), dmem->C_d(), sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyDeviceToDevice)); - - // Destroy the original dmem->C_d(): - HIP_CHECK(hipMemset(dmem->C_d(), 0x5A, sizeElements)); - - HIP_CHECK(hipMemcpy(hmem->C_h(), dmem->C_dd(), sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyDeviceToHost)); - } else { - HIP_CHECK(hipMemcpy(hmem->C_h(), dmem->C_d(), sizeElements, - useMemkindDefault ? hipMemcpyDefault : hipMemcpyDeviceToHost)); - } +TEST_CASE("Unit_hipMemcpy_Positive_Basic") { MemcpyWithDirectionCommonTests(hipMemcpy); } +TEST_CASE("Unit_hipMemcpy_Positive_Synchronization_Behavior") { + using namespace std::placeholders; HIP_CHECK(hipDeviceSynchronize()); - HipTest::checkVectorADD(hmem->A_h(), hmem->B_h(), hmem->C_h(), numElements); + // For transfers from pageable host memory to device memory, a stream sync is performed before + // the copy is initiated. The function will return once the pageable buffer has been copied to + // the staging memory for DMA transfer to device memory, but the DMA to final destination may + // not have completed. + // For transfers from pinned host memory to device memory, the function is synchronous with + // respect to the host + SECTION("Host memory to device memory") { + MemcpyHtoDSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyHostToDevice), true); + } - printf(" %s success\n", __func__); -} + // For transfers from device to either pageable or pinned host memory, the function returns only + // once the copy has completed + SECTION("Device memory to host memory") { + const auto f = std::bind(hipMemcpy, _1, _2, _3, hipMemcpyDeviceToHost); + MemcpyDtoHPageableSyncBehavior(f, true); + MemcpyDtoHPinnedSyncBehavior(f, true); + } -// Try all the 16 possible combinations to memcpytest2 - usePinnedHost, -// useHostToHost, -// useDeviceToDevice, useMemkindDefault -template -void memcpytest2_for_type(size_t numElements) { - DeviceMemory memD(numElements); - HostMemory memU(numElements, 0 /*usePinnedHost*/); - HostMemory memP(numElements, 1 /*usePinnedHost*/); + // For transfers from device memory to device memory, no host-side synchronization is performed. + SECTION("Device memory to device memory") { + // This behavior differs on NVIDIA and AMD, on AMD the hipMemcpy calls is synchronous with + // respect to the host +#if HT_AMD + HipTest::HIP_SKIP_TEST( + "EXSWCPHIPT-127 - Memcpy from device to device memory behavior differs on AMD and Nvidia"); + return; +#endif + MemcpyDtoDSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyDeviceToDevice), false); + } - for (int usePinnedHost = 0; usePinnedHost <= 1; usePinnedHost++) { - for (int useHostToHost = 0; useHostToHost <= 1; useHostToHost++) { - for (int useDeviceToDevice = 0; useDeviceToDevice <= 1; - useDeviceToDevice++) { - for (int useMemkindDefault = 0; useMemkindDefault <= 1; - useMemkindDefault++) { - memcpytest2(&memD, usePinnedHost ? &memP : &memU, - numElements, useHostToHost, - useDeviceToDevice, useMemkindDefault); - } - } - } + // For transfers from any host memory to any host memory, the function is fully synchronous with + // respect to the host + SECTION("Host memory to host memory") { + MemcpyHtoHSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyHostToHost), true); } } -// Try many different sizes to memory copy. -template -void memcpytest2_sizes(size_t maxElem = 0) { - int deviceId; - HIP_CHECK(hipGetDevice(&deviceId)); +TEST_CASE("Unit_hipMemcpy_Negative_Parameters") { + using namespace std::placeholders; - size_t free, total, freeCPU, totalCPU; - HIP_CHECK(hipMemGetInfo(&free, &total)); - memcpytest2_get_host_memory(&freeCPU, &totalCPU); - - if (maxElem == 0) { - // Use lesser maxElem if not enough host memory available - size_t maxElemGPU = free / sizeof(T) / 8; - size_t maxElemCPU = freeCPU / sizeof(T) / 8; - maxElem = maxElemGPU < maxElemCPU ? maxElemGPU : maxElemCPU; + SECTION("Host to device") { + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, device_alloc.ptr(), host_alloc.ptr(), + kPageSize, hipMemcpyHostToDevice); } - HIP_CHECK(hipDeviceReset()); - DeviceMemory memD(maxElem); - HostMemory memU(maxElem, 0 /*usePinnedHost*/); - HostMemory memP(maxElem, 1 /*usePinnedHost*/); + SECTION("Device to host") { + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, host_alloc.ptr(), device_alloc.ptr(), + kPageSize, hipMemcpyDeviceToHost); + } - for (size_t elem = 1; elem <= maxElem; elem *= 2) { - memcpytest2(&memD, &memU, elem, 1, 1, 0); // unpinned host - memcpytest2(&memD, &memP, elem, 1, 1, 0); // pinned host - } -} - -// Try many different sizes to memory copy. -template -void memcpytest2_offsets(size_t maxElem, bool devOffsets, bool hostOffsets) { - int deviceId; - HIP_CHECK(hipGetDevice(&deviceId)); - - size_t free, total; - HIP_CHECK(hipMemGetInfo(&free, &total)); - - HIP_CHECK(hipDeviceReset()); - DeviceMemory memD(maxElem); - HostMemory memU(maxElem, 0 /*usePinnedHost*/); - HostMemory memP(maxElem, 1 /*usePinnedHost*/); - - size_t elem = maxElem / 2; - - for (size_t offset = 0; offset < 512; offset++) { - assert(elem + offset < maxElem); - if (devOffsets) { - memD.offset(offset); - } - if (hostOffsets) { - memU.offset(offset); - memP.offset(offset); - } - memcpytest2(&memD, &memU, elem, 1, 1, 0); // unpinned host - memcpytest2(&memD, &memP, elem, 1, 1, 0); // pinned host - } - - for (size_t offset = 512; offset < elem; offset *= 2) { - assert(elem + offset < maxElem); - if (devOffsets) { - memD.offset(offset); - } - if (hostOffsets) { - memU.offset(offset); - memP.offset(offset); - } - memcpytest2(&memD, &memU, elem, 1, 1, 0); // unpinned host - memcpytest2(&memD, &memP, elem, 1, 1, 0); // pinned host - } -} - -// Create multiple threads to stress multi-thread locking behavior in the -// allocation/deallocation/tracking logic: -template -void multiThread_1(bool serialize, bool usePinnedHost) { - DeviceMemory memD(NUM_ELM); - HostMemory mem1(NUM_ELM, usePinnedHost); - HostMemory mem2(NUM_ELM, usePinnedHost); - - std::thread t1(memcpytest2, &memD, &mem1, NUM_ELM, 0, 0, 0); - if (serialize) { - t1.join(); - } - - - std::thread t2(memcpytest2, &memD, &mem2, NUM_ELM, 0, 0, 0); - if (serialize) { - t2.join(); - } -} - - - -/* -This testcase verifies hipMemcpy API -Initializes device variables -Launches kernel and performs the sum of device variables -copies the result to host variable and validates the result. -*/ -TEMPLATE_TEST_CASE("Unit_hipMemcpy_KernelLaunch", "", int, float, - double) { - size_t Nbytes = NUM_ELM * sizeof(TestType); - - TestType *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - TestType *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; - - HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, NUM_ELM, false); - - HIP_CHECK(hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(B_d, B_h, Nbytes, hipMemcpyHostToDevice)); - - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), 0, 0, - static_cast(A_d), - static_cast(B_d), C_d, NUM_ELM); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); - - HIP_CHECK(hipDeviceSynchronize()); - HipTest::checkVectorADD(A_h, B_h, C_h, NUM_ELM); - - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); -} - -/* -This testcase verifies the following scenarios -1. H2H,H2PinMem and PinnedMem2Host -2. H2D-D2D-D2H in same GPU -3. Pinned Host Memory to device variables in same GPU -4. Device context change -5. H2D-D2D-D2H peer GPU -*/ -TEMPLATE_TEST_CASE("Unit_hipMemcpy_H2H-H2D-D2H-H2PinMem", "", int, - float, double) { - TestType *A_d{nullptr}, *B_d{nullptr}; - TestType *A_h{nullptr}, *B_h{nullptr}; - TestType *A_Ph{nullptr}, *B_Ph{nullptr}; - HIP_CHECK(hipSetDevice(0)); - HipTest::initArrays(&A_d, &B_d, nullptr, - &A_h, &B_h, nullptr, - NUM_ELM*sizeof(TestType)); - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_Ph, &B_Ph, nullptr, - NUM_ELM*sizeof(TestType), true); - - SECTION("H2H, H2PinMem and PinMem2H") { - HIP_CHECK(hipMemcpy(B_h, A_h, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(A_Ph, B_h, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_Ph, A_Ph, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HipTest::checkTest(A_h, B_Ph, NUM_ELM); - } - - SECTION("H2D-D2D-D2H-SameGPU") { - HIP_CHECK(hipMemcpy(A_d, A_h, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_d, A_d, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_h, B_d, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); - HipTest::checkTest(A_h, B_h, NUM_ELM); - } - - SECTION("pH2D-D2D-D2pH-SameGPU") { - HIP_CHECK(hipMemcpy(A_d, A_Ph, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_d, A_d, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_Ph, B_d, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HipTest::checkTest(A_Ph, B_Ph, NUM_ELM); - } - SECTION("H2D-D2D-D2H-DeviceContextChange") { - int deviceCount = 0; - HIP_CHECK(hipGetDeviceCount(&deviceCount)); - if (deviceCount < 2) { - SUCCEED("deviceCount less then 2"); - } else { - int canAccessPeer = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipMemcpy(A_d, A_h, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_d, A_d, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_h, B_d, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HipTest::checkTest(A_h, B_h, NUM_ELM); - } else { - SUCCEED("P2P capability is not present"); - } - } - } - - SECTION("H2D-D2D-D2H-PeerGPU") { - int deviceCount = 0; - HIP_CHECK(hipGetDeviceCount(&deviceCount)); - if (deviceCount < 2) { - SUCCEED("deviceCount less then 2"); - } else { - int canAccessPeer = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - HIP_CHECK(hipSetDevice(1)); - TestType *C_d{nullptr}; - HipTest::initArrays(nullptr, nullptr, &C_d, - nullptr, nullptr, nullptr, - NUM_ELM*sizeof(TestType)); - HIP_CHECK(hipMemcpy(A_d, A_h, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(C_d, A_d, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HIP_CHECK(hipMemcpy(B_h, C_d, NUM_ELM*sizeof(TestType), - hipMemcpyDefault)); - HipTest::checkTest(A_h, B_h, NUM_ELM); - HIP_CHECK(hipFree(C_d)); - } else { - SUCCEED("P2P capability is not present"); - } - } - } - - HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); - HipTest::freeArrays(nullptr, nullptr, nullptr, A_Ph, - B_Ph, nullptr, true); -} -/* -This testcase verifies the multi thread scenario -*/ -TEST_CASE("Unit_hipMemcpy_MultiThreadWithSerialization") { - HIP_CHECK(hipDeviceReset()); - - // Simplest cases: serialize the threads, and also used pinned memory: - // This verifies that the sub-calls to memcpytest2 are correct. - multiThread_1(true, true); - - // Serialize, but use unpinned memory to stress the unpinned memory xfer path. - multiThread_1(true, false); -} - -/* -This testcase verifies hipMemcpy API with pinnedMemory and hostRegister -along with kernel launches -*/ - -TEMPLATE_TEST_CASE("Unit_hipMemcpy_PinnedRegMemWithKernelLaunch", - "", int, float, double) { - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices < 2) { - SUCCEED("No of devices are less than 2"); - } else { - // 1 refers to pinned Memory - // 2 refers to register Memory - int MallocPinType = GENERATE(0, 1); - size_t Nbytes = NUM_ELM * sizeof(TestType); - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, - threadsPerBlock, NUM_ELM); - - TestType *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - TestType *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; - TestType *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; - if (MallocPinType) { - HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, NUM_ELM, true); - } else { - A_h = reinterpret_cast(malloc(Nbytes)); - HIP_CHECK(hipHostRegister(A_h, Nbytes, hipHostRegisterDefault)); - B_h = reinterpret_cast(malloc(Nbytes)); - HIP_CHECK(hipHostRegister(B_h, Nbytes, hipHostRegisterDefault)); - C_h = reinterpret_cast(malloc(Nbytes)); - HIP_CHECK(hipHostRegister(C_h, Nbytes, hipHostRegisterDefault)); - HipTest::initArrays(&A_d, &B_d, &C_d, nullptr, nullptr, - nullptr, NUM_ELM, false); - HipTest::setDefaultData(NUM_ELM, A_h, B_h, C_h); - } - HIP_CHECK(hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(B_d, B_h, Nbytes, hipMemcpyHostToDevice)); - - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), - 0, 0, static_cast(A_d), - static_cast(B_d), C_d, NUM_ELM); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h, B_h, C_h, NUM_ELM); - - unsigned int seed = time(0); - HIP_CHECK(hipSetDevice(HipTest::RAND_R(&seed) % (numDevices-1)+1)); - - int device; - HIP_CHECK(hipGetDevice(&device)); - std::cout <<"hipMemcpy is set to happen between device 0 and device " - <(&X_d, &Y_d, &Z_d, nullptr, - nullptr, nullptr, NUM_ELM, false); - - for (int j = 0; j < NUM_ELM; j++) { - A_h[j] = 0; - B_h[j] = 0; - C_h[j] = 0; - } - - HIP_CHECK(hipMemcpy(A_h, A_d, Nbytes, hipMemcpyDeviceToHost)); - HIP_CHECK(hipMemcpy(X_d, A_h, Nbytes, hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(B_h, B_d, Nbytes, hipMemcpyDeviceToHost)); - HIP_CHECK(hipMemcpy(Y_d, B_h, Nbytes, hipMemcpyHostToDevice)); - - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), - 0, 0, static_cast(X_d), - static_cast(Y_d), Z_d, NUM_ELM); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, Z_d, Nbytes, hipMemcpyDeviceToHost)); - - HipTest::checkVectorADD(A_h, B_h, C_h, NUM_ELM); - - if (MallocPinType) { - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, true); - } else { - HIP_CHECK(hipHostUnregister(A_h)); - free(A_h); - HIP_CHECK(hipHostUnregister(B_h)); - free(B_h); - HIP_CHECK(hipHostUnregister(C_h)); - free(C_h); - HipTest::freeArrays(A_d, B_d, C_d, nullptr, - nullptr, nullptr, false); - } - HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, - nullptr, nullptr, false); + SECTION("Host to host") { + LinearAllocGuard src_alloc(LinearAllocs::hipHostMalloc, kPageSize); + LinearAllocGuard dst_alloc(LinearAllocs::hipHostMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, dst_alloc.ptr(), src_alloc.ptr(), kPageSize, + hipMemcpyHostToHost); + } + + SECTION("Device to device") { + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, dst_alloc.ptr(), src_alloc.ptr(), kPageSize, + hipMemcpyDeviceToDevice); } } diff --git a/catch/unit/memory/hipMemcpyWithStream.cc b/catch/unit/memory/hipMemcpyWithStream.cc index 5efc690b8d..24e11c25f3 100644 --- a/catch/unit/memory/hipMemcpyWithStream.cc +++ b/catch/unit/memory/hipMemcpyWithStream.cc @@ -1,13 +1,15 @@ /* -Copyright (c) 2021-22-present 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 WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -17,570 +19,83 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* - * Different test for checking functionality of - * hipError_t hipMemcpyWithStream(void* dst, const void* src, size_t sizeBytes,hipMemcpyKind kind, - * hipStream_t stream); - */ -/* -This testfile verifies the following scenarios -1. hipMemcpyWithStream with one stream -2. hipMemcpyWithStream with two streams -3. Multi GPU and single stream -4. hipMemcpyWithStream API with testkind DtoH -5. hipMemcpyWithStream API with testkind DtoD -6. hipMemcpyWithStream API with testkind HtoH -7. hipMemcpyWithStream API with testkind TestkindDefault -8. hipMemcpyWithStream API with testkind TestkindDefaultForDtoD -9. hipMemcpyWithStream API DtoD on same device -*/ - - #include -#include -#include +#include +#include +#include +#include -#include -#include -#include +TEST_CASE("Unit_hipMemcpy_Positive_Basic") { MemcpyWithDirectionCommonTests(hipMemcpy); } -#define LEN 64 -#define SIZE LEN << 2 -#define THREADS 2 -#define MAX_THREADS 16 +TEST_CASE("Unit_hipMemcpy_Positive_Synchronization_Behavior") { + using namespace std::placeholders; + HIP_CHECK(hipDeviceSynchronize()); -static constexpr size_t N{4 * 1024 * 1024}; -static const auto MaxGPUDevices{256}; -static constexpr unsigned blocksPerCU{6}; // to hide latency -static constexpr unsigned threadsPerBlock{256}; - -enum class ops -{ TestwithOnestream, - TestwithTwoStream, - TestOnMultiGPUwithOneStream, - TestkindDtoH, - TestkindDtoD, - TestkindHtoH, - TestkindDefault, - TestkindDefaultForDtoD, - TestDtoDonSameDevice, - END_OF_LIST -}; - -struct joinable_thread : std::thread { - template - explicit joinable_thread(Xs&&... xs) : std::thread(std::forward(xs)...) - {} // NOLINT - - joinable_thread& operator=(joinable_thread&& other) = default; - joinable_thread(joinable_thread&& other) = default; - - ~joinable_thread() { - if (this->joinable()) - this->join(); - } -}; - -void TestwithOnestream(void) { - size_t Nbytes = N * sizeof(int); - int *A_d, *B_d, *C_d; - int *A_h, *B_h, *C_h; - - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); - - hipStream_t stream; - HIP_CHECK(hipStreamCreate(&stream)); - - HIP_CHECK(hipMemcpyWithStream(A_d, A_h, Nbytes, - hipMemcpyHostToDevice, stream)); - HIP_CHECK(hipMemcpyWithStream(B_d, B_h, Nbytes, - hipMemcpyHostToDevice, stream)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), - 0, stream, static_cast(A_d), - static_cast(B_d), C_d, N); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipStreamSynchronize(stream)); - HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h, B_h, C_h, N); - - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HIP_CHECK(hipStreamDestroy(stream)); -} - -void TestwithTwoStream(void) { - size_t Nbytes = N * sizeof(int); - const int NUM_STREAMS = 2; - int *A_d[NUM_STREAMS], *B_d[NUM_STREAMS], *C_d[NUM_STREAMS]; - int *A_h[NUM_STREAMS], *B_h[NUM_STREAMS], *C_h[NUM_STREAMS]; - - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - - for (int i=0; i < NUM_STREAMS; ++i) { - HipTest::initArrays(&A_d[i], &B_d[i], &C_d[i], - &A_h[i], &B_h[i], &C_h[i], N, false); + // For transfers from pageable host memory to device memory, a stream sync is performed before + // the copy is initiated. The function will return once the pageable buffer has been copied to + // the staging memory for DMA transfer to device memory, but the DMA to final destination may + // not have completed. + // For transfers from pinned host memory to device memory, the function is synchronous with + // respect to the host + SECTION("Host memory to device memory") { + MemcpyHtoDSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyHostToDevice), true); } - hipStream_t stream[NUM_STREAMS]; - for (int i=0; i < NUM_STREAMS; ++i) { - HIP_CHECK(hipStreamCreate(&stream[i])); + // For transfers from device to either pageable or pinned host memory, the function returns only + // once the copy has completed + SECTION("Device memory to host memory") { + const auto f = std::bind(hipMemcpy, _1, _2, _3, hipMemcpyDeviceToHost); + MemcpyDtoHPageableSyncBehavior(f, true); + MemcpyDtoHPinnedSyncBehavior(f, true); } - for (int i=0; i < NUM_STREAMS; ++i) { - HIP_CHECK(hipMemcpyWithStream(A_d[i], A_h[i], Nbytes, - hipMemcpyHostToDevice, stream[i])); - HIP_CHECK(hipMemcpyWithStream(B_d[i], B_h[i], Nbytes, - hipMemcpyHostToDevice, stream[i])); - } - - for (int i=0; i < NUM_STREAMS; ++i) { - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), - 0, stream[i], static_cast(A_d[i]), - static_cast(B_d[i]), C_d[i], N); - HIP_CHECK(hipGetLastError()); - } - - for (int i=0; i < NUM_STREAMS; ++i) { - HIP_CHECK(hipStreamSynchronize(stream[i])); - HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h[i], B_h[i], C_h[i], N); - } - - for (int i=0; i < NUM_STREAMS; ++i) { - HipTest::freeArrays(A_d[i], B_d[i], C_d[i], A_h[i], B_h[i], C_h[i], false); - HIP_CHECK(hipStreamDestroy(stream[i])); - } -} - -void TestDtoDonSameDevice(void) { - size_t Nbytes = N * sizeof(int); - const int NUM_STREAMS = 2; - int *A_d[NUM_STREAMS], *B_d[NUM_STREAMS], *C_d[NUM_STREAMS]; - int *A_h[NUM_STREAMS], *B_h[NUM_STREAMS], *C_h[NUM_STREAMS]; - - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - - HipTest::initArrays(&A_d[0], &B_d[0], &C_d[0], - &A_h[0], &B_h[0], &C_h[0], N, false); - - - hipStream_t stream[NUM_STREAMS]; - for (int i=0; i < NUM_STREAMS; ++i) { - HIP_CHECK(hipSetDevice(0)); - HIP_CHECK(hipStreamCreate(&stream[i])); - } - - HIP_CHECK(hipSetDevice(0)); - HIP_CHECK(hipMalloc(&A_d[1], Nbytes)); - HIP_CHECK(hipMalloc(&B_d[1], Nbytes)); - HIP_CHECK(hipMalloc(&C_d[1], Nbytes)); - C_h[1] = reinterpret_cast(malloc(Nbytes)); - HIP_ASSERT(C_h[1] != NULL); - - HIP_CHECK(hipMemcpyWithStream(A_d[0], A_h[0], Nbytes, - hipMemcpyHostToDevice, stream[0])); - HIP_CHECK(hipMemcpyWithStream(B_d[0], B_h[0], Nbytes, - hipMemcpyHostToDevice, stream[0])); - - HIP_CHECK(hipMemcpyWithStream(A_d[1], A_d[0], Nbytes, - hipMemcpyDeviceToDevice, stream[1])); - HIP_CHECK(hipMemcpyWithStream(B_d[1], B_d[0], Nbytes, - hipMemcpyDeviceToDevice, stream[1])); - - - for (int i=0; i < NUM_STREAMS; ++i) { - HIP_CHECK(hipSetDevice(0)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), - 0, stream[i], static_cast(A_d[i]), - static_cast(B_d[i]), C_d[i], N); - HIP_CHECK(hipGetLastError()); - } - - for (int i=0; i < NUM_STREAMS; ++i) { - HIP_CHECK(hipSetDevice(0)); - HIP_CHECK(hipStreamSynchronize(stream[i])); - HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h[0], B_h[0], C_h[i], N); - } - - - HipTest::freeArrays(A_d[0], B_d[0], C_d[0], A_h[0], B_h[0], C_h[0], false); - - if (A_d[1]) { - HIP_CHECK(hipFree(A_d[1])); - } - if (B_d[1]) { - HIP_CHECK(hipFree(B_d[1])); - } - if (C_d[1]) { - HIP_CHECK(hipFree(C_d[1])); - } - if (C_h[1]) { - free(C_h[1]); - } - - - for (int i=0; i < NUM_STREAMS; ++i) { - HIP_CHECK(hipStreamDestroy(stream[i])); - } -} - -void TestOnMultiGPUwithOneStream(void) { - size_t Nbytes = N * sizeof(int); - int NumDevices = 0; - - HIP_CHECK(hipGetDeviceCount(&NumDevices)); - // If you have single GPU machine the return - if (NumDevices <= 1) { - SUCCEED("NumDevices <2"); - } else { - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - int *A_d[MaxGPUDevices], *B_d[MaxGPUDevices], *C_d[MaxGPUDevices]; - int *A_h[MaxGPUDevices], *B_h[MaxGPUDevices], *C_h[MaxGPUDevices]; - - hipStream_t stream[MaxGPUDevices]; - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipStreamCreate(&stream[i])); - } - - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HipTest::initArrays(&A_d[i], &B_d[i], &C_d[i], - &A_h[i], &B_h[i], &C_h[i], N, false); - } - - - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipMemcpyWithStream(A_d[i], A_h[i], Nbytes, - hipMemcpyHostToDevice, stream[i])); - HIP_CHECK(hipMemcpyWithStream(B_d[i], B_h[i], Nbytes, - hipMemcpyHostToDevice, stream[i])); - } - - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), - dim3(threadsPerBlock), 0, stream[i], - static_cast(A_d[i]), - static_cast(B_d[i]), C_d[i], N); - HIP_CHECK(hipGetLastError()); - } - - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipStreamSynchronize(stream[i])); - HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h[i], B_h[i], C_h[i], N); - } - - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HipTest::freeArrays(A_d[i], B_d[i], C_d[i], - A_h[i], B_h[i], C_h[i], false); - HIP_CHECK(hipStreamDestroy(stream[i])); - } - } -} - -void TestkindDtoH(void) { - size_t Nbytes = N * sizeof(int); - int *A_d, *B_d, *C_d; - int *A_h, *B_h, *C_h; - - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); - - hipStream_t stream; - HIP_CHECK(hipStreamCreate(&stream)); - - HIP_CHECK(hipMemcpyWithStream(A_d, A_h, Nbytes, - hipMemcpyHostToDevice, stream)); - HIP_CHECK(hipMemcpyWithStream(B_d, B_h, Nbytes, - hipMemcpyHostToDevice, stream)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), - 0, stream, static_cast(A_d), - static_cast(B_d), C_d, N); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipStreamSynchronize(stream)); - HIP_CHECK(hipMemcpyWithStream(C_h, C_d, Nbytes, - hipMemcpyDeviceToHost, stream)); - HipTest::checkVectorADD(A_h, B_h, C_h, N); - - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HIP_CHECK(hipStreamDestroy(stream)); -} - -void TestkindDtoD(void) { - size_t Nbytes = N * sizeof(int); - int NumDevices = 0; - - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - HIP_CHECK(hipGetDeviceCount(&NumDevices)); - // If you have single GPU machine the return - if (NumDevices <= 1) { - SUCCEED("NumDevices are less than 2"); - } else { - int *A_d[MaxGPUDevices], *B_d[MaxGPUDevices], *C_d[MaxGPUDevices]; - int *A_h[MaxGPUDevices], *B_h[MaxGPUDevices], *C_h[MaxGPUDevices]; - - hipStream_t stream[MaxGPUDevices]; - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipStreamCreate(&stream[i])); - } - - // Initialize and create the host and device elements for first device - HIP_CHECK(hipSetDevice(0)); - HipTest::initArrays(&A_d[0], &B_d[0], &C_d[0], - &A_h[0], &B_h[0], &C_h[0], N, false); - - for (int i=1; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)) - HIP_CHECK(hipMalloc(&A_d[i], Nbytes)); - HIP_CHECK(hipMalloc(&B_d[i], Nbytes)); - HIP_CHECK(hipMalloc(&C_d[i], Nbytes)); - C_h[i] = reinterpret_cast(malloc(Nbytes)); - HIP_ASSERT(C_h[i] != NULL); - } - - HIP_CHECK(hipSetDevice(0)); - HIP_CHECK(hipMemcpyWithStream(A_d[0], A_h[0], Nbytes, - hipMemcpyHostToDevice, stream[0])); - HIP_CHECK(hipMemcpyWithStream(B_d[0], B_h[0], Nbytes, - hipMemcpyHostToDevice, stream[0])); - - // Copying device data from 1st GPU to the rest of the the GPUs that is - // NumDevices in the setup. 1st GPU start numbering from 0,1,2..n etc. - for (int i=1; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipMemcpyWithStream(A_d[i], A_d[0], Nbytes, - hipMemcpyDeviceToDevice, stream[i])); - HIP_CHECK(hipMemcpyWithStream(B_d[i], B_d[0], Nbytes, - hipMemcpyDeviceToDevice, stream[i])); - } - - - // Launching the kernel including the 1st GPU to the no of GPUs present - // in the setup. 1st GPU start numbering from 0,1,2..n etc. - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), - dim3(threadsPerBlock), - 0, stream[i], static_cast(A_d[i]), - static_cast(B_d[i]), C_d[i], N); - HIP_CHECK(hipGetLastError()); - } - - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipStreamSynchronize(stream[i])); - HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h[0], B_h[0], C_h[i], N); - } - - HipTest::freeArrays(A_d[0], B_d[0], C_d[0], A_h[0], B_h[0], C_h[0], false); - HIP_CHECK(hipStreamDestroy(stream[0])); - - for (int i=1; i < NumDevices; ++i) { - if (A_d[i]) { - HIP_CHECK(hipFree(A_d[i])); - } - if (B_d[i]) { - HIP_CHECK(hipFree(B_d[i])); - } - if (C_d[i]) { - HIP_CHECK(hipFree(C_d[i])); - } - if (C_h[i]) { - free(C_h[i]); - } - HIP_CHECK(hipStreamDestroy(stream[i])); - } - } -} - -void TestkindDefault(void) { - size_t Nbytes = N * sizeof(int); - int *A_d, *B_d, *C_d; - int *A_h, *B_h, *C_h; - - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); - - hipStream_t stream; - HIP_CHECK(hipStreamCreate(&stream)); - - HIP_CHECK(hipMemcpyWithStream(A_d, A_h, Nbytes, hipMemcpyDefault, stream)); - HIP_CHECK(hipMemcpyWithStream(B_d, B_h, Nbytes, hipMemcpyDefault, stream)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), - 0, stream, static_cast(A_d), - static_cast(B_d), C_d, N); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipStreamSynchronize(stream)); - HIP_CHECK(hipMemcpyWithStream(C_h, C_d, Nbytes, hipMemcpyDefault, stream)); - HipTest::checkVectorADD(A_h, B_h, C_h, N); - - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HIP_CHECK(hipStreamDestroy(stream)); -} - -void TestkindDefaultForDtoD(void) { - size_t Nbytes = N * sizeof(int); - int NumDevices = 0; - - unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); - HIP_CHECK(hipGetDeviceCount(&NumDevices)); - // Test case will not run on single GPU setup. - if (NumDevices <= 1) { - SUCCEED("No of Devices < 2"); - } else { - int *A_d[MaxGPUDevices], *B_d[MaxGPUDevices], *C_d[MaxGPUDevices]; - int *A_h[MaxGPUDevices], *B_h[MaxGPUDevices], *C_h[MaxGPUDevices]; - - // Initialize and create the host and device elements for first device - HIP_CHECK(hipSetDevice(0)); - HipTest::initArrays(&A_d[0], &B_d[0], &C_d[0], - &A_h[0], &B_h[0], &C_h[0], N, false); - - for (int i=1; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipMalloc(&A_d[i], Nbytes)); - HIP_CHECK(hipMalloc(&B_d[i], Nbytes)); - HIP_CHECK(hipMalloc(&C_d[i], Nbytes)); - C_h[i] = reinterpret_cast(malloc(Nbytes)); - HIP_ASSERT(C_h[i] != NULL); - } - - hipStream_t stream[MaxGPUDevices]; - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipStreamCreate(&stream[i])); - } - - HIP_CHECK(hipMemcpyWithStream(A_d[0], A_h[0], Nbytes, - hipMemcpyHostToDevice, stream[0])); - HIP_CHECK(hipMemcpyWithStream(B_d[0], B_h[0], Nbytes, - hipMemcpyHostToDevice, stream[0])); - - // Copying device data from 1st GPU to the rest of the the GPUs - // using hipMemcpyDefault kind that is NumDevices in the setup. - // 1st GPU start numbering from 0,1,2..n etc. - for (int i=1; i < NumDevices; ++i) { - HIP_CHECK(hipMemcpyWithStream(A_d[i], A_d[0], Nbytes, - hipMemcpyDefault, stream[i])); - HIP_CHECK(hipMemcpyWithStream(B_d[i], B_d[0], Nbytes, - hipMemcpyDefault, stream[i])); - } - - for (int i=0; i < NumDevices; ++i) { - hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), - dim3(threadsPerBlock), - 0, stream[i], static_cast(A_d[i]), - static_cast(B_d[i]), C_d[i], N); - HIP_CHECK(hipGetLastError()); - } - - for (int i=0; i < NumDevices; ++i) { - HIP_CHECK(hipSetDevice(i)); // hipMemcpy will be on this device - HIP_CHECK(hipStreamSynchronize(stream[i])); - HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); - // Output of each GPU is getting validated with input of 1st GPU. - HipTest::checkVectorADD(A_h[0], B_h[0], C_h[i], N); - } - - HipTest::freeArrays(A_d[0], B_d[0], C_d[0], A_h[0], B_h[0], C_h[0], false); - HIP_CHECK(hipStreamDestroy(stream[0])); - - for (int i=1; i < NumDevices; ++i) { - if (A_d[i]) { - HIP_CHECK(hipFree(A_d[i])); - } - if (B_d[i]) { - HIP_CHECK(hipFree(B_d[i])); - } - if (C_d[i]) { - HIP_CHECK(hipFree(C_d[i])); - } - if (C_h[i]) { - free(C_h[i]); - } - HIP_CHECK(hipStreamDestroy(stream[i])); - } - } -} - -void TestkindHtoH(void) { - size_t Nbytes = N * sizeof(int); - int *A_h, *B_h; - - - // Allocate memory to A_h and B_h - A_h = static_cast(malloc(Nbytes)); - HIP_ASSERT(A_h != NULL); - B_h = static_cast(malloc(Nbytes)); - HIP_ASSERT(B_h != NULL); - - for (size_t i = 0; i < N; ++i) { - if (A_h) { - (A_h)[i] = 3.146f + i; // Pi - } - } - - hipStream_t stream; - HIP_CHECK(hipStreamCreate(&stream)); - - HIP_CHECK(hipMemcpyWithStream(B_h, A_h, Nbytes, hipMemcpyHostToHost, stream)); - HIP_CHECK(hipStreamSynchronize(stream)); - - for (size_t i = 0; i < N; i++) { - HIP_ASSERT(A_h[i] == B_h[i]); - } - - if (A_h) { - free(A_h); - } - if (B_h) { - free(B_h); - } - HIP_CHECK(hipStreamDestroy(stream)); -} - - -TEST_CASE("Unit_hipMemcpyWithStream_TestWithOneStream") { - TestwithOnestream(); -} - -TEST_CASE("Unit_hipMemcpyWithStream_TestwithTwoStream") { - TestwithTwoStream(); -} - -TEST_CASE("Unit_hipMemcpyWithStream_TestkindDtoH") { - TestkindDtoH(); -} - -TEST_CASE("Unit_hipMemcpyWithStream_TestkindHtoH") { - TestkindHtoH(); -} - -TEST_CASE("Unit_hipMemcpyWithStream_TestkindDtoD") { - TestkindDtoD(); -} - -TEST_CASE("Unit_hipMemcpyWithStream_TestOnMultiGPUwithOneStream") { - TestOnMultiGPUwithOneStream(); -} - -TEST_CASE("Unit_hipMemcpyWithStream_TestkindDefault") { - TestkindDefault(); -} -#ifndef __HIP_PLATFORM_NVCC__ -TEST_CASE("Unit_hipMemcpyWithStream_TestkindDefaultForDtoD") { - TestkindDefaultForDtoD(); -} + // For transfers from device memory to device memory, no host-side synchronization is performed. + SECTION("Device memory to device memory") { + // This behavior differs on NVIDIA and AMD, on AMD the hipMemcpy calls is synchronous with + // respect to the host +#if HT_AMD + HipTest::HIP_SKIP_TEST( + "EXSWCPHIPT-127 - Memcpy from device to device memory behavior differs on AMD and Nvidia"); + return; #endif + MemcpyDtoDSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyDeviceToDevice), false); + } -TEST_CASE("Unit_hipMemcpyWithStream_TestDtoDonSameDevice") { - TestDtoDonSameDevice(); + // For transfers from any host memory to any host memory, the function is fully synchronous with + // respect to the host + SECTION("Host memory to host memory") { + MemcpyHtoHSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyHostToHost), true); + } +} + +TEST_CASE("Unit_hipMemcpy_Negative_Parameters") { + using namespace std::placeholders; + + SECTION("Host to device") { + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, device_alloc.ptr(), host_alloc.ptr(), + kPageSize, hipMemcpyHostToDevice); + } + + SECTION("Device to host") { + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, host_alloc.ptr(), device_alloc.ptr(), + kPageSize, hipMemcpyDeviceToHost); + } + + SECTION("Host to host") { + LinearAllocGuard src_alloc(LinearAllocs::hipHostMalloc, kPageSize); + LinearAllocGuard dst_alloc(LinearAllocs::hipHostMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, dst_alloc.ptr(), src_alloc.ptr(), kPageSize, + hipMemcpyHostToHost); + } + + SECTION("Device to device") { + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); + MemcpyWithDirectionCommonNegativeTests(hipMemcpy, dst_alloc.ptr(), src_alloc.ptr(), kPageSize, + hipMemcpyDeviceToDevice); + } } diff --git a/catch/unit/memory/hipMemcpyWithStream_old.cc b/catch/unit/memory/hipMemcpyWithStream_old.cc new file mode 100644 index 0000000000..5efc690b8d --- /dev/null +++ b/catch/unit/memory/hipMemcpyWithStream_old.cc @@ -0,0 +1,586 @@ +/* +Copyright (c) 2021-22-present 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. +*/ + +/* + * Different test for checking functionality of + * hipError_t hipMemcpyWithStream(void* dst, const void* src, size_t sizeBytes,hipMemcpyKind kind, + * hipStream_t stream); + */ +/* +This testfile verifies the following scenarios +1. hipMemcpyWithStream with one stream +2. hipMemcpyWithStream with two streams +3. Multi GPU and single stream +4. hipMemcpyWithStream API with testkind DtoH +5. hipMemcpyWithStream API with testkind DtoD +6. hipMemcpyWithStream API with testkind HtoH +7. hipMemcpyWithStream API with testkind TestkindDefault +8. hipMemcpyWithStream API with testkind TestkindDefaultForDtoD +9. hipMemcpyWithStream API DtoD on same device +*/ + + +#include +#include +#include + +#include +#include +#include + +#define LEN 64 +#define SIZE LEN << 2 +#define THREADS 2 +#define MAX_THREADS 16 + +static constexpr size_t N{4 * 1024 * 1024}; +static const auto MaxGPUDevices{256}; +static constexpr unsigned blocksPerCU{6}; // to hide latency +static constexpr unsigned threadsPerBlock{256}; + +enum class ops +{ TestwithOnestream, + TestwithTwoStream, + TestOnMultiGPUwithOneStream, + TestkindDtoH, + TestkindDtoD, + TestkindHtoH, + TestkindDefault, + TestkindDefaultForDtoD, + TestDtoDonSameDevice, + END_OF_LIST +}; + +struct joinable_thread : std::thread { + template + explicit joinable_thread(Xs&&... xs) : std::thread(std::forward(xs)...) + {} // NOLINT + + joinable_thread& operator=(joinable_thread&& other) = default; + joinable_thread(joinable_thread&& other) = default; + + ~joinable_thread() { + if (this->joinable()) + this->join(); + } +}; + +void TestwithOnestream(void) { + size_t Nbytes = N * sizeof(int); + int *A_d, *B_d, *C_d; + int *A_h, *B_h, *C_h; + + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); + + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + + HIP_CHECK(hipMemcpyWithStream(A_d, A_h, Nbytes, + hipMemcpyHostToDevice, stream)); + HIP_CHECK(hipMemcpyWithStream(B_d, B_h, Nbytes, + hipMemcpyHostToDevice, stream)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), + 0, stream, static_cast(A_d), + static_cast(B_d), C_d, N); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h, B_h, C_h, N); + + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HIP_CHECK(hipStreamDestroy(stream)); +} + +void TestwithTwoStream(void) { + size_t Nbytes = N * sizeof(int); + const int NUM_STREAMS = 2; + int *A_d[NUM_STREAMS], *B_d[NUM_STREAMS], *C_d[NUM_STREAMS]; + int *A_h[NUM_STREAMS], *B_h[NUM_STREAMS], *C_h[NUM_STREAMS]; + + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + + for (int i=0; i < NUM_STREAMS; ++i) { + HipTest::initArrays(&A_d[i], &B_d[i], &C_d[i], + &A_h[i], &B_h[i], &C_h[i], N, false); + } + + hipStream_t stream[NUM_STREAMS]; + for (int i=0; i < NUM_STREAMS; ++i) { + HIP_CHECK(hipStreamCreate(&stream[i])); + } + + for (int i=0; i < NUM_STREAMS; ++i) { + HIP_CHECK(hipMemcpyWithStream(A_d[i], A_h[i], Nbytes, + hipMemcpyHostToDevice, stream[i])); + HIP_CHECK(hipMemcpyWithStream(B_d[i], B_h[i], Nbytes, + hipMemcpyHostToDevice, stream[i])); + } + + for (int i=0; i < NUM_STREAMS; ++i) { + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), + 0, stream[i], static_cast(A_d[i]), + static_cast(B_d[i]), C_d[i], N); + HIP_CHECK(hipGetLastError()); + } + + for (int i=0; i < NUM_STREAMS; ++i) { + HIP_CHECK(hipStreamSynchronize(stream[i])); + HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h[i], B_h[i], C_h[i], N); + } + + for (int i=0; i < NUM_STREAMS; ++i) { + HipTest::freeArrays(A_d[i], B_d[i], C_d[i], A_h[i], B_h[i], C_h[i], false); + HIP_CHECK(hipStreamDestroy(stream[i])); + } +} + +void TestDtoDonSameDevice(void) { + size_t Nbytes = N * sizeof(int); + const int NUM_STREAMS = 2; + int *A_d[NUM_STREAMS], *B_d[NUM_STREAMS], *C_d[NUM_STREAMS]; + int *A_h[NUM_STREAMS], *B_h[NUM_STREAMS], *C_h[NUM_STREAMS]; + + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + + HipTest::initArrays(&A_d[0], &B_d[0], &C_d[0], + &A_h[0], &B_h[0], &C_h[0], N, false); + + + hipStream_t stream[NUM_STREAMS]; + for (int i=0; i < NUM_STREAMS; ++i) { + HIP_CHECK(hipSetDevice(0)); + HIP_CHECK(hipStreamCreate(&stream[i])); + } + + HIP_CHECK(hipSetDevice(0)); + HIP_CHECK(hipMalloc(&A_d[1], Nbytes)); + HIP_CHECK(hipMalloc(&B_d[1], Nbytes)); + HIP_CHECK(hipMalloc(&C_d[1], Nbytes)); + C_h[1] = reinterpret_cast(malloc(Nbytes)); + HIP_ASSERT(C_h[1] != NULL); + + HIP_CHECK(hipMemcpyWithStream(A_d[0], A_h[0], Nbytes, + hipMemcpyHostToDevice, stream[0])); + HIP_CHECK(hipMemcpyWithStream(B_d[0], B_h[0], Nbytes, + hipMemcpyHostToDevice, stream[0])); + + HIP_CHECK(hipMemcpyWithStream(A_d[1], A_d[0], Nbytes, + hipMemcpyDeviceToDevice, stream[1])); + HIP_CHECK(hipMemcpyWithStream(B_d[1], B_d[0], Nbytes, + hipMemcpyDeviceToDevice, stream[1])); + + + for (int i=0; i < NUM_STREAMS; ++i) { + HIP_CHECK(hipSetDevice(0)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), + 0, stream[i], static_cast(A_d[i]), + static_cast(B_d[i]), C_d[i], N); + HIP_CHECK(hipGetLastError()); + } + + for (int i=0; i < NUM_STREAMS; ++i) { + HIP_CHECK(hipSetDevice(0)); + HIP_CHECK(hipStreamSynchronize(stream[i])); + HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h[0], B_h[0], C_h[i], N); + } + + + HipTest::freeArrays(A_d[0], B_d[0], C_d[0], A_h[0], B_h[0], C_h[0], false); + + if (A_d[1]) { + HIP_CHECK(hipFree(A_d[1])); + } + if (B_d[1]) { + HIP_CHECK(hipFree(B_d[1])); + } + if (C_d[1]) { + HIP_CHECK(hipFree(C_d[1])); + } + if (C_h[1]) { + free(C_h[1]); + } + + + for (int i=0; i < NUM_STREAMS; ++i) { + HIP_CHECK(hipStreamDestroy(stream[i])); + } +} + +void TestOnMultiGPUwithOneStream(void) { + size_t Nbytes = N * sizeof(int); + int NumDevices = 0; + + HIP_CHECK(hipGetDeviceCount(&NumDevices)); + // If you have single GPU machine the return + if (NumDevices <= 1) { + SUCCEED("NumDevices <2"); + } else { + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + int *A_d[MaxGPUDevices], *B_d[MaxGPUDevices], *C_d[MaxGPUDevices]; + int *A_h[MaxGPUDevices], *B_h[MaxGPUDevices], *C_h[MaxGPUDevices]; + + hipStream_t stream[MaxGPUDevices]; + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipStreamCreate(&stream[i])); + } + + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HipTest::initArrays(&A_d[i], &B_d[i], &C_d[i], + &A_h[i], &B_h[i], &C_h[i], N, false); + } + + + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipMemcpyWithStream(A_d[i], A_h[i], Nbytes, + hipMemcpyHostToDevice, stream[i])); + HIP_CHECK(hipMemcpyWithStream(B_d[i], B_h[i], Nbytes, + hipMemcpyHostToDevice, stream[i])); + } + + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), + dim3(threadsPerBlock), 0, stream[i], + static_cast(A_d[i]), + static_cast(B_d[i]), C_d[i], N); + HIP_CHECK(hipGetLastError()); + } + + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipStreamSynchronize(stream[i])); + HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h[i], B_h[i], C_h[i], N); + } + + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HipTest::freeArrays(A_d[i], B_d[i], C_d[i], + A_h[i], B_h[i], C_h[i], false); + HIP_CHECK(hipStreamDestroy(stream[i])); + } + } +} + +void TestkindDtoH(void) { + size_t Nbytes = N * sizeof(int); + int *A_d, *B_d, *C_d; + int *A_h, *B_h, *C_h; + + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); + + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + + HIP_CHECK(hipMemcpyWithStream(A_d, A_h, Nbytes, + hipMemcpyHostToDevice, stream)); + HIP_CHECK(hipMemcpyWithStream(B_d, B_h, Nbytes, + hipMemcpyHostToDevice, stream)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), + 0, stream, static_cast(A_d), + static_cast(B_d), C_d, N); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipMemcpyWithStream(C_h, C_d, Nbytes, + hipMemcpyDeviceToHost, stream)); + HipTest::checkVectorADD(A_h, B_h, C_h, N); + + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HIP_CHECK(hipStreamDestroy(stream)); +} + +void TestkindDtoD(void) { + size_t Nbytes = N * sizeof(int); + int NumDevices = 0; + + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + HIP_CHECK(hipGetDeviceCount(&NumDevices)); + // If you have single GPU machine the return + if (NumDevices <= 1) { + SUCCEED("NumDevices are less than 2"); + } else { + int *A_d[MaxGPUDevices], *B_d[MaxGPUDevices], *C_d[MaxGPUDevices]; + int *A_h[MaxGPUDevices], *B_h[MaxGPUDevices], *C_h[MaxGPUDevices]; + + hipStream_t stream[MaxGPUDevices]; + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipStreamCreate(&stream[i])); + } + + // Initialize and create the host and device elements for first device + HIP_CHECK(hipSetDevice(0)); + HipTest::initArrays(&A_d[0], &B_d[0], &C_d[0], + &A_h[0], &B_h[0], &C_h[0], N, false); + + for (int i=1; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)) + HIP_CHECK(hipMalloc(&A_d[i], Nbytes)); + HIP_CHECK(hipMalloc(&B_d[i], Nbytes)); + HIP_CHECK(hipMalloc(&C_d[i], Nbytes)); + C_h[i] = reinterpret_cast(malloc(Nbytes)); + HIP_ASSERT(C_h[i] != NULL); + } + + HIP_CHECK(hipSetDevice(0)); + HIP_CHECK(hipMemcpyWithStream(A_d[0], A_h[0], Nbytes, + hipMemcpyHostToDevice, stream[0])); + HIP_CHECK(hipMemcpyWithStream(B_d[0], B_h[0], Nbytes, + hipMemcpyHostToDevice, stream[0])); + + // Copying device data from 1st GPU to the rest of the the GPUs that is + // NumDevices in the setup. 1st GPU start numbering from 0,1,2..n etc. + for (int i=1; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipMemcpyWithStream(A_d[i], A_d[0], Nbytes, + hipMemcpyDeviceToDevice, stream[i])); + HIP_CHECK(hipMemcpyWithStream(B_d[i], B_d[0], Nbytes, + hipMemcpyDeviceToDevice, stream[i])); + } + + + // Launching the kernel including the 1st GPU to the no of GPUs present + // in the setup. 1st GPU start numbering from 0,1,2..n etc. + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), + dim3(threadsPerBlock), + 0, stream[i], static_cast(A_d[i]), + static_cast(B_d[i]), C_d[i], N); + HIP_CHECK(hipGetLastError()); + } + + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipStreamSynchronize(stream[i])); + HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h[0], B_h[0], C_h[i], N); + } + + HipTest::freeArrays(A_d[0], B_d[0], C_d[0], A_h[0], B_h[0], C_h[0], false); + HIP_CHECK(hipStreamDestroy(stream[0])); + + for (int i=1; i < NumDevices; ++i) { + if (A_d[i]) { + HIP_CHECK(hipFree(A_d[i])); + } + if (B_d[i]) { + HIP_CHECK(hipFree(B_d[i])); + } + if (C_d[i]) { + HIP_CHECK(hipFree(C_d[i])); + } + if (C_h[i]) { + free(C_h[i]); + } + HIP_CHECK(hipStreamDestroy(stream[i])); + } + } +} + +void TestkindDefault(void) { + size_t Nbytes = N * sizeof(int); + int *A_d, *B_d, *C_d; + int *A_h, *B_h, *C_h; + + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false); + + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + + HIP_CHECK(hipMemcpyWithStream(A_d, A_h, Nbytes, hipMemcpyDefault, stream)); + HIP_CHECK(hipMemcpyWithStream(B_d, B_h, Nbytes, hipMemcpyDefault, stream)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), + 0, stream, static_cast(A_d), + static_cast(B_d), C_d, N); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipStreamSynchronize(stream)); + HIP_CHECK(hipMemcpyWithStream(C_h, C_d, Nbytes, hipMemcpyDefault, stream)); + HipTest::checkVectorADD(A_h, B_h, C_h, N); + + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HIP_CHECK(hipStreamDestroy(stream)); +} + +void TestkindDefaultForDtoD(void) { + size_t Nbytes = N * sizeof(int); + int NumDevices = 0; + + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N); + HIP_CHECK(hipGetDeviceCount(&NumDevices)); + // Test case will not run on single GPU setup. + if (NumDevices <= 1) { + SUCCEED("No of Devices < 2"); + } else { + int *A_d[MaxGPUDevices], *B_d[MaxGPUDevices], *C_d[MaxGPUDevices]; + int *A_h[MaxGPUDevices], *B_h[MaxGPUDevices], *C_h[MaxGPUDevices]; + + // Initialize and create the host and device elements for first device + HIP_CHECK(hipSetDevice(0)); + HipTest::initArrays(&A_d[0], &B_d[0], &C_d[0], + &A_h[0], &B_h[0], &C_h[0], N, false); + + for (int i=1; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipMalloc(&A_d[i], Nbytes)); + HIP_CHECK(hipMalloc(&B_d[i], Nbytes)); + HIP_CHECK(hipMalloc(&C_d[i], Nbytes)); + C_h[i] = reinterpret_cast(malloc(Nbytes)); + HIP_ASSERT(C_h[i] != NULL); + } + + hipStream_t stream[MaxGPUDevices]; + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); + HIP_CHECK(hipStreamCreate(&stream[i])); + } + + HIP_CHECK(hipMemcpyWithStream(A_d[0], A_h[0], Nbytes, + hipMemcpyHostToDevice, stream[0])); + HIP_CHECK(hipMemcpyWithStream(B_d[0], B_h[0], Nbytes, + hipMemcpyHostToDevice, stream[0])); + + // Copying device data from 1st GPU to the rest of the the GPUs + // using hipMemcpyDefault kind that is NumDevices in the setup. + // 1st GPU start numbering from 0,1,2..n etc. + for (int i=1; i < NumDevices; ++i) { + HIP_CHECK(hipMemcpyWithStream(A_d[i], A_d[0], Nbytes, + hipMemcpyDefault, stream[i])); + HIP_CHECK(hipMemcpyWithStream(B_d[i], B_d[0], Nbytes, + hipMemcpyDefault, stream[i])); + } + + for (int i=0; i < NumDevices; ++i) { + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), + dim3(threadsPerBlock), + 0, stream[i], static_cast(A_d[i]), + static_cast(B_d[i]), C_d[i], N); + HIP_CHECK(hipGetLastError()); + } + + for (int i=0; i < NumDevices; ++i) { + HIP_CHECK(hipSetDevice(i)); // hipMemcpy will be on this device + HIP_CHECK(hipStreamSynchronize(stream[i])); + HIP_CHECK(hipMemcpy(C_h[i], C_d[i], Nbytes, hipMemcpyDeviceToHost)); + // Output of each GPU is getting validated with input of 1st GPU. + HipTest::checkVectorADD(A_h[0], B_h[0], C_h[i], N); + } + + HipTest::freeArrays(A_d[0], B_d[0], C_d[0], A_h[0], B_h[0], C_h[0], false); + HIP_CHECK(hipStreamDestroy(stream[0])); + + for (int i=1; i < NumDevices; ++i) { + if (A_d[i]) { + HIP_CHECK(hipFree(A_d[i])); + } + if (B_d[i]) { + HIP_CHECK(hipFree(B_d[i])); + } + if (C_d[i]) { + HIP_CHECK(hipFree(C_d[i])); + } + if (C_h[i]) { + free(C_h[i]); + } + HIP_CHECK(hipStreamDestroy(stream[i])); + } + } +} + +void TestkindHtoH(void) { + size_t Nbytes = N * sizeof(int); + int *A_h, *B_h; + + + // Allocate memory to A_h and B_h + A_h = static_cast(malloc(Nbytes)); + HIP_ASSERT(A_h != NULL); + B_h = static_cast(malloc(Nbytes)); + HIP_ASSERT(B_h != NULL); + + for (size_t i = 0; i < N; ++i) { + if (A_h) { + (A_h)[i] = 3.146f + i; // Pi + } + } + + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + + HIP_CHECK(hipMemcpyWithStream(B_h, A_h, Nbytes, hipMemcpyHostToHost, stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + + for (size_t i = 0; i < N; i++) { + HIP_ASSERT(A_h[i] == B_h[i]); + } + + if (A_h) { + free(A_h); + } + if (B_h) { + free(B_h); + } + HIP_CHECK(hipStreamDestroy(stream)); +} + + +TEST_CASE("Unit_hipMemcpyWithStream_TestWithOneStream") { + TestwithOnestream(); +} + +TEST_CASE("Unit_hipMemcpyWithStream_TestwithTwoStream") { + TestwithTwoStream(); +} + +TEST_CASE("Unit_hipMemcpyWithStream_TestkindDtoH") { + TestkindDtoH(); +} + +TEST_CASE("Unit_hipMemcpyWithStream_TestkindHtoH") { + TestkindHtoH(); +} + +TEST_CASE("Unit_hipMemcpyWithStream_TestkindDtoD") { + TestkindDtoD(); +} + +TEST_CASE("Unit_hipMemcpyWithStream_TestOnMultiGPUwithOneStream") { + TestOnMultiGPUwithOneStream(); +} + +TEST_CASE("Unit_hipMemcpyWithStream_TestkindDefault") { + TestkindDefault(); +} +#ifndef __HIP_PLATFORM_NVCC__ +TEST_CASE("Unit_hipMemcpyWithStream_TestkindDefaultForDtoD") { + TestkindDefaultForDtoD(); +} +#endif + +TEST_CASE("Unit_hipMemcpyWithStream_TestDtoDonSameDevice") { + TestDtoDonSameDevice(); +} diff --git a/catch/unit/memory/hipMemcpy_derivatives.cc b/catch/unit/memory/hipMemcpy_derivatives.cc new file mode 100644 index 0000000000..0375602508 --- /dev/null +++ b/catch/unit/memory/hipMemcpy_derivatives.cc @@ -0,0 +1,119 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include + +// hipMemcpyDtoH +TEST_CASE("Unit_hipMemcpyDtoH_Positive_Basic") { + MemcpyDeviceToHostShell([](void* dst, void* src, size_t count) { + return hipMemcpyDtoH(dst, reinterpret_cast(src), count); + }); +} + +TEST_CASE("Unit_hipMemcpyDtoH_Positive_Synchronization_Behavior") { + const auto f = [](void* dst, void* src, size_t count) { + return hipMemcpyDtoH(dst, reinterpret_cast(src), count); + }; + MemcpyDtoHPageableSyncBehavior(f, true); + MemcpyDtoHPinnedSyncBehavior(f, true); +} + +TEST_CASE("Unit_hipMemcpyDtoH_Negative_Parameters") { + using namespace std::placeholders; + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); + + MemcpyCommonNegativeTests( + [](void* dst, void* src, size_t count) { + return hipMemcpyDtoH(dst, reinterpret_cast(src), count); + }, + host_alloc.ptr(), device_alloc.ptr(), kPageSize); +} + +// hipMemcpyHtoD +TEST_CASE("Unit_hipMemcpyHtoD_Positive_Basic") { + MemcpyHostToDeviceShell([](void* dst, void* src, size_t count) { + return hipMemcpyHtoD(reinterpret_cast(dst), src, count); + }); +} + +TEST_CASE("Unit_hipMemcpyHtoD_Positive_Synchronization_Behavior") { + MemcpyHtoDSyncBehavior( + [](void* dst, void* src, size_t count) { + return hipMemcpyHtoD(reinterpret_cast(dst), src, count); + }, + true); +} + +TEST_CASE("Unit_hipMemcpyHtoD_Negative_Parameters") { + using namespace std::placeholders; + LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); + + MemcpyCommonNegativeTests( + [](void* dst, void* src, size_t count) { + return hipMemcpyHtoD(reinterpret_cast(dst), src, count); + }, + device_alloc.ptr(), host_alloc.ptr(), kPageSize); +} + +// hipMemcpyDtoD +TEST_CASE("Unit_hipMemcpyDtoD_Positive_Basic") { + const auto f = [](void* dst, void* src, size_t count) { + return hipMemcpyDtoD(reinterpret_cast(dst), + reinterpret_cast(src), count); + }; + SECTION("Peer access enabled") { MemcpyDeviceToDeviceShell(f); } + SECTION("Peer access disabled") { MemcpyDeviceToDeviceShell(f); } +} + +TEST_CASE("Unit_hipMemcpyDtoD_Positive_Synchronization_Behavior") { + // This behavior differs on NVIDIA and AMD, on AMD the hipMemcpy calls is synchronous with + // respect to the host +#if HT_AMD + HipTest::HIP_SKIP_TEST( + "EXSWCPHIPT-127 - Memcpy from device to device memory behavior differs on AMD and Nvidia"); + return; +#endif + MemcpyDtoDSyncBehavior( + [](void* dst, void* src, size_t count) { + return hipMemcpyDtoD(reinterpret_cast(dst), + reinterpret_cast(src), count); + }, + false); +} + +TEST_CASE("Unit_hipMemcpyDtoD_Negative_Parameters") { + using namespace std::placeholders; + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); + + MemcpyCommonNegativeTests( + [](void* dst, void* src, size_t count) { + return hipMemcpyDtoD(reinterpret_cast(dst), + reinterpret_cast(src), count); + }, + dst_alloc.ptr(), src_alloc.ptr(), kPageSize); +} \ No newline at end of file diff --git a/catch/unit/memory/hipMemcpy_old.cc b/catch/unit/memory/hipMemcpy_old.cc new file mode 100644 index 0000000000..84af63bea0 --- /dev/null +++ b/catch/unit/memory/hipMemcpy_old.cc @@ -0,0 +1,619 @@ +/* +Copyright (c) 2021 - present 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 testcase verifies following scenarios +1. hipMemcpy API along with kernel launch with different data types +2. H2D-D2D-D2H scenarios for unpinned and pinned memory +3. Boundary checks with different sizes +4. Multithread scenario +5. device offset scenario +*/ + +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include "sys/types.h" +#include "sys/sysinfo.h" +#endif + + +static constexpr auto NUM_ELM{4*1024 * 1024}; +static unsigned blocksPerCU{6}; // to hide latency +static unsigned threadsPerBlock{256}; + +template +class DeviceMemory { + public: + explicit DeviceMemory(size_t numElements); + DeviceMemory() = delete; + ~DeviceMemory(); + T* A_d() const { return _A_d + _offset; } + T* B_d() const { return _B_d + _offset; } + T* C_d() const { return _C_d + _offset; } + T* C_dd() const { return _C_dd + _offset; } + size_t maxNumElements() const { return _maxNumElements; } + void offset(int offset) { _offset = offset; } + int offset() const { return _offset; } + private: + T* _A_d; + T* _B_d; + T* _C_d; + T* _C_dd; + size_t _maxNumElements; + int _offset; +}; + +template +DeviceMemory::DeviceMemory(size_t numElements) : + _maxNumElements(numElements), _offset(0) { + T** np = nullptr; + HipTest::initArrays(&_A_d, &_B_d, &_C_d, np, np, np, numElements, 0); + size_t sizeElements = numElements * sizeof(T); + HIP_CHECK(hipMalloc(&_C_dd, sizeElements)); +} + + +template +DeviceMemory::~DeviceMemory() { + T* np = nullptr; + HipTest::freeArrays(_A_d, _B_d, _C_d, np, np, np, 0); + HIP_CHECK(hipFree(_C_dd)); + _C_dd = NULL; +} + +template +class HostMemory { + public: + HostMemory(size_t numElements, bool usePinnedHost); + HostMemory() = delete; + void reset(size_t numElements, bool full = false); + ~HostMemory(); + T* A_h() const { return _A_h + _offset; } + T* B_h() const { return _B_h + _offset; } + T* C_h() const { return _C_h + _offset; } + + size_t maxNumElements() const { return _maxNumElements; } + void offset(int offset) { _offset = offset; } + int offset() const { return _offset; } + + // Host arrays, secondary copy + T* A_hh; + T* B_hh; + bool _usePinnedHost; + + private: + size_t _maxNumElements; + int _offset; + + // Host arrays + T* _A_h; + T* _B_h; + T* _C_h; +}; + + template +HostMemory::HostMemory(size_t numElements, bool usePinnedHost) + : _usePinnedHost(usePinnedHost), _maxNumElements(numElements), _offset(0) { + T** np = nullptr; + HipTest::initArrays(np, np, np, &_A_h, &_B_h, &_C_h, + numElements, usePinnedHost); + + A_hh = NULL; + B_hh = NULL; + + + size_t sizeElements = numElements * sizeof(T); + + if (usePinnedHost) { + HIP_CHECK(hipHostMalloc(reinterpret_cast(&A_hh), sizeElements, + hipHostMallocDefault)); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&B_hh), sizeElements, + hipHostMallocDefault)); + } else { + A_hh = reinterpret_cast(malloc(sizeElements)); + B_hh = reinterpret_cast(malloc(sizeElements)); + } + } + +template +void HostMemory::reset(size_t numElements, bool full) { + // Initialize the host data: + for (size_t i = 0; i < numElements; i++) { + (A_hh)[i] = 1097.0 + i; + (B_hh)[i] = 1492.0 + i; // Phi + + if (full) { + (_A_h)[i] = 3.146f + i; // Pi + (_B_h)[i] = 1.618f + i; // Phi + } + } +} + +template +HostMemory::~HostMemory() { + HipTest::freeArraysForHost(_A_h, _B_h, _C_h, _usePinnedHost); + + if (_usePinnedHost) { + HIP_CHECK(hipHostFree(A_hh)); + HIP_CHECK(hipHostFree(B_hh)); + + } else { + free(A_hh); + free(B_hh); + } +} + +#ifdef _WIN32 +void memcpytest2_get_host_memory(size_t *free, size_t *total) { + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + // Windows doesn't allow allocating more than half of system memory to the gpu + // Since the runtime also needs space for its internal allocations, + // we should not try to allocate more than 40% of reported system memory, + // otherwise we can run into OOM issues. + *free = static_cast(0.4 * status.ullAvailPhys); + *total = static_cast(0.4 * status.ullTotalPhys); +} +#else +struct sysinfo memInfo; +void memcpytest2_get_host_memory(size_t *free, size_t *total) { + sysinfo(&memInfo); + uint64_t freePhysMem = memInfo.freeram; + freePhysMem *= memInfo.mem_unit; + *free = freePhysMem; + uint64_t totalPhysMem = memInfo.totalram; + totalPhysMem *= memInfo.mem_unit; + *total = totalPhysMem; +} +#endif + +//--- +// Test many different kinds of memory copies. +// The subroutine allocates memory , copies to device, runs a vector +// add kernel, copies back, and +// checks the result. +// +// IN: numElements controls the number of elements used for allocations. +// IN: usePinnedHost : If true, allocate host with hipHostMalloc and is pinned +// else allocate host +// memory with malloc. IN: useHostToHost : If true, add an extra +// host-to-host copy. IN: +// useDeviceToDevice : If true, add an extra deviceto-device copy after +// result is produced. IN: +// useMemkindDefault : If true, use memkinddefault +// (runtime figures out direction). if false, use +// explicit memcpy direction. +// +template +void memcpytest2(DeviceMemory* dmem, HostMemory* hmem, + size_t numElements, bool useHostToHost, + bool useDeviceToDevice, bool useMemkindDefault) { + size_t sizeElements = numElements * sizeof(T); + + hmem->reset(numElements); + + assert(numElements <= dmem->maxNumElements()); + assert(numElements <= hmem->maxNumElements()); + + + if (useHostToHost) { + // Do some extra host-to-host copies here to mix things up: + HIP_CHECK(hipMemcpy(hmem->A_hh, hmem->A_h(), sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToHost)); + HIP_CHECK(hipMemcpy(hmem->B_hh, hmem->B_h(), sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToHost)); + + + HIP_CHECK(hipMemcpy(dmem->A_d(), hmem->A_hh, sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(dmem->B_d(), hmem->B_hh, sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); + } else { + HIP_CHECK(hipMemcpy(dmem->A_d(), hmem->A_h(), sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(dmem->B_d(), hmem->B_h(), sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyHostToDevice)); + } + + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), 0, 0, + static_cast(dmem->A_d()), static_cast(dmem->B_d()), + dmem->C_d(), numElements); + HIP_CHECK(hipGetLastError()); + + if (useDeviceToDevice) { + // Do an extra device-to-device copy here to mix things up: + HIP_CHECK(hipMemcpy(dmem->C_dd(), dmem->C_d(), sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyDeviceToDevice)); + + // Destroy the original dmem->C_d(): + HIP_CHECK(hipMemset(dmem->C_d(), 0x5A, sizeElements)); + + HIP_CHECK(hipMemcpy(hmem->C_h(), dmem->C_dd(), sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyDeviceToHost)); + } else { + HIP_CHECK(hipMemcpy(hmem->C_h(), dmem->C_d(), sizeElements, + useMemkindDefault ? hipMemcpyDefault : hipMemcpyDeviceToHost)); + } + + HIP_CHECK(hipDeviceSynchronize()); + HipTest::checkVectorADD(hmem->A_h(), hmem->B_h(), hmem->C_h(), numElements); + + + printf(" %s success\n", __func__); +} + +// Try all the 16 possible combinations to memcpytest2 - usePinnedHost, +// useHostToHost, +// useDeviceToDevice, useMemkindDefault +template +void memcpytest2_for_type(size_t numElements) { + DeviceMemory memD(numElements); + HostMemory memU(numElements, 0 /*usePinnedHost*/); + HostMemory memP(numElements, 1 /*usePinnedHost*/); + + for (int usePinnedHost = 0; usePinnedHost <= 1; usePinnedHost++) { + for (int useHostToHost = 0; useHostToHost <= 1; useHostToHost++) { + for (int useDeviceToDevice = 0; useDeviceToDevice <= 1; + useDeviceToDevice++) { + for (int useMemkindDefault = 0; useMemkindDefault <= 1; + useMemkindDefault++) { + memcpytest2(&memD, usePinnedHost ? &memP : &memU, + numElements, useHostToHost, + useDeviceToDevice, useMemkindDefault); + } + } + } + } +} + +// Try many different sizes to memory copy. +template +void memcpytest2_sizes(size_t maxElem = 0) { + int deviceId; + HIP_CHECK(hipGetDevice(&deviceId)); + + size_t free, total, freeCPU, totalCPU; + HIP_CHECK(hipMemGetInfo(&free, &total)); + memcpytest2_get_host_memory(&freeCPU, &totalCPU); + + if (maxElem == 0) { + // Use lesser maxElem if not enough host memory available + size_t maxElemGPU = free / sizeof(T) / 8; + size_t maxElemCPU = freeCPU / sizeof(T) / 8; + maxElem = maxElemGPU < maxElemCPU ? maxElemGPU : maxElemCPU; + } + + HIP_CHECK(hipDeviceReset()); + DeviceMemory memD(maxElem); + HostMemory memU(maxElem, 0 /*usePinnedHost*/); + HostMemory memP(maxElem, 1 /*usePinnedHost*/); + + for (size_t elem = 1; elem <= maxElem; elem *= 2) { + memcpytest2(&memD, &memU, elem, 1, 1, 0); // unpinned host + memcpytest2(&memD, &memP, elem, 1, 1, 0); // pinned host + } +} + +// Try many different sizes to memory copy. +template +void memcpytest2_offsets(size_t maxElem, bool devOffsets, bool hostOffsets) { + int deviceId; + HIP_CHECK(hipGetDevice(&deviceId)); + + size_t free, total; + HIP_CHECK(hipMemGetInfo(&free, &total)); + + HIP_CHECK(hipDeviceReset()); + DeviceMemory memD(maxElem); + HostMemory memU(maxElem, 0 /*usePinnedHost*/); + HostMemory memP(maxElem, 1 /*usePinnedHost*/); + + size_t elem = maxElem / 2; + + for (size_t offset = 0; offset < 512; offset++) { + assert(elem + offset < maxElem); + if (devOffsets) { + memD.offset(offset); + } + if (hostOffsets) { + memU.offset(offset); + memP.offset(offset); + } + memcpytest2(&memD, &memU, elem, 1, 1, 0); // unpinned host + memcpytest2(&memD, &memP, elem, 1, 1, 0); // pinned host + } + + for (size_t offset = 512; offset < elem; offset *= 2) { + assert(elem + offset < maxElem); + if (devOffsets) { + memD.offset(offset); + } + if (hostOffsets) { + memU.offset(offset); + memP.offset(offset); + } + memcpytest2(&memD, &memU, elem, 1, 1, 0); // unpinned host + memcpytest2(&memD, &memP, elem, 1, 1, 0); // pinned host + } +} + +// Create multiple threads to stress multi-thread locking behavior in the +// allocation/deallocation/tracking logic: +template +void multiThread_1(bool serialize, bool usePinnedHost) { + DeviceMemory memD(NUM_ELM); + HostMemory mem1(NUM_ELM, usePinnedHost); + HostMemory mem2(NUM_ELM, usePinnedHost); + + std::thread t1(memcpytest2, &memD, &mem1, NUM_ELM, 0, 0, 0); + if (serialize) { + t1.join(); + } + + + std::thread t2(memcpytest2, &memD, &mem2, NUM_ELM, 0, 0, 0); + if (serialize) { + t2.join(); + } +} + + + +/* +This testcase verifies hipMemcpy API +Initializes device variables +Launches kernel and performs the sum of device variables +copies the result to host variable and validates the result. +*/ +TEMPLATE_TEST_CASE("Unit_hipMemcpy_KernelLaunch", "", int, float, + double) { + size_t Nbytes = NUM_ELM * sizeof(TestType); + + TestType *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + TestType *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; + + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, NUM_ELM, false); + + HIP_CHECK(hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(B_d, B_h, Nbytes, hipMemcpyHostToDevice)); + + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), 0, 0, + static_cast(A_d), + static_cast(B_d), C_d, NUM_ELM); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); + + HIP_CHECK(hipDeviceSynchronize()); + HipTest::checkVectorADD(A_h, B_h, C_h, NUM_ELM); + + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); +} + +/* +This testcase verifies the following scenarios +1. H2H,H2PinMem and PinnedMem2Host +2. H2D-D2D-D2H in same GPU +3. Pinned Host Memory to device variables in same GPU +4. Device context change +5. H2D-D2D-D2H peer GPU +*/ +TEMPLATE_TEST_CASE("Unit_hipMemcpy_H2H-H2D-D2H-H2PinMem", "", int, + float, double) { + TestType *A_d{nullptr}, *B_d{nullptr}; + TestType *A_h{nullptr}, *B_h{nullptr}; + TestType *A_Ph{nullptr}, *B_Ph{nullptr}; + HIP_CHECK(hipSetDevice(0)); + HipTest::initArrays(&A_d, &B_d, nullptr, + &A_h, &B_h, nullptr, + NUM_ELM*sizeof(TestType)); + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_Ph, &B_Ph, nullptr, + NUM_ELM*sizeof(TestType), true); + + SECTION("H2H, H2PinMem and PinMem2H") { + HIP_CHECK(hipMemcpy(B_h, A_h, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(A_Ph, B_h, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_Ph, A_Ph, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HipTest::checkTest(A_h, B_Ph, NUM_ELM); + } + + SECTION("H2D-D2D-D2H-SameGPU") { + HIP_CHECK(hipMemcpy(A_d, A_h, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_d, A_d, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_h, B_d, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); + HipTest::checkTest(A_h, B_h, NUM_ELM); + } + + SECTION("pH2D-D2D-D2pH-SameGPU") { + HIP_CHECK(hipMemcpy(A_d, A_Ph, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_d, A_d, NUM_ELM*sizeof(TestType), hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_Ph, B_d, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HipTest::checkTest(A_Ph, B_Ph, NUM_ELM); + } + SECTION("H2D-D2D-D2H-DeviceContextChange") { + int deviceCount = 0; + HIP_CHECK(hipGetDeviceCount(&deviceCount)); + if (deviceCount < 2) { + SUCCEED("deviceCount less then 2"); + } else { + int canAccessPeer = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipMemcpy(A_d, A_h, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_d, A_d, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_h, B_d, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HipTest::checkTest(A_h, B_h, NUM_ELM); + } else { + SUCCEED("P2P capability is not present"); + } + } + } + + SECTION("H2D-D2D-D2H-PeerGPU") { + int deviceCount = 0; + HIP_CHECK(hipGetDeviceCount(&deviceCount)); + if (deviceCount < 2) { + SUCCEED("deviceCount less then 2"); + } else { + int canAccessPeer = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + HIP_CHECK(hipSetDevice(1)); + TestType *C_d{nullptr}; + HipTest::initArrays(nullptr, nullptr, &C_d, + nullptr, nullptr, nullptr, + NUM_ELM*sizeof(TestType)); + HIP_CHECK(hipMemcpy(A_d, A_h, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(C_d, A_d, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HIP_CHECK(hipMemcpy(B_h, C_d, NUM_ELM*sizeof(TestType), + hipMemcpyDefault)); + HipTest::checkTest(A_h, B_h, NUM_ELM); + HIP_CHECK(hipFree(C_d)); + } else { + SUCCEED("P2P capability is not present"); + } + } + } + + HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); + HipTest::freeArrays(nullptr, nullptr, nullptr, A_Ph, + B_Ph, nullptr, true); +} +/* +This testcase verifies the multi thread scenario +*/ +TEST_CASE("Unit_hipMemcpy_MultiThreadWithSerialization") { + HIP_CHECK(hipDeviceReset()); + + // Simplest cases: serialize the threads, and also used pinned memory: + // This verifies that the sub-calls to memcpytest2 are correct. + multiThread_1(true, true); + + // Serialize, but use unpinned memory to stress the unpinned memory xfer path. + multiThread_1(true, false); +} + +/* +This testcase verifies hipMemcpy API with pinnedMemory and hostRegister +along with kernel launches +*/ + +TEMPLATE_TEST_CASE("Unit_hipMemcpy_PinnedRegMemWithKernelLaunch", + "", int, float, double) { + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices < 2) { + SUCCEED("No of devices are less than 2"); + } else { + // 1 refers to pinned Memory + // 2 refers to register Memory + int MallocPinType = GENERATE(0, 1); + size_t Nbytes = NUM_ELM * sizeof(TestType); + unsigned blocks = HipTest::setNumBlocks(blocksPerCU, + threadsPerBlock, NUM_ELM); + + TestType *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + TestType *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; + TestType *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; + if (MallocPinType) { + HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, NUM_ELM, true); + } else { + A_h = reinterpret_cast(malloc(Nbytes)); + HIP_CHECK(hipHostRegister(A_h, Nbytes, hipHostRegisterDefault)); + B_h = reinterpret_cast(malloc(Nbytes)); + HIP_CHECK(hipHostRegister(B_h, Nbytes, hipHostRegisterDefault)); + C_h = reinterpret_cast(malloc(Nbytes)); + HIP_CHECK(hipHostRegister(C_h, Nbytes, hipHostRegisterDefault)); + HipTest::initArrays(&A_d, &B_d, &C_d, nullptr, nullptr, + nullptr, NUM_ELM, false); + HipTest::setDefaultData(NUM_ELM, A_h, B_h, C_h); + } + HIP_CHECK(hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(B_d, B_h, Nbytes, hipMemcpyHostToDevice)); + + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), + 0, 0, static_cast(A_d), + static_cast(B_d), C_d, NUM_ELM); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h, B_h, C_h, NUM_ELM); + + unsigned int seed = time(0); + HIP_CHECK(hipSetDevice(HipTest::RAND_R(&seed) % (numDevices-1)+1)); + + int device; + HIP_CHECK(hipGetDevice(&device)); + std::cout <<"hipMemcpy is set to happen between device 0 and device " + <(&X_d, &Y_d, &Z_d, nullptr, + nullptr, nullptr, NUM_ELM, false); + + for (int j = 0; j < NUM_ELM; j++) { + A_h[j] = 0; + B_h[j] = 0; + C_h[j] = 0; + } + + HIP_CHECK(hipMemcpy(A_h, A_d, Nbytes, hipMemcpyDeviceToHost)); + HIP_CHECK(hipMemcpy(X_d, A_h, Nbytes, hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(B_h, B_d, Nbytes, hipMemcpyDeviceToHost)); + HIP_CHECK(hipMemcpy(Y_d, B_h, Nbytes, hipMemcpyHostToDevice)); + + hipLaunchKernelGGL(HipTest::vectorADD, dim3(blocks), dim3(threadsPerBlock), + 0, 0, static_cast(X_d), + static_cast(Y_d), Z_d, NUM_ELM); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, Z_d, Nbytes, hipMemcpyDeviceToHost)); + + HipTest::checkVectorADD(A_h, B_h, C_h, NUM_ELM); + + if (MallocPinType) { + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, true); + } else { + HIP_CHECK(hipHostUnregister(A_h)); + free(A_h); + HIP_CHECK(hipHostUnregister(B_h)); + free(B_h); + HIP_CHECK(hipHostUnregister(C_h)); + free(C_h); + HipTest::freeArrays(A_d, B_d, C_d, nullptr, + nullptr, nullptr, false); + } + HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, + nullptr, nullptr, false); + } +} From 47d9bf2b3f07746f1e980cbdb9feee998292108d Mon Sep 17 00:00:00 2001 From: music-dino <111048524+music-dino@users.noreply.github.com> Date: Tue, 17 Jan 2023 07:56:04 +0100 Subject: [PATCH 02/14] EXSWHTEC-70 - Reimplement tests for hipMemPrefetchAsync (#17) - Basic positive tests - Negative parameter tests --- catch/unit/memory/hipMemPrefetchAsync.cc | 218 ++++++++++++++--------- 1 file changed, 130 insertions(+), 88 deletions(-) diff --git a/catch/unit/memory/hipMemPrefetchAsync.cc b/catch/unit/memory/hipMemPrefetchAsync.cc index 17ef618b77..8ff869d56f 100644 --- a/catch/unit/memory/hipMemPrefetchAsync.cc +++ b/catch/unit/memory/hipMemPrefetchAsync.cc @@ -1,13 +1,15 @@ /* -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 WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -17,9 +19,27 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include + #include -// Kernel function -__global__ void MemPrftchAsyncKernel(int* C_d, const int* A_d, size_t N) { +#include +#include +#include + +std::vector GetDevicesWithPrefetchSupport() { + const auto device_count = HipTest::getDeviceCount(); + std::vector supported_devices; + supported_devices.reserve(device_count + 1); + for (int i = 0; i < device_count; ++i) { + if (DeviceAttributesSupport(i, hipDeviceAttributeManagedMemory, + hipDeviceAttributeConcurrentManagedAccess)) { + supported_devices.push_back(i); + } + } + return supported_devices; +} + +__global__ void MemPrefetchAsyncKernel(int* C_d, const int* A_d, size_t N) { size_t offset = (blockIdx.x * blockDim.x + threadIdx.x); size_t stride = blockDim.x * gridDim.x; for (size_t i = offset; i < N; i += stride) { @@ -27,98 +47,120 @@ __global__ void MemPrftchAsyncKernel(int* C_d, const int* A_d, size_t N) { } } +TEST_CASE("Unit_hipMemPrefetchAsync_Basic") { + const auto supported_devices = GetDevicesWithPrefetchSupport(); + if (supported_devices.empty()) { + HipTest::HIP_SKIP_TEST("Test need at least one device with managed memory support"); + } -static int HmmAttrPrint() { - int managed = 0; - INFO("The following are the attribute values related to HMM for" - " device 0:\n"); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributeDirectManagedMemAccessFromHost, 0)); - INFO("hipDeviceAttributeDirectManagedMemAccessFromHost: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributeConcurrentManagedAccess, 0)); - INFO("hipDeviceAttributeConcurrentManagedAccess: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributePageableMemoryAccess, 0)); - INFO("hipDeviceAttributePageableMemoryAccess: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributePageableMemoryAccessUsesHostPageTables, 0)); - INFO("hipDeviceAttributePageableMemoryAccessUsesHostPageTables:" - << managed); + LinearAllocGuard alloc1(LinearAllocs::hipMallocManaged, kPageSize); + const auto count = kPageSize / sizeof(*alloc1.ptr()); + constexpr auto fill_value = 42; + std::fill_n(alloc1.ptr(), count, fill_value); - HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory, - 0)); - INFO("hipDeviceAttributeManagedMemory: " << managed); - return managed; + for (const auto device : supported_devices) { + HIP_CHECK(hipSetDevice(device)); + LinearAllocGuard alloc2(LinearAllocs::hipMallocManaged, kPageSize); + StreamGuard sg(Streams::created); + HIP_CHECK(hipMemPrefetchAsync(alloc1.ptr(), kPageSize, device, sg.stream())); + MemPrefetchAsyncKernel<<>>(alloc2.ptr(), alloc1.ptr(), + count); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipStreamSynchronize(sg.stream())); + ArrayFindIfNot(alloc1.ptr(), fill_value, count); + ArrayFindIfNot(alloc2.ptr(), fill_value * fill_value, count); + } + + HIP_CHECK(hipMemPrefetchAsync(alloc1.ptr(), kPageSize, hipCpuDeviceId)); + HIP_CHECK(hipStreamSynchronize(nullptr)); + ArrayFindIfNot(alloc1.ptr(), fill_value, count); } -/* - Test Description: This test prefetches the memory to each of the available - devices and launch kernel followed by result verification - At the end the memory is prefetched to Host and kernel is launched followed - by result verification. -*/ +TEST_CASE("Unit_hipMemPrefetchAsync_Sync_Behavior") { + const auto supported_devices = GetDevicesWithPrefetchSupport(); + if (supported_devices.empty()) { + HipTest::HIP_SKIP_TEST("Test need at least one device with managed memory support"); + } + const auto device = supported_devices.front(); + const auto stream_type = GENERATE(Streams::nullstream, Streams::perThread, Streams::created); -TEST_CASE("Unit_hipMemPrefetchAsync") { - int MangdMem = HmmAttrPrint(); - if (MangdMem == 1) { - bool IfTestPassed = true; - int A_CONST = 123, MEM_SIZE = (8192 * sizeof(int)); - int *devPtr1 = NULL, *devPtr2 = NULL, NumDevs = 0, flag = 0; - hipStream_t strm; - HIP_CHECK(hipMallocManaged(&devPtr1, MEM_SIZE)); - HIP_CHECK(hipMallocManaged(&devPtr2, MEM_SIZE)); - HIP_CHECK(hipGetDeviceCount(&NumDevs)); - // Initializing the memory - for (uint32_t k = 0; k < (MEM_SIZE/sizeof(int)); ++k) { - devPtr1[k] = A_CONST; - devPtr2[k] = 0; - } + StreamGuard sg(stream_type); + LinearAllocGuard alloc(LinearAllocs::hipMallocManaged, kPageSize); + LaunchDelayKernel(std::chrono::milliseconds{100}, sg.stream()); + HIP_CHECK(hipMemPrefetchAsync(alloc.ptr(), kPageSize, device, sg.stream())); + HIP_CHECK_ERROR(hipStreamQuery(sg.stream()), hipErrorNotReady); + HIP_CHECK(hipStreamSynchronize(sg.stream())); +} +TEST_CASE("Unit_hipMemPrefetchAsync_Rounding_Behavior") { + auto supported_devices = GetDevicesWithPrefetchSupport(); + if (supported_devices.empty()) { + HipTest::HIP_SKIP_TEST("Test need at least one device with managed memory support"); + } + const auto device = supported_devices.front(); + LinearAllocGuard alloc(LinearAllocs::hipMallocManaged, 3 * kPageSize); + REQUIRE_FALSE(reinterpret_cast(alloc.ptr()) % kPageSize); + const auto [offset, width] = + GENERATE_COPY(std::make_pair(kPageSize / 4, kPageSize / 2), // Withing page + std::make_pair(kPageSize / 2, kPageSize), // Across page border + std::make_pair(kPageSize / 2, kPageSize * 2)); // Across two page borders + HIP_CHECK(hipMemPrefetchAsync(alloc.ptr() + offset, width, device)); + HIP_CHECK(hipStreamSynchronize(nullptr)); + constexpr auto RoundDown = [](const intptr_t a, const intptr_t n) { return a - a % n; }; + constexpr auto RoundUp = [RoundDown](const intptr_t a, const intptr_t n) { + return RoundDown(a + n - 1, n); + }; + const auto base = alloc.ptr(); + const auto rounded_up = RoundUp(offset + width, kPageSize); + unsigned int attribute = 0; + HIP_CHECK(hipMemRangeGetAttribute(&attribute, sizeof(attribute), + hipMemRangeAttributeLastPrefetchLocation, + reinterpret_cast(base), rounded_up)); + REQUIRE(device == attribute); + HIP_CHECK(hipMemRangeGetAttribute(&attribute, sizeof(attribute), + hipMemRangeAttributeLastPrefetchLocation, alloc.ptr(), + 3 * kPageSize)); + REQUIRE((rounded_up == 3 * kPageSize ? device : hipInvalidDeviceId) == attribute); +} - for (int i = 0; i < NumDevs; ++i) { - HIP_CHECK(hipSetDevice(i)); - HIP_CHECK(hipStreamCreate(&strm)); - HIP_CHECK(hipMemPrefetchAsync(devPtr1, MEM_SIZE, i, strm)); - HIP_CHECK(hipStreamSynchronize(strm)); - MemPrftchAsyncKernel<<<32, (MEM_SIZE/sizeof(int)/32)>>>(devPtr2, devPtr1, - MEM_SIZE/sizeof(int)); - for (uint32_t m = 0; m < (MEM_SIZE/sizeof(int)); ++m) { - if (devPtr1[m] != (A_CONST * A_CONST)) { - flag = 1; - } - } - HIP_CHECK(hipStreamDestroy(strm)); - if (!flag) { - INFO("Test failed for device: " << i); - IfTestPassed = false; - flag = 0; - } - } - // The memory will be prefetched from last gpu in the system to the host - // memory and kernel is launched followed by result verification. - HIP_CHECK(hipStreamCreate(&strm)); - HIP_CHECK(hipMemPrefetchAsync(devPtr1, MEM_SIZE, hipCpuDeviceId, strm)); - HIP_CHECK(hipStreamSynchronize(strm)); - MemPrftchAsyncKernel<<<32, (MEM_SIZE/sizeof(int)/32)>>>(devPtr2, devPtr1, - MEM_SIZE/sizeof(int)); - for (uint32_t m = 0; m < (MEM_SIZE/sizeof(int)); ++m) { - if (devPtr1[m] != (A_CONST * A_CONST)) { - flag = 1; - } - } - HIP_CHECK(hipStreamDestroy(strm)); - if (!flag) { - INFO("Failed to prefetch the memory to System space.\n"); - IfTestPassed = false; - flag = 0; - } +TEST_CASE("Unit_hipMemPrefetchAsync_Negative_Parameters") { + auto supported_devices = GetDevicesWithPrefetchSupport(); + if (supported_devices.empty()) { + HipTest::HIP_SKIP_TEST("Test need at least one device with managed memory support"); + } + supported_devices.push_back(hipCpuDeviceId); + const auto device = GENERATE_COPY(from_range(supported_devices)); - HIP_CHECK(hipFree(devPtr1)); - HIP_CHECK(hipFree(devPtr2)); - REQUIRE(IfTestPassed); - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); + LinearAllocGuard alloc(LinearAllocs::hipMallocManaged, kPageSize); + SECTION("dev_ptr == nullptr") { + HIP_CHECK_ERROR(hipMemPrefetchAsync(nullptr, kPageSize, device), hipErrorInvalidValue); + } + +#if HT_NVIDIA + SECTION("dev_ptr points to non-managed memory") { + LinearAllocGuard alloc(LinearAllocs::hipMalloc, kPageSize); + HIP_CHECK_ERROR(hipMemPrefetchAsync(alloc.ptr(), kPageSize, device), hipErrorInvalidValue); + } +#endif + + SECTION("count == 0") { + HIP_CHECK_ERROR(hipMemPrefetchAsync(alloc.ptr(), 0, device), hipErrorInvalidValue); + } + + SECTION("count larger than allocation size") { + HIP_CHECK_ERROR(hipMemPrefetchAsync(alloc.ptr(), kPageSize + 1, device), hipErrorInvalidValue); + } + + SECTION("Invalid device") { + HIP_CHECK_ERROR(hipMemPrefetchAsync(alloc.ptr(), kPageSize, hipInvalidDeviceId), + hipErrorInvalidDevice); + } + + SECTION("Invalid stream") { + hipStream_t stream; + HIP_CHECK(hipStreamCreate(&stream)); + HIP_CHECK(hipStreamDestroy(stream)); + HIP_CHECK_ERROR(hipMemPrefetchAsync(alloc.ptr(), kPageSize, device, stream), + hipErrorContextIsDestroyed); } } From 6c54bd9e37969e8ffcaa979e3370c6a9f6e264ce Mon Sep 17 00:00:00 2001 From: music-dino <111048524+music-dino@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:54:41 +0100 Subject: [PATCH 03/14] EXSWHTEC-68 - Implement tests for hipMalloc and hipExtMallocWithFlags (#19) - Basic positive tests - Negative parameter tests --- catch/unit/memory/CMakeLists.txt | 3 + catch/unit/memory/hipExtMallocWithFlags.cc | 134 +++++++++++++++++++++ catch/unit/memory/hipMalloc.cc | 59 +++++++++ 3 files changed, 196 insertions(+) create mode 100644 catch/unit/memory/hipExtMallocWithFlags.cc create mode 100644 catch/unit/memory/hipMalloc.cc diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index 2b846d7c41..6554caec00 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -80,6 +80,8 @@ set(TEST_SRC hipMemcpy_derivatives.cc hipMemcpyAsync.cc hipMemsetFunctional.cc + hipMalloc.cc + hipExtMallocWithFlags.cc hipMallocPitch.cc hipMallocArray.cc hipMalloc3D.cc @@ -160,6 +162,7 @@ set(TEST_SRC hipMemcpy_derivatives.cc hipMemcpyAsync.cc hipMemsetFunctional.cc + hipMalloc.cc hipMallocPitch.cc hipMallocArray.cc hipMalloc3D.cc diff --git a/catch/unit/memory/hipExtMallocWithFlags.cc b/catch/unit/memory/hipExtMallocWithFlags.cc new file mode 100644 index 0000000000..8743254283 --- /dev/null +++ b/catch/unit/memory/hipExtMallocWithFlags.cc @@ -0,0 +1,134 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include + +TEST_CASE("Unit_hipExtMallocWithFlags_Positive_Basic") { + void* ptr = nullptr; + + SECTION("hipDeviceMallocDefault") { + const auto alloc_size = + GENERATE_COPY(10, kPageSize / 2, kPageSize, kPageSize * 3 / 2, kPageSize * 2); + HIP_CHECK(hipExtMallocWithFlags(&ptr, alloc_size, hipDeviceMallocDefault)); + CHECK(ptr != nullptr); + CHECK(reinterpret_cast(ptr) % 256 == 0); + HIP_CHECK(hipFree(ptr)); + } + + SECTION("hipDeviceMallocFinegrained") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeFineGrainSupport)) { + HipTest::HIP_SKIP_TEST("Device does not support fine-grained memory allocations"); + return; + } + const auto alloc_size = + GENERATE_COPY(10, kPageSize / 2, kPageSize, kPageSize * 3 / 2, kPageSize * 2); + HIP_CHECK(hipExtMallocWithFlags(&ptr, alloc_size, hipDeviceMallocFinegrained)); + CHECK(ptr != nullptr); + CHECK(reinterpret_cast(ptr) % 256 == 0); + HIP_CHECK(hipFree(ptr)); + } + + SECTION("hipMallocSignalMemory") { + HIP_CHECK(hipExtMallocWithFlags(&ptr, 8, hipMallocSignalMemory)); + CHECK(ptr != nullptr); + HIP_CHECK(hipFree(ptr)); + } +} + +TEST_CASE("Unit_hipExtMallocWithFlags_Positive_Zero_Size") { + void* ptr = reinterpret_cast(0x1); + const auto flag = GENERATE(hipDeviceMallocDefault, hipDeviceMallocFinegrained); + HIP_CHECK(hipExtMallocWithFlags(&ptr, 0, flag)); + REQUIRE(ptr == nullptr); +} + +TEST_CASE("Unit_hipExtMallocWithFlags_Positive_Alignment") { + void *ptr1 = nullptr, *ptr2 = nullptr; + const auto flag = GENERATE(hipDeviceMallocDefault, hipDeviceMallocFinegrained); + if (flag == hipDeviceMallocFinegrained && + !DeviceAttributesSupport(0, hipDeviceAttributeFineGrainSupport)) { + HipTest::HIP_SKIP_TEST("Device does not support fine-grained memory allocations"); + return; + } + HIP_CHECK(hipExtMallocWithFlags(&ptr1, 1, flag)); + HIP_CHECK(hipExtMallocWithFlags(&ptr2, 10, flag)); + CHECK(reinterpret_cast(ptr1) % 256 == 0); + CHECK(reinterpret_cast(ptr2) % 256 == 0); + HIP_CHECK(hipFree(ptr1)); + HIP_CHECK(hipFree(ptr2)); +} + +TEST_CASE("Unit_hipExtMallocWithFlags_Negative_Parameters") { + SECTION("Invalid flags") { + void* ptr = nullptr; + HIP_CHECK_ERROR( + hipExtMallocWithFlags(&ptr, 4096, hipDeviceMallocDefault | hipMallocSignalMemory), + hipErrorInvalidValue); + } + + SECTION("hipDeviceMallocDefault") { + SECTION("ptr == nullptr") { + HIP_CHECK_ERROR(hipExtMallocWithFlags(nullptr, 4096, hipDeviceMallocDefault), + hipErrorInvalidValue); + } + + SECTION("size == max size_t") { + void* ptr; + HIP_CHECK_ERROR( + hipExtMallocWithFlags(&ptr, std::numeric_limits::max(), hipDeviceMallocDefault), + hipErrorOutOfMemory); + } + } + + SECTION("hipDeviceMallocFinegrained") { + SECTION("ptr == nullptr") { + HIP_CHECK_ERROR(hipExtMallocWithFlags(nullptr, 4096, hipDeviceMallocFinegrained), + hipErrorInvalidValue); + } + + SECTION("size == max size_t") { + void* ptr; + HIP_CHECK_ERROR(hipExtMallocWithFlags(&ptr, std::numeric_limits::max(), + hipDeviceMallocFinegrained), + hipErrorOutOfMemory); + } + } + + SECTION("hipMallocSignalMemory") { + SECTION("ptr == nullptr") { + HIP_CHECK_ERROR(hipExtMallocWithFlags(nullptr, 4096, hipMallocSignalMemory), + hipErrorInvalidValue); + } + + SECTION("size == 0") { + void* ptr; + HIP_CHECK_ERROR(hipExtMallocWithFlags(&ptr, 0, hipMallocSignalMemory), hipErrorInvalidValue); + } + + SECTION("size != 8") { + void* ptr; + HIP_CHECK_ERROR(hipExtMallocWithFlags(&ptr, 16, hipMallocSignalMemory), hipErrorInvalidValue); + } + } +} \ No newline at end of file diff --git a/catch/unit/memory/hipMalloc.cc b/catch/unit/memory/hipMalloc.cc new file mode 100644 index 0000000000..8692f5d59f --- /dev/null +++ b/catch/unit/memory/hipMalloc.cc @@ -0,0 +1,59 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include + +TEST_CASE("Unit_hipMalloc_Positive_Basic") { + constexpr size_t page_size = 4096; + void* ptr = nullptr; + const auto alloc_size = + GENERATE_COPY(10, page_size / 2, page_size, page_size * 3 / 2, page_size * 2); + HIP_CHECK(hipMalloc(&ptr, alloc_size)); + CHECK(ptr != nullptr); + CHECK(reinterpret_cast(ptr) % 256 == 0); + HIP_CHECK(hipFree(ptr)); +} + +TEST_CASE("Unit_hipMalloc_Positive_Zero_Size") { + void* ptr = reinterpret_cast(0x1); + HIP_CHECK(hipMalloc(&ptr, 0)); + REQUIRE(ptr == nullptr); +} + +TEST_CASE("Unit_hipMalloc_Positive_Alignment") { + void *ptr1 = nullptr, *ptr2 = nullptr; + HIP_CHECK(hipMalloc(&ptr1, 1)); + HIP_CHECK(hipMalloc(&ptr2, 10)); + CHECK(reinterpret_cast(ptr1) % 256 == 0); + CHECK(reinterpret_cast(ptr2) % 256 == 0); + HIP_CHECK(hipFree(ptr1)); + HIP_CHECK(hipFree(ptr2)); +} + +TEST_CASE("Unit_hipMalloc_Negative_Parameters") { + SECTION("ptr == nullptr") { HIP_CHECK_ERROR(hipMalloc(nullptr, 4096), hipErrorInvalidValue); } + SECTION("size == max size_t") { + void* ptr; + HIP_CHECK_ERROR(hipMalloc(&ptr, std::numeric_limits::max()), hipErrorOutOfMemory); + } +} \ No newline at end of file From a3e1dcd26c93fd3a0869b640d5b9c59ae3fe947b Mon Sep 17 00:00:00 2001 From: music-dino <111048524+music-dino@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:55:07 +0100 Subject: [PATCH 04/14] EXSWHTEC-149 - Implement tests for hipExternalMemoryGetMappedBuffer for the Vulkan API (#25) - Basic positive test - Negative parameter tests --- catch/unit/CMakeLists.txt | 6 +- catch/unit/vulkan_interop/CMakeLists.txt | 26 ++ .../hipExternalMemoryGetMappedBuffer.cc | 153 +++++++ catch/unit/vulkan_interop/vulkan_test.cc | 410 ++++++++++++++++++ catch/unit/vulkan_interop/vulkan_test.hh | 259 +++++++++++ 5 files changed, 852 insertions(+), 2 deletions(-) create mode 100644 catch/unit/vulkan_interop/CMakeLists.txt create mode 100644 catch/unit/vulkan_interop/hipExternalMemoryGetMappedBuffer.cc create mode 100644 catch/unit/vulkan_interop/vulkan_test.cc create mode 100644 catch/unit/vulkan_interop/vulkan_test.hh diff --git a/catch/unit/CMakeLists.txt b/catch/unit/CMakeLists.txt index d37cea450b..57eb632947 100644 --- a/catch/unit/CMakeLists.txt +++ b/catch/unit/CMakeLists.txt @@ -34,6 +34,8 @@ add_subdirectory(multiThread) add_subdirectory(compiler) add_subdirectory(errorHandling) add_subdirectory(cooperativeGrps) -#if(HIP_PLATFORM STREQUAL "amd") +if(HIP_PLATFORM STREQUAL "amd") #add_subdirectory(clock) -#endif() +# Vulkan interop APIs currently undefined for Nvidia +add_subdirectory(vulkan_interop) +endif() diff --git a/catch/unit/vulkan_interop/CMakeLists.txt b/catch/unit/vulkan_interop/CMakeLists.txt new file mode 100644 index 0000000000..2f015d726a --- /dev/null +++ b/catch/unit/vulkan_interop/CMakeLists.txt @@ -0,0 +1,26 @@ +set(TEST_SRC + vulkan_test.cc + hipExternalMemoryGetMappedBuffer.cc + ) + +find_package(Vulkan) +if(NOT Vulkan_FOUND) + if(EXISTS "${VULKAN_PATH}") + message(STATUS "Vulkan SDK: ${VULKAN_PATH}") + elseif (EXISTS "$ENV{VULKAN_SDK}") + message(STATUS "FOUND VULKAN SDK: $ENV{VULKAN_SDK}") + set(VULKAN_PATH $ENV{VULKAN_SDK}) + else() + message("Error: Unable to locate Vulkan SDK. please specify VULKAN_PATH") + return() + endif() +endif() + +hip_add_exe_to_target(NAME VulkanInteropTest + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests) +if (WIN32) + target_link_libraries(VulkanInteropTest vulkan-1) +else (WIN32) + target_link_libraries(VulkanInteropTest vulkan) +endif (WIN32) \ No newline at end of file diff --git a/catch/unit/vulkan_interop/hipExternalMemoryGetMappedBuffer.cc b/catch/unit/vulkan_interop/hipExternalMemoryGetMappedBuffer.cc new file mode 100644 index 0000000000..c4ad1443c4 --- /dev/null +++ b/catch/unit/vulkan_interop/hipExternalMemoryGetMappedBuffer.cc @@ -0,0 +1,153 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "vulkan_test.hh" + +constexpr bool enable_validation = false; + +template __global__ void Set(T* ptr, const T val) { ptr[threadIdx.x] = val; } + +TEST_CASE("Unit_hipExternalMemoryGetMappedBuffer_Vulkan_Positive_Read_Write") { + VulkanTest vkt(enable_validation); + using type = uint8_t; + constexpr uint32_t count = 3; + + const auto vk_storage = + vkt.CreateMappedStorage(count, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true); + + const auto hip_ext_mem_desc = vkt.BuildMemoryDescriptor(vk_storage.memory, vk_storage.size); + hipExternalMemory_t hip_ext_memory; + HIP_CHECK(hipImportExternalMemory(&hip_ext_memory, &hip_ext_mem_desc)); + + hipExternalMemoryBufferDesc external_mem_buffer_desc = {}; + external_mem_buffer_desc.size = vk_storage.size; + + type* hip_dev_ptr = nullptr; + HIP_CHECK(hipExternalMemoryGetMappedBuffer(reinterpret_cast(&hip_dev_ptr), hip_ext_memory, + &external_mem_buffer_desc)); + REQUIRE(nullptr != hip_dev_ptr); + + vk_storage.host_ptr[0] = 41; + vk_storage.host_ptr[1] = 40; + vk_storage.host_ptr[2] = 43; + + std::vector read_buffer(count, 0); + HIP_CHECK( + hipMemcpy(read_buffer.data(), hip_dev_ptr, count * sizeof(type), hipMemcpyDeviceToHost)); + REQUIRE(41 == read_buffer[0]); + REQUIRE(40 == read_buffer[1]); + REQUIRE(43 == read_buffer[2]); + + Set<<<1, 1>>>(hip_dev_ptr + 1, static_cast(42)); + HIP_CHECK(hipDeviceSynchronize()); + REQUIRE(41 == vk_storage.host_ptr[0]); + REQUIRE(42 == vk_storage.host_ptr[1]); + REQUIRE(43 == vk_storage.host_ptr[2]); + + // Defect - EXSWHTEC-181 + // HIP_CHECK(hipFree(hip_dev_ptr)); + HIP_CHECK(hipDestroyExternalMemory(hip_ext_memory)); +} + +// Disabled on AMD due to defect - EXSWHTEC-175 +#if HT_NVIDIA +TEST_CASE("Unit_hipExternalMemoryGetMappedBuffer_Vulkan_Positive_Read_Write_With_Offset") { + VulkanTest vkt(enable_validation); + using type = uint8_t; + constexpr uint32_t count = 2; + + const auto vk_storage = + vkt.CreateMappedStorage(count, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true); + + const auto hip_ext_mem_desc = vkt.BuildMemoryDescriptor(vk_storage.memory, vk_storage.size); + hipExternalMemory_t hip_ext_memory; + HIP_CHECK(hipImportExternalMemory(&hip_ext_memory, &hip_ext_mem_desc)); + + hipExternalMemoryBufferDesc external_mem_buffer_desc = {}; + constexpr auto offset = (count - 1) * sizeof(type); + external_mem_buffer_desc.size = vk_storage.size - offset; + external_mem_buffer_desc.offset = offset; + + type* hip_dev_ptr = nullptr; + HIP_CHECK(hipExternalMemoryGetMappedBuffer(reinterpret_cast(&hip_dev_ptr), hip_ext_memory, + &external_mem_buffer_desc)); + + vk_storage.host_ptr[0] = 41; + vk_storage.host_ptr[1] = 42; + type read_val = 0; + HIP_CHECK(hipMemcpy(&read_val, hip_dev_ptr, 1, hipMemcpyDeviceToHost)); + REQUIRE(42 == read_val); + + // Defect - EXSWHTEC-181 + // HIP_CHECK(hipFree(hip_dev_ptr)); + HIP_CHECK(hipDestroyExternalMemory(hip_ext_memory)); +} +#endif + +TEST_CASE("Unit_hipExternalMemoryGetMappedBuffer_Vulkan_Negative_Parameters") { + VulkanTest vkt(enable_validation); + const auto vk_storage = vkt.CreateMappedStorage(1, VK_BUFFER_USAGE_TRANSFER_DST_BIT, true); + + const auto hip_ext_mem_desc = vkt.BuildMemoryDescriptor(vk_storage.memory, vk_storage.size); + hipExternalMemory_t hip_ext_memory; + HIP_CHECK(hipImportExternalMemory(&hip_ext_memory, &hip_ext_mem_desc)); + + hipExternalMemoryBufferDesc external_mem_buffer_desc = {}; + external_mem_buffer_desc.size = vk_storage.size; + void* hip_dev_ptr = nullptr; + +// Disabled on AMD due to defect - EXSWHTEC-176 +#if HT_NVIDIA + SECTION("devPtr == nullptr") { + HIP_CHECK_ERROR( + hipExternalMemoryGetMappedBuffer(nullptr, hip_ext_memory, &external_mem_buffer_desc), + hipErrorInvalidValue); + } +#endif + +// Disabled on AMD due to defect - EXSWHTEC-177 +#if HT_NVIDIA + SECTION("bufferDesc == nullptr") { + HIP_CHECK_ERROR(hipExternalMemoryGetMappedBuffer(&hip_dev_ptr, hip_ext_memory, nullptr), + hipErrorInvalidValue); + } +#endif + +// Disabled on AMD due to defect - EXSWHTEC-179 +#if HT_NVIDIA + SECTION("bufferDesc.flags != 0") { + external_mem_buffer_desc.flags = 1; + HIP_CHECK_ERROR( + hipExternalMemoryGetMappedBuffer(&hip_dev_ptr, hip_ext_memory, &external_mem_buffer_desc), + hipErrorInvalidValue); + } +#endif + +// Disabled on AMD due to defect - EXSWHTEC-180 +#if HT_NVIDIA + SECTION("bufferDesc.offset + bufferDesc.size > hipExternalMemHandleDesc.size") { + external_mem_buffer_desc.offset = 1; + HIP_CHECK_ERROR( + hipExternalMemoryGetMappedBuffer(&hip_dev_ptr, hip_ext_memory, &external_mem_buffer_desc), + hipErrorInvalidValue); + } +#endif +} \ No newline at end of file diff --git a/catch/unit/vulkan_interop/vulkan_test.cc b/catch/unit/vulkan_interop/vulkan_test.cc new file mode 100644 index 0000000000..4252916177 --- /dev/null +++ b/catch/unit/vulkan_interop/vulkan_test.cc @@ -0,0 +1,410 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "vulkan_test.hh" + +#include +#include + +VkFence VulkanTest::CreateFence() { + VkFence fence; + VkFenceCreateInfo fence_create_info = {}; + fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_create_info.flags = 0; + VK_CHECK_RESULT(vkCreateFence(_device, &fence_create_info, nullptr, &fence)); + + _fences.push_back(fence); + return fence; +} + +VkSemaphore VulkanTest::CreateExternalSemaphore(VkSemaphoreType sem_type, uint64_t initial_value) { + VkExportSemaphoreCreateInfoKHR export_sem_create_info = {}; + export_sem_create_info.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR; + export_sem_create_info.handleTypes = _sem_handle_type; + + if (sem_type == VK_SEMAPHORE_TYPE_TIMELINE) { + VkSemaphoreTypeCreateInfo timeline_create_info = {}; + timeline_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; + timeline_create_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + timeline_create_info.initialValue = initial_value; + export_sem_create_info.pNext = &timeline_create_info; + } else { + export_sem_create_info.pNext = nullptr; + } + + VkSemaphoreCreateInfo semaphore_create_info = {}; + semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_create_info.pNext = &export_sem_create_info; + + VkSemaphore semaphore; + VK_CHECK_RESULT(vkCreateSemaphore(_device, &semaphore_create_info, nullptr, &semaphore)); + + _semaphores.push_back(semaphore); + return semaphore; +} + +hipExternalSemaphoreHandleDesc VulkanTest::BuildSemaphoreDescriptor(VkSemaphore vk_sem, + VkSemaphoreType sem_type) { + hipExternalSemaphoreHandleDesc sem_handle_desc = {}; + sem_handle_desc.type = VulkanSemHandleTypeToHIPHandleType(sem_type); +#ifdef _WIN64 + sem_handle_desc.handle.win32.handle = GetSemaphoreHandle(vk_sem); +#else + sem_handle_desc.handle.fd = GetSemaphoreHandle(vk_sem); +#endif + sem_handle_desc.flags = 0; + + return sem_handle_desc; +} + +hipExternalMemoryHandleDesc VulkanTest::BuildMemoryDescriptor(VkDeviceMemory vk_mem, + uint32_t size) { + hipExternalMemoryHandleDesc mem_handle_desc = {}; + mem_handle_desc.type = VulkanMemHandleTypeToHIPHandleType(); +#ifdef _WIN64 + mem_handle_desc.handle.win32.handle = GetMemoryHandle(ck_mem); +#else + mem_handle_desc.handle.fd = GetMemoryHandle(vk_mem); +#endif + mem_handle_desc.size = size; + + return mem_handle_desc; +} + +void VulkanTest::CreateInstance() { + UNSCOPED_INFO("Not all of the required instance extensions are supported"); + REQUIRE(CheckExtensionSupport(_required_instance_extensions)); + if (_enable_validation) { + EnableValidationLayer(); + } + + VkApplicationInfo app_info = {}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.apiVersion = VK_API_VERSION_1_2; + + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + create_info.enabledExtensionCount = static_cast(_required_instance_extensions.size()); + create_info.ppEnabledExtensionNames = _required_instance_extensions.data(); + create_info.enabledLayerCount = static_cast(_enabled_layers.size()); + create_info.ppEnabledLayerNames = _enabled_layers.data(); + + VK_CHECK_RESULT(vkCreateInstance(&create_info, nullptr, &_instance)); +} + +void VulkanTest::CreateDevice() { + UNSCOPED_INFO("Not all of the required device extensions are supported"); + REQUIRE(CheckExtensionSupport(_required_device_extensions)); + + FindPhysicalDevice(); + + VkDeviceQueueCreateInfo queue_create_info = {}; + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info.queueFamilyIndex = _compute_family_queue_idx = GetComputeQueueFamilyIndex(); + queue_create_info.queueCount = 1; + float queue_priorities = 1.0; + queue_create_info.pQueuePriorities = &queue_priorities; + + VkPhysicalDeviceVulkan12Features features = {}; + features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + features.timelineSemaphore = true; + + VkDeviceCreateInfo device_create_info = {}; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_create_info.enabledLayerCount = _enabled_layers.size(); + device_create_info.ppEnabledLayerNames = _enabled_layers.data(); + device_create_info.enabledExtensionCount = _required_device_extensions.size(); + device_create_info.ppEnabledExtensionNames = _required_device_extensions.data(); + device_create_info.pQueueCreateInfos = &queue_create_info; + device_create_info.queueCreateInfoCount = 1; + device_create_info.pNext = &features; + + VK_CHECK_RESULT(vkCreateDevice(_physical_device, &device_create_info, nullptr, &_device)); + vkGetDeviceQueue(_device, _compute_family_queue_idx, 0, &_queue); +} + +void VulkanTest::CreateCommandBuffer() { + VkCommandPoolCreateInfo command_pool_create_info = {}; + command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_create_info.flags = 0; + command_pool_create_info.queueFamilyIndex = _compute_family_queue_idx; + VK_CHECK_RESULT(vkCreateCommandPool(_device, &command_pool_create_info, nullptr, &_command_pool)); + + VkCommandBufferAllocateInfo command_buffer_allocate_info = {}; + command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_allocate_info.commandPool = _command_pool; + command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + command_buffer_allocate_info.commandBufferCount = 1; + VK_CHECK_RESULT( + vkAllocateCommandBuffers(_device, &command_buffer_allocate_info, &_command_buffer)); +} + +bool VulkanTest::CheckExtensionSupport(std::vector expected_extensions) { + uint32_t extension_count = 0; + vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr); + std::vector extension_properties(extension_count); + vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, extension_properties.data()); + + std::vector supported_extensions; + supported_extensions.reserve(extension_count); + std::transform(extension_properties.begin(), extension_properties.end(), + std::back_inserter(supported_extensions), + [](const auto& p) { return p.extensionName; }); + + constexpr auto p = [](const char* l, const char* r) { return strcmp(l, r) < 0; }; + std::sort(expected_extensions.begin(), expected_extensions.end(), p); + std::sort(supported_extensions.begin(), supported_extensions.end(), p); + + return std::includes(supported_extensions.begin(), supported_extensions.end(), + expected_extensions.begin(), expected_extensions.end(), + [](const char* l, const char* r) { return strcmp(l, r) == 0; }); +} + +void VulkanTest::EnableValidationLayer() { + uint32_t layer_count = 0; + vkEnumerateInstanceLayerProperties(&layer_count, nullptr); + std::vector layer_properties(layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, layer_properties.data()); + const bool found_val_layer = + std::any_of(layer_properties.cbegin(), layer_properties.cend(), [](const auto& props) { + return strcmp(props.layerName, "VK_LAYER_KHRONOS_validation") == 0; + }); + + + if (found_val_layer) { + _enabled_layers.push_back("VK_LAYER_KHRONOS_validation"); + } else { + UNSCOPED_INFO("Validation was requested, but the validation layer could not be located"); + REQUIRE(found_val_layer); + } +} + +uint32_t VulkanTest::GetComputeQueueFamilyIndex() { + uint32_t queue_family_count = 0u; + + vkGetPhysicalDeviceQueueFamilyProperties(_physical_device, &queue_family_count, nullptr); + std::vector queue_families(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(_physical_device, &queue_family_count, + queue_families.data()); + + const auto it = + std::find_if(queue_families.cbegin(), queue_families.cend(), [](const auto& props) { + return props.queueCount > 0 && (props.queueFlags & VK_QUEUE_COMPUTE_BIT); + }); + REQUIRE(it != queue_families.cend()); + + return std::distance(queue_families.cbegin(), it); +} + +void VulkanTest::FindPhysicalDevice() { + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(_instance, &device_count, nullptr); + REQUIRE(device_count != 0u); + + std::vector physical_devices(device_count); + vkEnumeratePhysicalDevices(_instance, &device_count, physical_devices.data()); + + _physical_device = physical_devices[0]; +} + +uint32_t VulkanTest::FindMemoryType(uint32_t memory_type_bits, VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties(_physical_device, &memory_properties); + for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i) { + if ((memory_type_bits & (1 << i)) && + ((memory_properties.memoryTypes[i].propertyFlags & properties) == properties)) { + return i; + } + } + return VK_MAX_MEMORY_TYPES; +} + +hipExternalSemaphoreHandleType VulkanTest::VulkanSemHandleTypeToHIPHandleType( + VkSemaphoreType sem_type) { + if (sem_type == VK_SEMAPHORE_TYPE_BINARY) { + if (_sem_handle_type & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT) { + return hipExternalSemaphoreHandleTypeOpaqueWin32; + } else if (_sem_handle_type & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) { + return hipExternalSemaphoreHandleTypeOpaqueWin32Kmt; + } else if (_sem_handle_type & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) { + return hipExternalSemaphoreHandleTypeOpaqueFd; + } + } else if (sem_type == VK_SEMAPHORE_TYPE_TIMELINE) { +#if HT_AMD + throw std::invalid_argument("Timeline semaphore unsupported on AMD"); +#else + if (_sem_handle_type & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT) { + return hipExternalSemaphoreHandleTypeTimelineSemaphoreWin32; + } else if (_sem_handle_type & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) { + return hipExternalSemaphoreHandleTypeTimelineSemaphoreWin32; + } else if (_sem_handle_type & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) { + return hipExternalSemaphoreHandleTypeTimelineSemaphoreFd; + } +#endif + } + + throw std::invalid_argument("Invalid vulkan semaphore handle type"); +} + +hipExternalMemoryHandleType VulkanTest::VulkanMemHandleTypeToHIPHandleType() { + if (_mem_handle_type & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) { + return hipExternalMemoryHandleTypeOpaqueWin32; + } else if (_mem_handle_type & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) { + return hipExternalMemoryHandleTypeOpaqueWin32Kmt; + } else if (_mem_handle_type & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) { + return hipExternalMemoryHandleTypeOpaqueFd; + } + + throw std::invalid_argument("Invalid vulkan memory handle type"); +} + +#ifdef _WIN64 +HANDLE +VulkanTest::GetSemaphoreHandle(VkSemaphore semaphore) { + HANDLE handle = 0; + + VkSemaphoreGetWin32HandleInfoKHR semaphoreGetWin32HandleInfoKHR = {}; + semaphoreGetWin32HandleInfoKHR.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR; + semaphoreGetWin32HandleInfoKHR.pNext = NULL; + semaphoreGetWin32HandleInfoKHR.semaphore = semaphore; + semaphoreGetWin32HandleInfoKHR.handleType = _sem_handle_type; + + PFN_vkGetSemaphoreWin32HandleKHR fpGetSemaphoreWin32HandleKHR; + fpGetSemaphoreWin32HandleKHR = (PFN_vkGetSemaphoreWin32HandleKHR)vkGetDeviceProcAddr( + _device, "vkGetSemaphoreWin32HandleKHR"); + if (!fpGetSemaphoreWin32HandleKHR) { + throw std::runtime_error("Failed to retrieve vkGetSemaphoreWin32HandleKHR"); + } + if (fpGetSemaphoreWin32HandleKHR(_device, &semaphoreGetWin32HandleInfoKHR, &handle) != + VK_SUCCESS) { + throw std::runtime_error("Failed to retrieve handle for buffer!"); + } + + return handle; +} +#else +int VulkanTest::GetSemaphoreHandle(VkSemaphore semaphore) { + int fd; + + VkSemaphoreGetFdInfoKHR semaphoreGetFdInfoKHR = {}; + semaphoreGetFdInfoKHR.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + semaphoreGetFdInfoKHR.pNext = NULL; + semaphoreGetFdInfoKHR.semaphore = semaphore; + semaphoreGetFdInfoKHR.handleType = _sem_handle_type; + + PFN_vkGetSemaphoreFdKHR fpGetSemaphoreFdKHR; + fpGetSemaphoreFdKHR = + (PFN_vkGetSemaphoreFdKHR)vkGetDeviceProcAddr(_device, "vkGetSemaphoreFdKHR"); + if (!fpGetSemaphoreFdKHR) { + throw std::runtime_error("Failed to retrieve vkGetSemaphoreFdKHR"); + } + if (fpGetSemaphoreFdKHR(_device, &semaphoreGetFdInfoKHR, &fd) != VK_SUCCESS) { + throw std::runtime_error("Failed to retrieve semaphore handle"); + } + + return fd; +} +#endif + +#ifdef _WIN64 +HANDLE +VulkanTest::GetMemoryHandle(VkDeviceMemory memory) { + Handle handle = 0; + + VkMemoryGetWin32HandleInfoKHR vkMemoryGetWin32HandleInfoKHR = {}; + vkMemoryGetWin32HandleInfoKHR.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + vkMemoryGetWin32HandleInfoKHR.memory = memory; + vkMemoryGetWin32HandleInfoKHR.handleType = _mem_handle_type; + + PFN_vkGetMemoryWin32HandleKHR fpGetMemoryWin32HandleKHR = + (PFN_vkGetMemoryWin32HandleKHR)vkGetDeviceProcAddr(m_device, "vkGetMemoryWin32HandleKHR"); + + if (!fpGetMemoryWin32HandleKHR) { + throw std::runtime_error("Failed to retrieve vkGetMemoryWin32HandleKHR"); + } + if (fpGetMemoryWin32HandleKHR(_device, &vkMemoryGetWin32HandleInfoKHR, &handle) != VK_SUCCESS) { + throw std::runtime_error("Failed to retrieve memory handle"); + } + + return handle; +} +#else +int VulkanTest::GetMemoryHandle(VkDeviceMemory memory) { + int fd; + + VkMemoryGetFdInfoKHR memoryGetFdInfoKHR = {}; + memoryGetFdInfoKHR.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + memoryGetFdInfoKHR.memory = memory; + memoryGetFdInfoKHR.handleType = _mem_handle_type; + + PFN_vkGetMemoryFdKHR fpGetMemoryFdKHR = + (PFN_vkGetMemoryFdKHR)vkGetDeviceProcAddr(_device, "vkGetMemoryFdKHR"); + if (!fpGetMemoryFdKHR) { + throw std::runtime_error("Failed to retrieve vkGetMemoryFdKHR"); + } + if (fpGetMemoryFdKHR(_device, &memoryGetFdInfoKHR, &fd) != VK_SUCCESS) { + throw std::runtime_error("Failed to retrieve memory handle"); + } + + return fd; +} +#endif + +VkExternalSemaphoreHandleTypeFlagBits VulkanTest::GetVkSemHandlePlatformType() const { +#ifdef _WIN64 + return IsWindows8OrGreater() ? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT + : VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; +#else + return VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; +#endif +} + +VkExternalMemoryHandleTypeFlagBits VulkanTest::GetVkMemHandlePlatformType() const { +#ifdef _WIN64 + return IsWindows8OrGreater() ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT + : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT; +#else + return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; +#endif +} + +// Sometimes in CUDA the stream is not immediately ready after a semaphore has been signaled +void PollStream(hipStream_t stream, hipError_t expected, uint32_t num_iterations) { + hipError_t query_result; + for (uint32_t _ = 0; _ < num_iterations; ++_) { + if ((query_result = hipStreamQuery(stream)) != expected) { + std::this_thread::sleep_for(std::chrono::milliseconds{5}); + } else { + break; + } + } + REQUIRE(expected == query_result); +} + +hipExternalSemaphore_t ImportBinarySemaphore(VulkanTest& vkt) { + const auto semaphore = vkt.CreateExternalSemaphore(VK_SEMAPHORE_TYPE_BINARY); + const auto sem_handle_desc = vkt.BuildSemaphoreDescriptor(semaphore, VK_SEMAPHORE_TYPE_BINARY); + hipExternalSemaphore_t hip_ext_semaphore; + HIP_CHECK(hipImportExternalSemaphore(&hip_ext_semaphore, &sem_handle_desc)); + + return hip_ext_semaphore; +} \ No newline at end of file diff --git a/catch/unit/vulkan_interop/vulkan_test.hh b/catch/unit/vulkan_interop/vulkan_test.hh new file mode 100644 index 0000000000..e223e8cd2b --- /dev/null +++ b/catch/unit/vulkan_interop/vulkan_test.hh @@ -0,0 +1,259 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#pragma once + +#include +#include + +#ifdef _WIN64 +#include +#endif + +#include +#include + +#define VK_CHECK_RESULT(code) \ + { \ + VkResult res = (code); \ + if (res != VK_SUCCESS) { \ + INFO("Vulkan error: " << std::to_string(res) << "\n In File: " << __FILE__ \ + << "\n At line: " << __LINE__); \ + REQUIRE(false); \ + } \ + } + +class VulkanTest { + public: + VulkanTest(bool enable_validation) + : _enable_validation{enable_validation}, + _sem_handle_type{GetVkSemHandlePlatformType()}, + _mem_handle_type{GetVkMemHandlePlatformType()} { + CreateInstance(); + CreateDevice(); + CreateCommandBuffer(); + } + + ~VulkanTest() { + for (const auto s : _semaphores) { + vkDestroySemaphore(_device, s, nullptr); + } + + for (const auto f : _fences) { + vkDestroyFence(_device, f, nullptr); + } + + for (const auto& s : _stores) { + vkUnmapMemory(_device, s.memory); + vkDestroyBuffer(_device, s.buffer, nullptr); + vkFreeMemory(_device, s.memory, nullptr); + } + + if (_command_buffer != VK_NULL_HANDLE) + vkFreeCommandBuffers(_device, _command_pool, 1, &_command_buffer); + + if (_command_pool != VK_NULL_HANDLE) vkDestroyCommandPool(_device, _command_pool, nullptr); + + if (_device != VK_NULL_HANDLE) vkDestroyDevice(_device, nullptr); + + if (_instance != VK_NULL_HANDLE) vkDestroyInstance(_instance, nullptr); + } + + VulkanTest(const VulkanTest&) = delete; + + VulkanTest(VulkanTest&&) = delete; + + template struct MappedBuffer { + VkDeviceMemory memory = VK_NULL_HANDLE; + VkBuffer buffer = VK_NULL_HANDLE; + uint32_t size = 0; + T* host_ptr = nullptr; + }; + + template + MappedBuffer CreateMappedStorage(uint32_t count, VkBufferUsageFlagBits transfer_flags, + bool external = false); + + VkFence CreateFence(); + + VkSemaphore CreateExternalSemaphore(VkSemaphoreType sem_type, uint64_t initial_value = 0); + + hipExternalSemaphoreHandleDesc BuildSemaphoreDescriptor(VkSemaphore vk_sem, + VkSemaphoreType sem_type); + + hipExternalMemoryHandleDesc BuildMemoryDescriptor(VkDeviceMemory vk_mem, uint32_t size); + + + VkDevice GetDevice() const { return _device; } + + VkCommandBuffer GetCommandBuffer() const { return _command_buffer; } + + VkQueue GetQueue() const { return _queue; } + + private: + void CreateInstance(); + + void CreateDevice(); + + void CreateCommandBuffer(); + + bool CheckExtensionSupport(std::vector expected_extensions); + + void EnableValidationLayer(); + + uint32_t GetComputeQueueFamilyIndex(); + + void FindPhysicalDevice(); + + uint32_t FindMemoryType(uint32_t memory_type_bits, VkMemoryPropertyFlags properties); + + hipExternalSemaphoreHandleType VulkanSemHandleTypeToHIPHandleType(VkSemaphoreType sem_type); + + hipExternalMemoryHandleType VulkanMemHandleTypeToHIPHandleType(); + +#ifdef _WIN64 + HANDLE + GetSemaphoreHandle(VkSemaphore semaphore); +#else + int GetSemaphoreHandle(VkSemaphore semaphore); +#endif + +#ifdef _WIN64 + HANDLE + GetMemoryHandle(VkDeviceMemory memory); +#else + int GetMemoryHandle(VkDeviceMemory memory); +#endif + + VkExternalSemaphoreHandleTypeFlagBits GetVkSemHandlePlatformType() const; + + VkExternalMemoryHandleTypeFlagBits GetVkMemHandlePlatformType() const; + + struct Storage { + VkBuffer buffer = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + uint32_t size = 0u; + }; + + private: + const bool _enable_validation = false; + const VkExternalSemaphoreHandleTypeFlagBits _sem_handle_type; + const VkExternalMemoryHandleTypeFlagBits _mem_handle_type; + VkInstance _instance = VK_NULL_HANDLE; + VkPhysicalDevice _physical_device = VK_NULL_HANDLE; + VkDevice _device = VK_NULL_HANDLE; + VkQueue _queue = VK_NULL_HANDLE; + VkCommandPool _command_pool = VK_NULL_HANDLE; + VkCommandBuffer _command_buffer = VK_NULL_HANDLE; + uint32_t _compute_family_queue_idx = 0u; + std::vector _enabled_layers; + + std::vector _semaphores; + std::vector _fences; + std::vector _stores; + + std::vector _required_instance_extensions{ + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, + VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME}; +#ifdef _WIN64 + std::vector _required_device_extensions{ + VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME, + VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME}; +#else + std::vector _required_device_extensions{ + VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, + VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME}; +#endif +}; + + +template +VulkanTest::MappedBuffer VulkanTest::CreateMappedStorage(uint32_t count, + VkBufferUsageFlagBits transfer_flags, + bool external) { + Storage storage; + const auto size = count * sizeof(T); + + VkBufferCreateInfo buffer_create_info = {}; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = size; + buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | transfer_flags; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VkExternalMemoryBufferCreateInfo external_memory_buffer_info = {}; + if (external) { + external_memory_buffer_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO; + external_memory_buffer_info.handleTypes = _mem_handle_type; + buffer_create_info.pNext = &external_memory_buffer_info; + } + VK_CHECK_RESULT(vkCreateBuffer(_device, &buffer_create_info, nullptr, &storage.buffer)); + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements(_device, storage.buffer, &memory_requirements); + storage.size = memory_requirements.size; + + VkMemoryAllocateInfo allocate_info = {}; + allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocate_info.allocationSize = memory_requirements.size; + allocate_info.memoryTypeIndex = + FindMemoryType(memory_requirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + REQUIRE(allocate_info.memoryTypeIndex != VK_MAX_MEMORY_TYPES); + + VkExportMemoryAllocateInfoKHR vulkan_export_memory_allocate_info = {}; + if (external) { + vulkan_export_memory_allocate_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + vulkan_export_memory_allocate_info.handleTypes = _mem_handle_type; + +#ifdef _WIN64 + WindowsSecurityAttributes winSecurityAttributes; + + VkExportMemoryWin32HandleInfoKHR vulkanExportMemoryWin32HandleInfoKHR = {}; + vulkanExportMemoryWin32HandleInfoKHR.sType = + VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR; + vulkanExportMemoryWin32HandleInfoKHR.pNext = NULL; + vulkanExportMemoryWin32HandleInfoKHR.pAttributes = &winSecurityAttributes; + vulkanExportMemoryWin32HandleInfoKHR.dwAccess = + DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE; + vulkanExportMemoryWin32HandleInfoKHR.name = (LPCWSTR)NULL; + + vulkan_export_memory_allocate_info.pNext = + _mem_handle_type & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR + ? &vulkanExportMemoryWin32HandleInfoKHR + : NULL; +#endif + allocate_info.pNext = &vulkan_export_memory_allocate_info; + } + + VK_CHECK_RESULT(vkAllocateMemory(_device, &allocate_info, nullptr, &storage.memory)); + VK_CHECK_RESULT(vkBindBufferMemory(_device, storage.buffer, storage.memory, 0)); + + T* host_ptr = nullptr; + VK_CHECK_RESULT(vkMapMemory(_device, storage.memory, 0, storage.size, 0, + reinterpret_cast(&host_ptr))); + + _stores.push_back(storage); + return MappedBuffer{storage.memory, storage.buffer, storage.size, host_ptr}; +} + +// Sometimes in CUDA the stream is not immediately ready after a semaphore has been signaled +void PollStream(hipStream_t stream, hipError_t expected, uint32_t num_iterations = 5); + +hipExternalSemaphore_t ImportBinarySemaphore(VulkanTest& vkt); \ No newline at end of file From 818923bfbce89268c6688628690b7a774daca58a Mon Sep 17 00:00:00 2001 From: nives-vukovic <110852104+nives-vukovic@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:55:54 +0100 Subject: [PATCH 05/14] 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 --- catch/unit/memory/CMakeLists.txt | 4 + .../unit/memory/array_memcpy_tests_common.hh | 442 +++++++++++++ .../unit/memory/hipMemcpy2DFromArrayAsync.cc | 587 ++++++++---------- .../memory/hipMemcpy2DFromArrayAsync_old.cc | 372 +++++++++++ catch/unit/memory/hipMemcpy2DToArrayAsync.cc | 577 ++++++++--------- .../memory/hipMemcpy2DToArrayAsync_old.cc | 369 +++++++++++ 6 files changed, 1681 insertions(+), 670 deletions(-) create mode 100644 catch/unit/memory/array_memcpy_tests_common.hh create mode 100644 catch/unit/memory/hipMemcpy2DFromArrayAsync_old.cc create mode 100644 catch/unit/memory/hipMemcpy2DToArrayAsync_old.cc diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index 6554caec00..d0dba71e6c 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -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 diff --git a/catch/unit/memory/array_memcpy_tests_common.hh b/catch/unit/memory/array_memcpy_tests_common.hh new file mode 100644 index 0000000000..71a02a8aca --- /dev/null +++ b/catch/unit/memory/array_memcpy_tests_common.hh @@ -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 + +#include +#include +#include +#include +#include "hipArrayCommon.hh" + +/* Array -> Host */ +template +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 host_allocation(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard 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 +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 host_allocation(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard 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 +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 host_allocation(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard array_allocation(make_hipExtent(width, height, 0), flag); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard2D 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 +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 host_allocation(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard 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 +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 host_allocation(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard 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 +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 host_allocation(LinearAllocs::hipHostMalloc, allocation_size); + LinearAllocGuard2D device_allocation(width, height); + HIP_CHECK(hipSetDevice(dst_device)); + ArrayAllocGuard 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 +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 +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 host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard 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 +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 host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard array_allocation(make_hipExtent(width, height, 0), flag); + MemcpyArraySyncBehaviorCheck(std::bind(memcpy_func, host_alloc.ptr(), array_allocation.ptr()), + should_sync, kernel_stream); +} + +template +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 host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + ArrayAllocGuard 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 +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 array_allocation(make_hipExtent(width, height, 0), flag); + LinearAllocGuard2D 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 +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 device_allocation(width, height); + ArrayAllocGuard 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 +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 array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard 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 array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard2D device_alloc(width, height); + LinearAllocGuard 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 +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 array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard 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 array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard2D device_alloc(width, height); + LinearAllocGuard 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); + } +} diff --git a/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc b/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc index f7c8e75b71..5dcd4085c6 100644 --- a/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc +++ b/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc @@ -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 #include -#include +#include +#include +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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - HipTest::initArrays(nullptr, nullptr, nullptr, - nullptr, &valData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - 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( + 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(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); - HipTest::freeArrays(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(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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( + 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(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(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&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(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( + 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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); - } else { - SUCCEED("Device Does not have P2P capability"); + SECTION("Peer access enabled") { + Memcpy2DDeviceFromAShell( + 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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - 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( + std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), height, + hipMemcpyDefault, stream), + width, height, stream); + } + SECTION("Peer access enabled") { + Memcpy2DDeviceFromAShell( + 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(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( + std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), 0, + hipMemcpyDeviceToHost, stream), + width, height, stream); + } + SECTION("Width is 0") { + Memcpy2DFromArrayZeroWidthHeight(std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, + 0, height, hipMemcpyDeviceToHost, stream), + width, height, stream); + } + } + SECTION("Array to device") { + SECTION("Height is 0") { + Memcpy2DFromArrayZeroWidthHeight( + std::bind(hipMemcpy2DFromArrayAsync, _1, _2, _3, 0, 0, width * sizeof(int), 0, + hipMemcpyDeviceToDevice, stream), + width, height, stream); + } + SECTION("Width is 0") { + Memcpy2DFromArrayZeroWidthHeight(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 array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard2D device_alloc(width, height); + LinearAllocGuard 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(-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(-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 + } +} diff --git a/catch/unit/memory/hipMemcpy2DFromArrayAsync_old.cc b/catch/unit/memory/hipMemcpy2DFromArrayAsync_old.cc new file mode 100644 index 0000000000..f7c8e75b71 --- /dev/null +++ b/catch/unit/memory/hipMemcpy2DFromArrayAsync_old.cc @@ -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 +#include + + +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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + HipTest::initArrays(nullptr, nullptr, nullptr, + nullptr, &valData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + 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(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); + HipTest::freeArrays(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(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&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(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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + 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(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); +} + diff --git a/catch/unit/memory/hipMemcpy2DToArrayAsync.cc b/catch/unit/memory/hipMemcpy2DToArrayAsync.cc index 990b9c651e..2cf02a3e01 100644 --- a/catch/unit/memory/hipMemcpy2DToArrayAsync.cc +++ b/catch/unit/memory/hipMemcpy2DToArrayAsync.cc @@ -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 #include -#include -#include +#include +#include -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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - 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( + 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(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(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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( + 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(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(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&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(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( + 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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); - } else { - SUCCEED("Machine Does not have P2P capability"); + SECTION("Peer access enabled") { + Memcpy2DDevicetoAShell( + 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(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - 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( + std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), height, + hipMemcpyDefault, stream), + width, height, stream); + } + SECTION("Peer access enabled") { + Memcpy2DDevicetoAShell( + 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(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( + std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), 0, + hipMemcpyHostToDevice, stream), + width, height, stream); + } + SECTION("Width is 0") { + Memcpy2DToArrayZeroWidthHeight(std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, 0, + height, hipMemcpyHostToDevice, stream), + width, height, stream); + } + } + SECTION("Array to device") { + SECTION("Height is 0") { + Memcpy2DToArrayZeroWidthHeight( + std::bind(hipMemcpy2DToArrayAsync, _1, 0, 0, _2, _3, width * sizeof(int), 0, + hipMemcpyDeviceToDevice, stream), + width, height, stream); + } + SECTION("Width is 0") { + Memcpy2DToArrayZeroWidthHeight(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 array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard2D device_alloc(width, height); + LinearAllocGuard 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(-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(-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 + } +} diff --git a/catch/unit/memory/hipMemcpy2DToArrayAsync_old.cc b/catch/unit/memory/hipMemcpy2DToArrayAsync_old.cc new file mode 100644 index 0000000000..990b9c651e --- /dev/null +++ b/catch/unit/memory/hipMemcpy2DToArrayAsync_old.cc @@ -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 +#include +#include + + +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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + 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(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(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&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(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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(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(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(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + 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(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); +} + From 9d62df85a16a0e89130597d57c81f34e79e76891 Mon Sep 17 00:00:00 2001 From: nives-vukovic <110852104+nives-vukovic@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:56:17 +0100 Subject: [PATCH 06/14] EXSWHTEC-91 - Implement tests for memcpy of 1D/2D hipArray (#32) - Implement tests for hipMemcpyAtoH using resource guards and templates - Implement tests for hipMemcpyHtoA using resource guards and templates - Implement tests for hipMemcpy2DFromArray using resource guards and templates - Implement tests for hipMemcpy2DToArray using resource guards and templates --- catch/unit/memory/CMakeLists.txt | 8 + catch/unit/memory/hipMemcpy2DFromArray.cc | 515 ++++++++---------- catch/unit/memory/hipMemcpy2DFromArray_old.cc | 331 +++++++++++ catch/unit/memory/hipMemcpy2DToArray.cc | 511 ++++++++--------- catch/unit/memory/hipMemcpy2DToArray_old.cc | 332 +++++++++++ catch/unit/memory/hipMemcpyAtoH.cc | 263 +++------ catch/unit/memory/hipMemcpyAtoH_old.cc | 219 ++++++++ catch/unit/memory/hipMemcpyHtoA.cc | 268 +++------ catch/unit/memory/hipMemcpyHtoA_old.cc | 229 ++++++++ 9 files changed, 1720 insertions(+), 956 deletions(-) create mode 100644 catch/unit/memory/hipMemcpy2DFromArray_old.cc create mode 100644 catch/unit/memory/hipMemcpy2DToArray_old.cc create mode 100644 catch/unit/memory/hipMemcpyAtoH_old.cc create mode 100644 catch/unit/memory/hipMemcpyHtoA_old.cc diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index d0dba71e6c..c89fae9bfc 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -25,6 +25,7 @@ set(TEST_SRC memset.cc malloc.cc hipMemcpy2DToArray.cc + hipMemcpy2DToArray_old.cc hipMemcpy2DToArrayAsync.cc hipMemcpy2DToArrayAsync_old.cc hipMemcpy3D.cc @@ -34,10 +35,13 @@ set(TEST_SRC hipMemcpy2D.cc hipMemcpy2DAsync.cc hipMemcpy2DFromArray.cc + hipMemcpy2DFromArray_old.cc hipMemcpy2DFromArrayAsync.cc hipMemcpy2DFromArrayAsync_old.cc hipMemcpyAtoH.cc + hipMemcpyAtoH_old.cc hipMemcpyHtoA.cc + hipMemcpyHtoA_old.cc hipMemcpyAllApiNegative.cc hipMemcpy_MultiThread.cc hipHostRegister.cc @@ -112,6 +116,7 @@ set(TEST_SRC memset.cc malloc.cc hipMemcpy2DToArray.cc + hipMemcpy2DToArray_old.cc hipMemcpy2DToArrayAsync.cc hipMemcpy2DToArrayAsync_old.cc hipMemcpy3D.cc @@ -121,10 +126,13 @@ set(TEST_SRC hipMemcpy2D.cc hipMemcpy2DAsync.cc hipMemcpy2DFromArray.cc + hipMemcpy2DFromArray_old.cc hipMemcpy2DFromArrayAsync.cc hipMemcpy2DFromArrayAsync_old.cc hipMemcpyAtoH.cc + hipMemcpyAtoH_old.cc hipMemcpyHtoA.cc + hipMemcpyHtoA_old.cc hipMemcpyAllApiNegative.cc hipMemcpy_MultiThread.cc hipHostRegister.cc diff --git a/catch/unit/memory/hipMemcpy2DFromArray.cc b/catch/unit/memory/hipMemcpy2DFromArray.cc index 1972a5f10a..809e18fb6e 100644 --- a/catch/unit/memory/hipMemcpy2DFromArray.cc +++ b/catch/unit/memory/hipMemcpy2DFromArray.cc @@ -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,316 +16,239 @@ 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 hipMemcpy2DFromArray API -1. Negative Scenarios -2. Extent Validation Scenarios -3. hipMemcpy2DFromArray Basic Scenario -4. Pinned Memory scenarios on same and peer GPU -5. Device Context change scenario where memory is allocated in - one GPU and API is triggered from peer GPU. +Testcase Scenarios : +Unit_hipMemcpy2DFromArray_Positive_Default - Test basic memcpy between 2D array +and host/device with hipMemcpy2DFromArray api +Unit_hipMemcpy2DFromArray_Positive_Synchronization_Behavior - Test +synchronization behavior for hipMemcpy2DFromArray api +Unit_hipMemcpy2DFromArray_Positive_ZeroWidthHeight - Test that no data is copied +when width/height is set to 0 Unit_hipMemcpy2DFromArray_Negative_Parameters - +Test unsuccessful execution of hipMemcpy2DFromArray api when parameters are +invalid */ +#include "array_memcpy_tests_common.hh" +#include #include -#include +#include +#include -static constexpr auto NUM_W{10}; -static constexpr auto NUM_H{10}; -/* - * This testcase verifies device to host copy for hipMemcpy2DFromArray 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_hipMemcpy2DFromArray_Basic") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d{nullptr}; - size_t width{sizeof(float)*NUM_W}; - float *A_h{nullptr}, *hData{nullptr}; - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, - hipMemcpyHostToDevice)); +TEST_CASE("Unit_hipMemcpy2DFromArray_Positive_Default") { + using namespace std::placeholders; - 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)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); -} - -/* - * This testcase verifies the extent validation scenarios - * of hipMemcpy2DFromArray API - */ -TEST_CASE("Unit_hipMemcpy2DFromArray_ExtentValidation") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d{nullptr}; - size_t width{sizeof(float)*NUM_W}; - float *A_h{nullptr}, *hData{nullptr}, *valData{nullptr}; - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - HipTest::initArrays(nullptr, nullptr, nullptr, - nullptr, &valData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - - SECTION("Destination width is 0") { - REQUIRE(hipMemcpy2DFromArray(A_h, 0, A_d, - 0, 0, NUM_W*sizeof(float), - NUM_H, hipMemcpyDeviceToHost) != hipSuccess); - } - // hipMemcpy2DFromArray 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(hipMemcpy2DFromArray(hData, width, A_d, - 0, 0, width, - 0, hipMemcpyDeviceToHost)); - REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true); - } - // hipMemcpy2DFromArray 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(hipMemcpy2DFromArray(hData, width, A_d, - 0, 0, 0, - NUM_H, hipMemcpyDeviceToHost)); - REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true); + SECTION("Array to host") { + Memcpy2DHostFromAShell( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), height, + hipMemcpyDeviceToHost), + width, height); } - // Cleaning the memory - HIP_CHECK(hipFreeArray(A_d)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); - HipTest::freeArrays(nullptr, nullptr, nullptr, - nullptr, valData, nullptr, false); -} -/* - * This Scenario Verifies hipMemcpy2DFromArray 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_hipMemcpy2DFromArray_PinnedMemSameGPU") { - 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}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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(std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, + width * sizeof(int), height, hipMemcpyDefault), + width, height); } - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, PinnMem, - width, width, NUM_H, - hipMemcpyHostToDevice)); - 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)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, nullptr, nullptr, false); -} -/* - * This Scenario Verifies hipMemcpy2DFromArray 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: Intializa A_d with A_h - * Copy A_d->E_h which is a pinned host memory - * OUTPUT: For validating the result,Copying A_d device variable - * --> E_h host variable - * and verifying A_h with E_h - */ -TEST_CASE("Unit_hipMemcpy2DFromArray_multiDevicePinnedMemPeerGpu") { - 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}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, A_h, - width, width, NUM_H, - hipMemcpyHostToDevice)); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&E_h), width * NUM_H)); - for (int i = 0; i < NUM_W*NUM_H; i++) { - E_h[i] = def_val + i; - } - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipMemcpy2DFromArray(E_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)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, nullptr, nullptr, false); - } else { - SUCCEED("Device Does not have P2P capability"); +#if HT_NVIDIA // EXSWHTEC-120 + SECTION("Array to device") { + SECTION("Peer access disabled") { + Memcpy2DDeviceFromAShell( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), height, + hipMemcpyDeviceToDevice), + width, height); } - } else { - SUCCEED("Number of devices are < 2"); - } -} - -/* - * This scenario verifies the hipMemcpy2DFromArray 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_hipMemcpy2DFromArray_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}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, - hipMemcpyHostToDevice)); - - 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)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); - } else { - SUCCEED("Device Does not have P2P capability"); + SECTION("Peer access enabled") { + Memcpy2DDeviceFromAShell( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), height, + hipMemcpyDeviceToDevice), + width, height); } - } else { - SUCCEED("Number of devices are < 2"); - } -} -/* This testcase verifies the negative scenarios of - * hipMemcpy2DFromArray API - */ -TEST_CASE("Unit_hipMemcpy2DFromArray_Negative") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d{nullptr}; - size_t width{sizeof(float)*NUM_W}; - float *A_h{nullptr}, *hData{nullptr}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - - SECTION("Nullptr to destination") { - REQUIRE(hipMemcpy2DFromArray(nullptr, width, A_d, - 0, 0, width, NUM_H, - hipMemcpyDeviceToHost) != hipSuccess); } - SECTION("Nullptr to source") { - REQUIRE(hipMemcpy2DFromArray(A_h, width, nullptr, - 0, 0, width, NUM_H, - hipMemcpyDeviceToHost) != hipSuccess); + SECTION("Array to device with default kind") { + SECTION("Peer access disabled") { + Memcpy2DDeviceFromAShell( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), height, + hipMemcpyDefault), + width, height); + } + SECTION("Peer access enabled") { + Memcpy2DDeviceFromAShell( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), height, + hipMemcpyDefault), + width, height); + } } - - SECTION("Passing offset more than 0") { - REQUIRE(hipMemcpy2DFromArray(A_h, width, A_d, 1, - 1, width, NUM_H, - hipMemcpyDeviceToHost) != hipSuccess); - } - - SECTION("Passing array more than allocated") { - REQUIRE(hipMemcpy2DFromArray(A_h, width, A_d, 0, - 0, width+2, NUM_H+2, - hipMemcpyDeviceToHost) != hipSuccess); - } - - // Cleaning of memory - HIP_CHECK(hipFreeArray(A_d)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); +#endif } +TEST_CASE("Unit_hipMemcpy2DFromArray_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(hipMemcpy2DFromArray, _1, width * sizeof(int), _2, 0, + 0, width * sizeof(int), height, hipMemcpyDeviceToHost), + width, height, true); + MemcpyAtoHPinnedSyncBehavior(std::bind(hipMemcpy2DFromArray, _1, width * sizeof(int), _2, 0, 0, + width * sizeof(int), height, hipMemcpyDeviceToHost), + width, height, true); + } +#if HT_NVIDIA // EXSWHTEC-214 + SECTION("Array to device") { + const auto width = GENERATE(16, 32, 48); + const auto height = GENERATE(16, 32, 48); + + MemcpyAtoDSyncBehavior(std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), + height, hipMemcpyDeviceToDevice), + width, height, false); + } +#endif +} + +TEST_CASE("Unit_hipMemcpy2DFromArray_Positive_ZeroWidthHeight") { + using namespace std::placeholders; + const auto width = 16; + const auto height = 16; + + SECTION("Array to host") { + SECTION("Height is 0") { + Memcpy2DFromArrayZeroWidthHeight( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), 0, + hipMemcpyDeviceToHost), + width, height); + } + SECTION("Width is 0") { + Memcpy2DFromArrayZeroWidthHeight( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, 0, height, hipMemcpyDeviceToHost), + width, height); + } + } + SECTION("Array to device") { + SECTION("Height is 0") { + Memcpy2DFromArrayZeroWidthHeight( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, width * sizeof(int), 0, + hipMemcpyDeviceToDevice), + width, height); + } + SECTION("Width is 0") { + Memcpy2DFromArrayZeroWidthHeight( + std::bind(hipMemcpy2DFromArray, _1, _2, _3, 0, 0, 0, height, hipMemcpyDeviceToDevice), + width, height); + } + } +} + +TEST_CASE("Unit_hipMemcpy2DFromArray_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; + + ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard2D device_alloc(width, height); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + + SECTION("Array to host") { + SECTION("dst == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DFromArray(nullptr, 2 * width * sizeof(int), array_alloc.ptr(), 0, + 0, width * sizeof(int), height, hipMemcpyDeviceToHost), + hipErrorInvalidValue); + } + SECTION("src == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DFromArray(host_alloc.ptr(), 2 * width * sizeof(int), nullptr, 0, 0, + width * sizeof(int), height, hipMemcpyDeviceToHost), + hipErrorInvalidHandle); + } +#if HT_NVIDIA // EXSWHTEC-119 + SECTION("dpitch < width") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(host_alloc.ptr(), width * sizeof(int) - 10, array_alloc.ptr(), 0, 0, + width * sizeof(int), height, hipMemcpyDeviceToHost), + hipErrorInvalidPitchValue); + } + SECTION("Offset + width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(host_alloc.ptr(), 2 * width * sizeof(int), array_alloc.ptr(), 1, 0, + width * sizeof(int), height, hipMemcpyDeviceToHost), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(host_alloc.ptr(), 2 * width * sizeof(int), array_alloc.ptr(), 0, 1, + width * sizeof(int), height, hipMemcpyDeviceToHost), + hipErrorInvalidValue); + } + SECTION("Width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(host_alloc.ptr(), 2 * width * sizeof(int), array_alloc.ptr(), 0, 0, + width * sizeof(int) + 1, height, hipMemcpyDeviceToHost), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(host_alloc.ptr(), 2 * width * sizeof(int), array_alloc.ptr(), 0, 0, + width * sizeof(int), height + 1, hipMemcpyDeviceToHost), + hipErrorInvalidValue); + } + SECTION("Memcpy kind is invalid") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(host_alloc.ptr(), 2 * width * sizeof(int), array_alloc.ptr(), 0, 0, + width * sizeof(int), height, static_cast(-1)), + hipErrorInvalidMemcpyDirection); + } +#endif + } + SECTION("Array to device") { + SECTION("dst == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DFromArray(nullptr, device_alloc.pitch(), array_alloc.ptr(), 0, 0, + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + } + SECTION("src == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DFromArray(device_alloc.ptr(), device_alloc.pitch(), nullptr, 0, 0, + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidHandle); + } +#if HT_NVIDIA // EXSWHTEC-119 + SECTION("dpitch < width") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(device_alloc.ptr(), width * sizeof(int) - 10, array_alloc.ptr(), 0, + 0, width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidPitchValue); + } + SECTION("Offset + width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(device_alloc.ptr(), device_alloc.pitch(), array_alloc.ptr(), 1, 0, + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(device_alloc.ptr(), device_alloc.pitch(), array_alloc.ptr(), 0, 1, + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + } + SECTION("Width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(device_alloc.ptr(), device_alloc.pitch(), array_alloc.ptr(), 0, 0, + width * sizeof(int) + 1, height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(device_alloc.ptr(), device_alloc.pitch(), array_alloc.ptr(), 0, 0, + width * sizeof(int), height + 1, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + } + SECTION("Memcpy kind is invalid") { + HIP_CHECK_ERROR( + hipMemcpy2DFromArray(device_alloc.ptr(), device_alloc.pitch(), array_alloc.ptr(), 0, 0, + width * sizeof(int), height, static_cast(-1)), + hipErrorInvalidMemcpyDirection); + } +#endif + } +} diff --git a/catch/unit/memory/hipMemcpy2DFromArray_old.cc b/catch/unit/memory/hipMemcpy2DFromArray_old.cc new file mode 100644 index 0000000000..1972a5f10a --- /dev/null +++ b/catch/unit/memory/hipMemcpy2DFromArray_old.cc @@ -0,0 +1,331 @@ +/* +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 hipMemcpy2DFromArray API +1. Negative Scenarios +2. Extent Validation Scenarios +3. hipMemcpy2DFromArray Basic Scenario +4. Pinned Memory scenarios on same and peer GPU +5. Device Context change scenario where memory is allocated in + one GPU and API is triggered from peer GPU. +*/ + +#include +#include + +static constexpr auto NUM_W{10}; +static constexpr auto NUM_H{10}; +/* + * This testcase verifies device to host copy for hipMemcpy2DFromArray 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_hipMemcpy2DFromArray_Basic") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d{nullptr}; + size_t width{sizeof(float)*NUM_W}; + float *A_h{nullptr}, *hData{nullptr}; + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, + hipMemcpyHostToDevice)); + + 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)); + HipTest::freeArrays(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); +} + +/* + * This testcase verifies the extent validation scenarios + * of hipMemcpy2DFromArray API + */ +TEST_CASE("Unit_hipMemcpy2DFromArray_ExtentValidation") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d{nullptr}; + size_t width{sizeof(float)*NUM_W}; + float *A_h{nullptr}, *hData{nullptr}, *valData{nullptr}; + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + HipTest::initArrays(nullptr, nullptr, nullptr, + nullptr, &valData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + + SECTION("Destination width is 0") { + REQUIRE(hipMemcpy2DFromArray(A_h, 0, A_d, + 0, 0, NUM_W*sizeof(float), + NUM_H, hipMemcpyDeviceToHost) != hipSuccess); + } + // hipMemcpy2DFromArray 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(hipMemcpy2DFromArray(hData, width, A_d, + 0, 0, width, + 0, hipMemcpyDeviceToHost)); + REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true); + } + // hipMemcpy2DFromArray 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(hipMemcpy2DFromArray(hData, width, A_d, + 0, 0, 0, + NUM_H, hipMemcpyDeviceToHost)); + REQUIRE(HipTest::checkArray(hData, valData, NUM_W, NUM_H) == true); + } + + // Cleaning the memory + HIP_CHECK(hipFreeArray(A_d)); + HipTest::freeArrays(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); + HipTest::freeArrays(nullptr, nullptr, nullptr, + nullptr, valData, nullptr, false); +} +/* + * This Scenario Verifies hipMemcpy2DFromArray 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_hipMemcpy2DFromArray_PinnedMemSameGPU") { + 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}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); + for (int i = 0; i < NUM_W*NUM_H; i++) { + PinnMem[i] = def_val + i; + } + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, PinnMem, + width, width, NUM_H, + hipMemcpyHostToDevice)); + 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)); + HipTest::freeArrays(nullptr, nullptr, nullptr, + A_h, nullptr, nullptr, false); +} +/* + * This Scenario Verifies hipMemcpy2DFromArray 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: Intializa A_d with A_h + * Copy A_d->E_h which is a pinned host memory + * OUTPUT: For validating the result,Copying A_d device variable + * --> E_h host variable + * and verifying A_h with E_h + */ +TEST_CASE("Unit_hipMemcpy2DFromArray_multiDevicePinnedMemPeerGpu") { + 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}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, A_h, + width, width, NUM_H, + hipMemcpyHostToDevice)); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&E_h), width * NUM_H)); + for (int i = 0; i < NUM_W*NUM_H; i++) { + E_h[i] = def_val + i; + } + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipMemcpy2DFromArray(E_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)); + HipTest::freeArrays(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 hipMemcpy2DFromArray 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_hipMemcpy2DFromArray_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}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, + hipMemcpyHostToDevice)); + + 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)); + HipTest::freeArrays(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 + * hipMemcpy2DFromArray API + */ +TEST_CASE("Unit_hipMemcpy2DFromArray_Negative") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d{nullptr}; + size_t width{sizeof(float)*NUM_W}; + float *A_h{nullptr}, *hData{nullptr}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + + SECTION("Nullptr to destination") { + REQUIRE(hipMemcpy2DFromArray(nullptr, width, A_d, + 0, 0, width, NUM_H, + hipMemcpyDeviceToHost) != hipSuccess); + } + + SECTION("Nullptr to source") { + REQUIRE(hipMemcpy2DFromArray(A_h, width, nullptr, + 0, 0, width, NUM_H, + hipMemcpyDeviceToHost) != hipSuccess); + } + + SECTION("Passing offset more than 0") { + REQUIRE(hipMemcpy2DFromArray(A_h, width, A_d, 1, + 1, width, NUM_H, + hipMemcpyDeviceToHost) != hipSuccess); + } + + SECTION("Passing array more than allocated") { + REQUIRE(hipMemcpy2DFromArray(A_h, width, A_d, 0, + 0, width+2, NUM_H+2, + hipMemcpyDeviceToHost) != hipSuccess); + } + + // Cleaning of memory + HIP_CHECK(hipFreeArray(A_d)); + HipTest::freeArrays(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); +} + diff --git a/catch/unit/memory/hipMemcpy2DToArray.cc b/catch/unit/memory/hipMemcpy2DToArray.cc index e75e2c6af3..15a518ce96 100644 --- a/catch/unit/memory/hipMemcpy2DToArray.cc +++ b/catch/unit/memory/hipMemcpy2DToArray.cc @@ -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,317 +16,234 @@ 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 hipMemcpy2DToArray API -1. Negative Scenarios -2. Extent Validation Scenarios -3. hipMemcpy2DToArray Basic Scenario -4. Pinned Memory scenarios on same and peer GPU -5. Device Context change scenario where memory is allocated in - one GPU and API is triggered from peer GPU. +Testcase Scenarios : +Unit_hipMemcpy2DToArray_Positive_Default - Test basic memcpy between host/device +and 2D array with hipMemcpy2DToArray api +Unit_hipMemcpy2DToArray_Positive_Synchronization_Behavior - Test synchronization +behavior for hipMemcpy2DToArray api +Unit_hipMemcpy2DToArray_Positive_ZeroWidthHeight - Test that no data is copied +when width/height is set to 0 Unit_hipMemcpy2DToArray_Negative_Parameters - Test +unsuccessful execution of hipMemcpy2DToArray api when parameters are invalid */ +#include "array_memcpy_tests_common.hh" +#include #include -#include -#include +#include +#include -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_hipMemcpy2DToArray_Basic") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d{nullptr}; - size_t width{sizeof(float)*NUM_W}; - float *A_h{nullptr}, *hData{nullptr}; - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, - hipMemcpyHostToDevice)); +TEST_CASE("Unit_hipMemcpy2DToArray_Positive_Default") { + using namespace std::placeholders; - 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)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); -} - -/* - * This testcase verifies the extent validation scenarios - */ -TEST_CASE("Unit_hipMemcpy2DToArray_ExtentValidation") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d{nullptr}; - size_t width{sizeof(float)*NUM_W}; - float *A_h{nullptr}, *hData{nullptr}; - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - - SECTION("Source width is 0") { - REQUIRE(hipMemcpy2DToArray(A_d, 0, 0, hData, 0, - width, NUM_H, - hipMemcpyHostToDevice) != hipSuccess); - } - // 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(hipMemcpy2DToArray(A_d, 0, 0, - A_h, width, width, - NUM_H, hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, - hData, width, - width, 0, hipMemcpyHostToDevice)); - 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(hipMemcpy2DToArray(A_d, 0, 0, - A_h, width, width, - NUM_H, hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, - hData, width, - 0, NUM_H, hipMemcpyHostToDevice)); - 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(std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, + width * sizeof(int), height, hipMemcpyHostToDevice), + width, height); } - // Cleaning the memory - HIP_CHECK(hipFreeArray(A_d)); - HipTest::freeArrays(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_hipMemcpy2DToArray_PinnedMemSameGPU") { - 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}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(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(std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, + width * sizeof(int), height, hipMemcpyDefault), + width, height); } - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, PinnMem, - width, width, NUM_H, - hipMemcpyHostToDevice)); - 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)); - HipTest::freeArrays(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_hipMemcpy2DToArray_multiDevicePinnedMemPeerGpu") { - 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}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, nullptr, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipHostMalloc(reinterpret_cast(&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, E_h, - width, width, NUM_H, - hipMemcpyHostToDevice)); - 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)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, nullptr, nullptr, false); - } else { - SUCCEED("Machine Does not have P2P capability"); +#if HT_NVIDIA // EXSWHTEC-120 + SECTION("Device to Array") { + SECTION("Peer access disabled") { + Memcpy2DDevicetoAShell( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, width * sizeof(int), height, + hipMemcpyDeviceToDevice), + width, height); } - } 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_hipMemcpy2DToArray_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}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, - hipMemcpyHostToDevice)); - - 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)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); - } else { - SUCCEED("Machine Does not have P2P capability"); + SECTION("Peer access enabled") { + Memcpy2DDevicetoAShell( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, width * sizeof(int), height, + hipMemcpyDeviceToDevice), + width, height); } - } else { - SUCCEED("Number of devices are < 2"); - } -} -/* This testcase verifies the negative scenarios - */ -TEST_CASE("Unit_hipMemcpy2DToArray_Negative") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d{nullptr}; - size_t width{sizeof(float)*NUM_W}; - float *A_h{nullptr}, *hData{nullptr}; - - // Initialization of variables - HipTest::initArrays(nullptr, nullptr, nullptr, - &A_h, &hData, nullptr, - width*NUM_H, false); - HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - - SECTION("Nullptr to destination") { - REQUIRE(hipMemcpy2DToArray(nullptr, 0, 0, hData, width, - width, NUM_H, - hipMemcpyHostToDevice) != hipSuccess); } - SECTION("Nullptr to source") { - REQUIRE(hipMemcpy2DToArray(A_d, 0, 0, - nullptr, width, width, - NUM_H, hipMemcpyHostToDevice) != hipSuccess); + SECTION("Device to Array with default kind") { + SECTION("Peer access disabled") { + Memcpy2DDevicetoAShell( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, width * sizeof(int), height, + hipMemcpyDefault), + width, height); + } + SECTION("Peer access enabled") { + Memcpy2DDevicetoAShell( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, width * sizeof(int), height, + hipMemcpyDefault), + width, height); + } } - - SECTION("Passing offset more than 0") { - REQUIRE(hipMemcpy2DToArray(A_d, 1, 1, - hData, width, width, - NUM_H, hipMemcpyHostToDevice) != hipSuccess); - } - - SECTION("Passing array more than allocated") { - REQUIRE(hipMemcpy2DToArray(A_d, 0, 0, - hData, width, width+2, - NUM_H+2, hipMemcpyHostToDevice) != hipSuccess); - } - - // Cleaning of memory - HIP_CHECK(hipFreeArray(A_d)); - HipTest::freeArrays(nullptr, nullptr, nullptr, - A_h, hData, nullptr, false); +#endif } +TEST_CASE("Unit_hipMemcpy2DToArray_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(hipMemcpy2DToArray, _1, 0, 0, _2, width * sizeof(int), + width * sizeof(int), height, hipMemcpyHostToDevice), + width, height, true); + } +#if HT_NVIDIA // EXSWHTEC-214 + SECTION("Device to Array") { + const auto width = GENERATE(16, 32, 48); + const auto height = GENERATE(16, 32, 48); + + MemcpyDtoASyncBehavior(std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, width * sizeof(int), + height, hipMemcpyDeviceToDevice), + width, height, false); + } +#endif +} + +TEST_CASE("Unit_hipMemcpy2DToArray_Positive_ZeroWidthHeight") { + using namespace std::placeholders; + const auto width = 16; + const auto height = 16; + + SECTION("Array to host") { + SECTION("Height is 0") { + Memcpy2DToArrayZeroWidthHeight( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, width * sizeof(int), 0, + hipMemcpyHostToDevice), + width, height); + } + SECTION("Width is 0") { + Memcpy2DToArrayZeroWidthHeight( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, 0, height, hipMemcpyHostToDevice), width, + height); + } + } + SECTION("Array to device") { + SECTION("Height is 0") { + Memcpy2DToArrayZeroWidthHeight( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, width * sizeof(int), 0, + hipMemcpyDeviceToDevice), + width, height); + } + SECTION("Width is 0") { + Memcpy2DToArrayZeroWidthHeight( + std::bind(hipMemcpy2DToArray, _1, 0, 0, _2, _3, 0, height, hipMemcpyDeviceToDevice), + width, height); + } + } +} + +TEST_CASE("Unit_hipMemcpy2DToArray_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; + + ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard2D device_alloc(width, height); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + + SECTION("Host to Array") { + SECTION("dst == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DToArray(nullptr, 0, 0, host_alloc.ptr(), 2 * width * sizeof(int), + width * sizeof(int), height, hipMemcpyHostToDevice), + hipErrorInvalidHandle); + } + SECTION("src == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, nullptr, 2 * width * sizeof(int), + width * sizeof(int), height, hipMemcpyHostToDevice), + hipErrorInvalidValue); + } +#if HT_NVIDIA // EXSWHTEC-119 + SECTION("spitch < width") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.ptr(), width * sizeof(int) - 10, + width * sizeof(int), height, hipMemcpyHostToDevice), + hipErrorInvalidPitchValue); + } + SECTION("Offset + width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 1, 0, host_alloc.ptr(), 2 * width * sizeof(int), + width * sizeof(int), height, hipMemcpyHostToDevice), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 1, host_alloc.ptr(), 2 * width * sizeof(int), + width * sizeof(int), height, hipMemcpyHostToDevice), + hipErrorInvalidValue); + } + SECTION("Width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.ptr(), 2 * width * sizeof(int), + width * sizeof(int) + 1, height, hipMemcpyHostToDevice), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.ptr(), 2 * width * sizeof(int), + width * sizeof(int), height + 1, hipMemcpyHostToDevice), + hipErrorInvalidValue); + } + SECTION("Memcpy kind is invalid") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.ptr(), 2 * width * sizeof(int), + width * sizeof(int), height, static_cast(-1)), + hipErrorInvalidMemcpyDirection); + } +#endif + } + SECTION("Device to Array") { + SECTION("dst == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DToArray(nullptr, 0, 0, device_alloc.ptr(), device_alloc.pitch(), + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidHandle); + } + SECTION("src == nullptr") { + HIP_CHECK_ERROR(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, nullptr, device_alloc.pitch(), + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + } +#if HT_NVIDIA // EXSWHTEC-119 + SECTION("spitch < width") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, device_alloc.ptr(), width * sizeof(int) - 10, + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidPitchValue); + } + SECTION("Offset + width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 1, 0, device_alloc.ptr(), device_alloc.pitch(), + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 1, device_alloc.ptr(), device_alloc.pitch(), + width * sizeof(int), height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + } + SECTION("Width/height overflows") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, device_alloc.ptr(), device_alloc.pitch(), + width * sizeof(int) + 1, height, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, device_alloc.ptr(), device_alloc.pitch(), + width * sizeof(int), height + 1, hipMemcpyDeviceToDevice), + hipErrorInvalidValue); + } + SECTION("Memcpy kind is invalid") { + HIP_CHECK_ERROR( + hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, device_alloc.ptr(), device_alloc.pitch(), + width * sizeof(int), height, static_cast(-1)), + hipErrorInvalidMemcpyDirection); + } +#endif + } +} diff --git a/catch/unit/memory/hipMemcpy2DToArray_old.cc b/catch/unit/memory/hipMemcpy2DToArray_old.cc new file mode 100644 index 0000000000..e75e2c6af3 --- /dev/null +++ b/catch/unit/memory/hipMemcpy2DToArray_old.cc @@ -0,0 +1,332 @@ +/* +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 hipMemcpy2DToArray API +1. Negative Scenarios +2. Extent Validation Scenarios +3. hipMemcpy2DToArray Basic Scenario +4. Pinned Memory scenarios on same and peer GPU +5. Device Context change scenario where memory is allocated in + one GPU and API is triggered from peer GPU. +*/ + +#include +#include +#include + +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_hipMemcpy2DToArray_Basic") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d{nullptr}; + size_t width{sizeof(float)*NUM_W}; + float *A_h{nullptr}, *hData{nullptr}; + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, + hipMemcpyHostToDevice)); + + 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)); + HipTest::freeArrays(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); +} + +/* + * This testcase verifies the extent validation scenarios + */ +TEST_CASE("Unit_hipMemcpy2DToArray_ExtentValidation") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d{nullptr}; + size_t width{sizeof(float)*NUM_W}; + float *A_h{nullptr}, *hData{nullptr}; + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + + SECTION("Source width is 0") { + REQUIRE(hipMemcpy2DToArray(A_d, 0, 0, hData, 0, + width, NUM_H, + hipMemcpyHostToDevice) != hipSuccess); + } + // 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(hipMemcpy2DToArray(A_d, 0, 0, + A_h, width, width, + NUM_H, hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, + hData, width, + width, 0, hipMemcpyHostToDevice)); + 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(hipMemcpy2DToArray(A_d, 0, 0, + A_h, width, width, + NUM_H, hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, + hData, width, + 0, NUM_H, hipMemcpyHostToDevice)); + 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)); + HipTest::freeArrays(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_hipMemcpy2DToArray_PinnedMemSameGPU") { + 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}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&PinnMem), width * NUM_H)); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); + for (int i = 0; i < NUM_W*NUM_H; i++) { + PinnMem[i] = def_val + i; + } + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, PinnMem, + width, width, NUM_H, + hipMemcpyHostToDevice)); + 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)); + HipTest::freeArrays(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_hipMemcpy2DToArray_multiDevicePinnedMemPeerGpu") { + 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}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, nullptr, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, nullptr, nullptr); + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipHostMalloc(reinterpret_cast(&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, E_h, + width, width, NUM_H, + hipMemcpyHostToDevice)); + 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)); + HipTest::freeArrays(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_hipMemcpy2DToArray_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}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, + hipMemcpyHostToDevice)); + + 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)); + HipTest::freeArrays(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_hipMemcpy2DToArray_Negative") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d{nullptr}; + size_t width{sizeof(float)*NUM_W}; + float *A_h{nullptr}, *hData{nullptr}; + + // Initialization of variables + HipTest::initArrays(nullptr, nullptr, nullptr, + &A_h, &hData, nullptr, + width*NUM_H, false); + HipTest::setDefaultData(width*NUM_H, A_h, hData, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + + SECTION("Nullptr to destination") { + REQUIRE(hipMemcpy2DToArray(nullptr, 0, 0, hData, width, + width, NUM_H, + hipMemcpyHostToDevice) != hipSuccess); + } + + SECTION("Nullptr to source") { + REQUIRE(hipMemcpy2DToArray(A_d, 0, 0, + nullptr, width, width, + NUM_H, hipMemcpyHostToDevice) != hipSuccess); + } + + SECTION("Passing offset more than 0") { + REQUIRE(hipMemcpy2DToArray(A_d, 1, 1, + hData, width, width, + NUM_H, hipMemcpyHostToDevice) != hipSuccess); + } + + SECTION("Passing array more than allocated") { + REQUIRE(hipMemcpy2DToArray(A_d, 0, 0, + hData, width, width+2, + NUM_H+2, hipMemcpyHostToDevice) != hipSuccess); + } + + // Cleaning of memory + HIP_CHECK(hipFreeArray(A_d)); + HipTest::freeArrays(nullptr, nullptr, nullptr, + A_h, hData, nullptr, false); +} + diff --git a/catch/unit/memory/hipMemcpyAtoH.cc b/catch/unit/memory/hipMemcpyAtoH.cc index d8d28432e3..b97759a913 100644 --- a/catch/unit/memory/hipMemcpyAtoH.cc +++ b/catch/unit/memory/hipMemcpyAtoH.cc @@ -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 @@ -17,164 +17,43 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* - * Test Scenarios: - * 1. Perform host and Pinned host Memory - * 2. Perform bytecount 0 validation for hipMemcpyAtoH API - * 3. Allocate Memory from one GPU device and call hipMemcpyAtoH from Peer - * GPU device - * 4. Perform hipMemcpyAtoH Negative Scenarios - */ +Testcase Scenarios : +Unit_hipMemcpy2DFromArray_Positive_Default - Test basic memcpy between 2D array +and host/device with hipMemcpy2DFromArray api +Unit_hipMemcpy2DFromArray_Positive_Synchronization_Behavior - Test +synchronization behavior for hipMemcpy2DFromArray api +Unit_hipMemcpy2DFromArray_Positive_ZeroWidthHeight - Test that no data is copied +when width/height is set to 0 Unit_hipMemcpy2DFromArray_Negative_Parameters - +Test unsuccessful execution of hipMemcpy2DFromArray api when parameters are +invalid +*/ +#include "array_memcpy_tests_common.hh" +#include #include -#include +#include +#include +TEST_CASE("Unit_hipMemcpyAtoH_Positive_Default") { + using namespace std::placeholders; -static constexpr auto NUM_W{10}; -static constexpr auto NUM_H{1}; -static constexpr auto copy_bytes{2}; + const auto width = GENERATE(512, 1024, 2048); + const auto allocation_size = width * sizeof(int); -/* -This testcase performs the basic and pinned host memory scenarios -of hipMemcpyAtoH API -Input: "A_d" initialized with "hData" Pi value -Output:"B_h" host variable output of hipMemcpyAtoH API - is then validated with "hData" - -The same scenario is then verified with pinned host memory -*/ - -TEMPLATE_TEST_CASE("Unit_hipMemcpyAtoH_Basic", "[hipMemcpyAtoH]", - char, int, float) { - HIP_CHECK(hipSetDevice(0)); - // 1 refers to pinned host memory scenario - auto memtype_check = GENERATE(0, 1); - hipArray *A_d; - TestType *hData{nullptr}, *B_h{nullptr}; - size_t width{NUM_W * sizeof(TestType)}; - - // Initialization of data - if (memtype_check) { - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W, true); - } else { - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - } - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); - - // Performing API call - REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, copy_bytes*sizeof(TestType)) - == hipSuccess); - - // Validating the result - REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); - - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - if (memtype_check) { - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - nullptr, true) == true); - } else { - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - nullptr, false) == true); - } + MemcpyAtoHShell(std::bind(hipMemcpyAtoH, _1, _2, 0, allocation_size), width); } -/* -This testcase performs the basic and pinned host memory scenarios -of hipMemcpyAtoH API -Memory is allocated in GPU-0 and the API is triggered from GPU-1 -Input: "A_d" initialized with "hData" Pi value -Output:"B_h" host variable output of hipMemcpyAtoH API - is then validated with "hData" -*/ -#if HT_AMD -TEMPLATE_TEST_CASE("Unit_hipMemcpyAtoH_multiDevice-PeerDeviceContext", - "[hipMemcpyAtoH]", - char, int, float) { - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - int peerAccess = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&peerAccess, 1, 0)); - if (!peerAccess) { - SUCCEED("Skipped the test as there is no peer access"); - } else { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d; - TestType *hData{nullptr}, *B_h{nullptr}; - size_t width{NUM_W * sizeof(TestType)}; +TEST_CASE("Unit_hipMemcpyAtoH_Positive_Synchronization_Behavior") { + using namespace std::placeholders; - // Initialization of data - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); + const auto width = GENERATE(512, 1024, 2048); + const auto height = 0; + const auto allocation_size = width * sizeof(int); - HIP_CHECK(hipDeviceSynchronize()); - // Changing the device context - HIP_CHECK(hipSetDevice(1)); - - // Performing API call - REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, copy_bytes*sizeof(TestType)) - == hipSuccess); - // Validating the result - REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); - - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, - hData, B_h, - nullptr, false) == true); - } - } else { - SUCCEED("skipping the testcases as numDevices < 2"); - } -} -#endif -/* -This testcase verifies the negative scenarios of hipMemcpyAtoH API -*/ -TEST_CASE("Unit_hipMemcpyAtoH_Negative") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d; - float *hData{nullptr}, *B_h{nullptr}; - size_t width{NUM_W * sizeof(float)}; - - // Initialization of data - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); - - SECTION("Destination pointer is nullptr") { - REQUIRE(hipMemcpyAtoH(nullptr, A_d, 0, copy_bytes*sizeof(float)) - != hipSuccess); - } - - SECTION("Source offset is more than allocated size") { - REQUIRE(hipMemcpyAtoH(B_h, A_d, 100, copy_bytes*sizeof(float)) - != hipSuccess); - } - - SECTION("ByteCount is greater than allocated size") { - REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, 12*sizeof(float)) != hipSuccess); - } - - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - nullptr, false) == true); + MemcpyAtoHPageableSyncBehavior(std::bind(hipMemcpyAtoH, _1, _2, 0, allocation_size), width, + height, true); + MemcpyAtoHPinnedSyncBehavior(std::bind(hipMemcpyAtoH, _1, _2, 0, allocation_size), width, height, + true); } /* @@ -183,37 +62,65 @@ Excluded the testcase for amd,as there is already a bug raised SWDEV-274683 */ #if HT_NVIDIA -TEST_CASE("Unit_hipMemcpyAtoH_SizeCheck") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d; - float *hData{nullptr}, *B_h{nullptr}, *def_data{nullptr}; - size_t width{NUM_W * sizeof(float)}; +TEST_CASE("Unit_hipMemcpyAtoH_Positive_ZeroCount") { + const auto width = 1024; + const auto height = 0; + const auto allocation_size = width * sizeof(int); - // Initialization of data - HipTest::initArrays(nullptr, nullptr, nullptr, - nullptr, &def_data, nullptr, NUM_W); - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - HipTest::setDefaultData(NUM_W, nullptr, def_data, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); + const unsigned int flag = hipArrayDefault; - SECTION("Passing 0 to copy bytes") { - REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, 0) == hipSuccess); - REQUIRE(HipTest::checkArray(B_h, def_data, NUM_W, NUM_H) == true); - } + ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); - SECTION(" Source Array is nullptr") { - REQUIRE(hipMemcpyAtoH(B_h, nullptr, 0, copy_bytes*sizeof(float)) - != hipSuccess); - } + int fill_value = 42; + std::fill_n(host_alloc.host_ptr(), width, fill_value); + HIP_CHECK(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.host_ptr(), sizeof(int) * width, + sizeof(int) * width, 1, hipMemcpyHostToDevice)); + fill_value = 41; + std::fill_n(host_alloc.host_ptr(), width, fill_value); + HIP_CHECK(hipMemcpyAtoH(host_alloc.ptr(), array_alloc.ptr(), 0, 0)); - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - def_data, false) == true); + ArrayFindIfNot(host_alloc.host_ptr(), static_cast(fill_value), width); } #endif + +TEST_CASE("Unit_hipMemcpyAtoH_Negative_Parameters") { + using namespace std::placeholders; + + const auto width = 1024; + const auto height = 0; + const auto allocation_size = width * sizeof(int); + + const unsigned int flag = hipArrayDefault; + + ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + + SECTION("dst == nullptr") { + HIP_CHECK_ERROR(hipMemcpyAtoH(nullptr, array_alloc.ptr(), 0, allocation_size), + hipErrorInvalidValue); + } + SECTION("src == nullptr") { + HIP_CHECK_ERROR(hipMemcpyAtoH(host_alloc.ptr(), nullptr, 0, allocation_size), + hipErrorInvalidValue); + } + SECTION("Offset is greater than allocated size") { + HIP_CHECK_ERROR( + hipMemcpyAtoH(host_alloc.ptr(), array_alloc.ptr(), allocation_size + 10, allocation_size), + hipErrorInvalidValue); + } + SECTION("Count is greater than allocated size") { + HIP_CHECK_ERROR(hipMemcpyAtoH(host_alloc.ptr(), array_alloc.ptr(), 0, allocation_size + 10), + hipErrorInvalidValue); + } + SECTION("2D array is allocated") { + const auto width_2d = 32; + const auto height_2d = width_2d; + const auto allocation_size_2d = width_2d * height_2d * sizeof(int); + + ArrayAllocGuard array_alloc_2d(make_hipExtent(width_2d, height_2d, 0), flag); + LinearAllocGuard host_alloc_2d(LinearAllocs::hipHostMalloc, allocation_size_2d); + HIP_CHECK_ERROR(hipMemcpyAtoH(host_alloc_2d.ptr(), array_alloc_2d.ptr(), 0, allocation_size_2d), + hipErrorInvalidValue); + } +} diff --git a/catch/unit/memory/hipMemcpyAtoH_old.cc b/catch/unit/memory/hipMemcpyAtoH_old.cc new file mode 100644 index 0000000000..d8d28432e3 --- /dev/null +++ b/catch/unit/memory/hipMemcpyAtoH_old.cc @@ -0,0 +1,219 @@ +/* +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. +*/ +/* + * Test Scenarios: + * 1. Perform host and Pinned host Memory + * 2. Perform bytecount 0 validation for hipMemcpyAtoH API + * 3. Allocate Memory from one GPU device and call hipMemcpyAtoH from Peer + * GPU device + * 4. Perform hipMemcpyAtoH Negative Scenarios + */ + +#include +#include + + +static constexpr auto NUM_W{10}; +static constexpr auto NUM_H{1}; +static constexpr auto copy_bytes{2}; + +/* +This testcase performs the basic and pinned host memory scenarios +of hipMemcpyAtoH API +Input: "A_d" initialized with "hData" Pi value +Output:"B_h" host variable output of hipMemcpyAtoH API + is then validated with "hData" + +The same scenario is then verified with pinned host memory +*/ + +TEMPLATE_TEST_CASE("Unit_hipMemcpyAtoH_Basic", "[hipMemcpyAtoH]", + char, int, float) { + HIP_CHECK(hipSetDevice(0)); + // 1 refers to pinned host memory scenario + auto memtype_check = GENERATE(0, 1); + hipArray *A_d; + TestType *hData{nullptr}, *B_h{nullptr}; + size_t width{NUM_W * sizeof(TestType)}; + + // Initialization of data + if (memtype_check) { + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W, true); + } else { + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + } + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + // Performing API call + REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, copy_bytes*sizeof(TestType)) + == hipSuccess); + + // Validating the result + REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + if (memtype_check) { + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + nullptr, true) == true); + } else { + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + nullptr, false) == true); + } +} + +/* +This testcase performs the basic and pinned host memory scenarios +of hipMemcpyAtoH API +Memory is allocated in GPU-0 and the API is triggered from GPU-1 +Input: "A_d" initialized with "hData" Pi value +Output:"B_h" host variable output of hipMemcpyAtoH API + is then validated with "hData" +*/ +#if HT_AMD +TEMPLATE_TEST_CASE("Unit_hipMemcpyAtoH_multiDevice-PeerDeviceContext", + "[hipMemcpyAtoH]", + char, int, float) { + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + int peerAccess = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&peerAccess, 1, 0)); + if (!peerAccess) { + SUCCEED("Skipped the test as there is no peer access"); + } else { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d; + TestType *hData{nullptr}, *B_h{nullptr}; + size_t width{NUM_W * sizeof(TestType)}; + + // Initialization of data + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + HIP_CHECK(hipDeviceSynchronize()); + // Changing the device context + HIP_CHECK(hipSetDevice(1)); + + // Performing API call + REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, copy_bytes*sizeof(TestType)) + == hipSuccess); + // Validating the result + REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, + hData, B_h, + nullptr, false) == true); + } + } else { + SUCCEED("skipping the testcases as numDevices < 2"); + } +} +#endif +/* +This testcase verifies the negative scenarios of hipMemcpyAtoH API +*/ +TEST_CASE("Unit_hipMemcpyAtoH_Negative") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d; + float *hData{nullptr}, *B_h{nullptr}; + size_t width{NUM_W * sizeof(float)}; + + // Initialization of data + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + SECTION("Destination pointer is nullptr") { + REQUIRE(hipMemcpyAtoH(nullptr, A_d, 0, copy_bytes*sizeof(float)) + != hipSuccess); + } + + SECTION("Source offset is more than allocated size") { + REQUIRE(hipMemcpyAtoH(B_h, A_d, 100, copy_bytes*sizeof(float)) + != hipSuccess); + } + + SECTION("ByteCount is greater than allocated size") { + REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, 12*sizeof(float)) != hipSuccess); + } + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + nullptr, false) == true); +} + +/* +This testcase verifies size 0 check of hipMemcpyAtoH API +Excluded the testcase for amd,as there is already a bug raised +SWDEV-274683 +*/ +#if HT_NVIDIA +TEST_CASE("Unit_hipMemcpyAtoH_SizeCheck") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d; + float *hData{nullptr}, *B_h{nullptr}, *def_data{nullptr}; + size_t width{NUM_W * sizeof(float)}; + + // Initialization of data + HipTest::initArrays(nullptr, nullptr, nullptr, + nullptr, &def_data, nullptr, NUM_W); + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + HipTest::setDefaultData(NUM_W, nullptr, def_data, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + SECTION("Passing 0 to copy bytes") { + REQUIRE(hipMemcpyAtoH(B_h, A_d, 0, 0) == hipSuccess); + REQUIRE(HipTest::checkArray(B_h, def_data, NUM_W, NUM_H) == true); + } + + SECTION(" Source Array is nullptr") { + REQUIRE(hipMemcpyAtoH(B_h, nullptr, 0, copy_bytes*sizeof(float)) + != hipSuccess); + } + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + def_data, false) == true); +} +#endif diff --git a/catch/unit/memory/hipMemcpyHtoA.cc b/catch/unit/memory/hipMemcpyHtoA.cc index 8712704400..e144e261f6 100644 --- a/catch/unit/memory/hipMemcpyHtoA.cc +++ b/catch/unit/memory/hipMemcpyHtoA.cc @@ -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,171 +16,41 @@ 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. */ - /* - * Test Scenarios: - * 1. Perform simple and pinned host memory of hipMemcpyHtoA API - * 2. Allocate Memory from one GPU device and call hipMemcpyHtoA from Peer - * GPU device - * 3. Perform hipMemcpyHtoA Negative Scenarios - * 4. Perform bytecount 0 validation for hipMemcpyHtoA API +Testcase Scenarios : +Unit_hipMemcpyHtoA_Positive_Default - Test basic memcpy between host and 1D +array with hipMemcpyHtoA api +Unit_hipMemcpyHtoA_Positive_Synchronization_Behavior - Test synchronization +behavior for hipMemcpyHtoA api Unit_hipMemcpyHtoA_Positive_ZeroCount - Test that +no data is copied when allocation_size is set to 0 +Unit_hipMemcpyHtoA_Negative_Parameters - Test unsuccessful execution of +hipMemcpyHtoA api when parameters are invalid */ +#include "array_memcpy_tests_common.hh" +#include #include -#include +#include +#include -static constexpr auto NUM_W{10}; -static constexpr auto NUM_H{1}; -static constexpr auto copy_bytes{2}; +TEST_CASE("Unit_hipMemcpyHtoA_Positive_Default") { + using namespace std::placeholders; -/* -This testcase performs the basic and pinned host memory scenarios -of hipMemcpyHtoA API -Input: "B_h" which is initialized with 1.6 -Output: "A_d" output of hipMemcpyHtoA is copied to "hData" host variable - validated the result with "B_h" + const auto width = GENERATE(512, 1024, 2048); + const auto allocation_size = width * sizeof(int); -The same scenario is then verified with pinned host memory -*/ -TEMPLATE_TEST_CASE("Unit_hipMemcpyHtoA_Basic", "[hipMemcpyHtoA]", - char, int, float) { - HIP_CHECK(hipSetDevice(0)); - auto memtype_check = GENERATE(0, 1); - hipArray *A_d; - TestType *hData{nullptr}, *B_h{nullptr}; - size_t width{NUM_W * sizeof(TestType)}; - - // Initialization of data - if (memtype_check) { - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W, true); - } else { - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - } - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); - - // Performing API call - HIP_CHECK(hipMemcpyHtoA(A_d, 0, B_h, copy_bytes*sizeof(TestType))); - HIP_CHECK(hipMemcpy2DFromArray(hData, sizeof(TestType)*NUM_W, A_d, - 0, 0, sizeof(TestType)*NUM_W, 1, hipMemcpyDeviceToHost)); - - - // Validating the result - REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); - - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - if (memtype_check) { - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - nullptr, true) == true); - } else { - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - nullptr, false) == true); - } + MemcpyHtoAShell(std::bind(hipMemcpyHtoA, _1, 0, _2, allocation_size), width); } +TEST_CASE("Unit_hipMemcpyHtoA_Positive_Synchronization_Behavior") { + using namespace std::placeholders; -/* -This testcase performs the peer device context scenario -of hipMemcpyHtoA API -Memory is allocated in GPU-0 and the API is triggered from GPU-1 -Input: "B_h" which is initialized with 1.6 -Output: "A_d" output of hipMemcpyHtoA is copied to "hData" host variable - validated the result with "B_h" -*/ -#if HT_AMD -TEMPLATE_TEST_CASE("Unit_hipMemcpyHtoA_multiDevice-PeerDeviceContext", - "[hipMemcpyHtoA]", - char, int, float) { - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - int peerAccess = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&peerAccess, 1, 0)); - if (!peerAccess) { - SUCCEED("Skipped the test as there is no peer access"); - } else { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d; - TestType *hData{nullptr}, *B_h{nullptr}; - size_t width{NUM_W * sizeof(TestType)}; + const auto width = GENERATE(512, 1024, 2048); + const auto height = 0; + const auto allocation_size = width * sizeof(int); - // Initialization of data - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); - - // Changing the device context - HIP_CHECK(hipSetDevice(1)); - - // Performing API call - HIP_CHECK(hipMemcpyHtoA(A_d, 0, B_h, copy_bytes*sizeof(TestType))); - HIP_CHECK(hipMemcpy2DFromArray(hData, sizeof(TestType)*NUM_W, A_d, - 0, 0, sizeof(TestType)*NUM_W, 1, - hipMemcpyDeviceToHost)); - - // Validating the result - REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); - - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, - hData, B_h, - nullptr, false) == true); - } - } else { - SUCCEED("skipping the testcases as numDevices < 2"); - } -} -#endif - - -/* -This testcase verifies the negative scenarios of hipMemcpyHtoA API -*/ -TEST_CASE("Unit_hipMemcpyHtoA_Negative") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d; - float *hData{nullptr}, *B_h{nullptr}; - size_t width{NUM_W * sizeof(float)}; - - // Initialization of data - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); - - SECTION("Source pointer is nullptr") { - REQUIRE(hipMemcpyHtoA(A_d, 0, nullptr, copy_bytes*sizeof(float)) - != hipSuccess); - } - - SECTION("Source offset is more than allocated size") { - REQUIRE(hipMemcpyHtoA(A_d, 100, B_h, copy_bytes*sizeof(float)) - != hipSuccess); - } - - SECTION("ByteCount is greater than allocated size") { - REQUIRE(hipMemcpyHtoA(A_d, 0, B_h, 12*sizeof(float)) != hipSuccess); - } - - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - nullptr, false) == true); + MemcpyHtoASyncBehavior(std::bind(hipMemcpyHtoA, _1, 0, _2, allocation_size), width, height, true); } /* @@ -189,41 +59,69 @@ This is excluded for AMD as we have a bug already raised SWDEV-274683 */ #if HT_NVIDIA -TEST_CASE("Unit_hipMemcpyHtoA_SizeCheck") { - HIP_CHECK(hipSetDevice(0)); - hipArray *A_d; - float *hData{nullptr}, *B_h{nullptr}, *def_data{nullptr}; - size_t width{NUM_W * sizeof(float)}; +TEST_CASE("Unit_hipMemcpyHtoA_Positive_ZeroCount") { + const auto width = 1024; + const auto height = 0; + const auto allocation_size = width * sizeof(int); - // Initialization of data - HipTest::initArrays(nullptr, nullptr, nullptr, - nullptr, &def_data, nullptr, NUM_W); - HipTest::initArrays(nullptr, nullptr, nullptr, - &hData, &B_h, nullptr, NUM_W); - HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); - HipTest::setDefaultData(NUM_W, nullptr, def_data, nullptr); - hipChannelFormatDesc desc = hipCreateChannelDesc(); - HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); - HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, - width, NUM_H, hipMemcpyHostToDevice)); + const unsigned int flag = hipArrayDefault; - SECTION("Passing 0 to copy bytes") { - REQUIRE(hipMemcpyHtoA(A_d, 0, B_h, 0) == hipSuccess); - HIP_CHECK(hipMemcpy2DFromArray(def_data, sizeof(float)*NUM_W, A_d, - 0, 0, sizeof(float)*NUM_W, 1, - hipMemcpyDeviceToHost)); + ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); - REQUIRE(HipTest::checkArray(hData, def_data, NUM_W, NUM_H) == true); - } + int fill_value = 42; + std::fill_n(host_alloc.host_ptr(), width, fill_value); + HIP_CHECK(hipMemcpy2DToArray(array_alloc.ptr(), 0, 0, host_alloc.host_ptr(), sizeof(int) * width, + sizeof(int) * width, 1, hipMemcpyHostToDevice)); + fill_value = 41; + std::fill_n(host_alloc.host_ptr(), width, fill_value); + HIP_CHECK(hipMemcpyHtoA(array_alloc.ptr(), 0, host_alloc.ptr(), 0)); - SECTION(" Source Array is nullptr") { - REQUIRE(hipMemcpyHtoA(nullptr, 0, B_h, copy_bytes*sizeof(float)) - != hipSuccess); - } + HIP_CHECK(hipMemcpy2DFromArray(host_alloc.host_ptr(), sizeof(int) * width, array_alloc.ptr(), 0, + 0, sizeof(int) * width, 1, hipMemcpyDeviceToHost)); - // DeAllocating the memory - HIP_CHECK(hipFreeArray(A_d)); - REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, - def_data, false) == true); + ArrayFindIfNot(host_alloc.host_ptr(), static_cast(42), width); } #endif + +TEST_CASE("Unit_hipMemcpyHtoA_Negative_Parameters") { + using namespace std::placeholders; + + const auto width = 1024; + const auto height = 0; + const auto allocation_size = width * sizeof(int); + + const unsigned int flag = hipArrayDefault; + + ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + + SECTION("dst == nullptr") { + HIP_CHECK_ERROR(hipMemcpyHtoA(nullptr, 0, host_alloc.ptr(), allocation_size), + hipErrorInvalidValue); + } + SECTION("src == nullptr") { + HIP_CHECK_ERROR(hipMemcpyHtoA(array_alloc.ptr(), 0, nullptr, allocation_size), + hipErrorInvalidValue); + } + SECTION("Offset is greater than allocated size") { + HIP_CHECK_ERROR( + hipMemcpyHtoA(array_alloc.ptr(), allocation_size + 10, host_alloc.ptr(), allocation_size), + hipErrorInvalidValue); + } + SECTION("Count is greater than allocated size") { + HIP_CHECK_ERROR(hipMemcpyHtoA(array_alloc.ptr(), 0, host_alloc.ptr(), allocation_size + 10), + hipErrorInvalidValue); + } + + SECTION("2D array is allocated") { + const auto width_2d = 32; + const auto height_2d = width_2d; + const auto allocation_size_2d = width_2d * height_2d * sizeof(int); + + ArrayAllocGuard array_alloc_2d(make_hipExtent(width_2d, height_2d, 0), flag); + LinearAllocGuard host_alloc_2d(LinearAllocs::hipHostMalloc, allocation_size_2d); + HIP_CHECK_ERROR(hipMemcpyHtoA(array_alloc_2d.ptr(), 0, host_alloc_2d.ptr(), allocation_size_2d), + hipErrorInvalidValue); + } +} diff --git a/catch/unit/memory/hipMemcpyHtoA_old.cc b/catch/unit/memory/hipMemcpyHtoA_old.cc new file mode 100644 index 0000000000..8712704400 --- /dev/null +++ b/catch/unit/memory/hipMemcpyHtoA_old.cc @@ -0,0 +1,229 @@ +/* +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. +*/ + +/* + * Test Scenarios: + * 1. Perform simple and pinned host memory of hipMemcpyHtoA API + * 2. Allocate Memory from one GPU device and call hipMemcpyHtoA from Peer + * GPU device + * 3. Perform hipMemcpyHtoA Negative Scenarios + * 4. Perform bytecount 0 validation for hipMemcpyHtoA API +*/ + +#include +#include + + +static constexpr auto NUM_W{10}; +static constexpr auto NUM_H{1}; +static constexpr auto copy_bytes{2}; + +/* +This testcase performs the basic and pinned host memory scenarios +of hipMemcpyHtoA API +Input: "B_h" which is initialized with 1.6 +Output: "A_d" output of hipMemcpyHtoA is copied to "hData" host variable + validated the result with "B_h" + +The same scenario is then verified with pinned host memory +*/ +TEMPLATE_TEST_CASE("Unit_hipMemcpyHtoA_Basic", "[hipMemcpyHtoA]", + char, int, float) { + HIP_CHECK(hipSetDevice(0)); + auto memtype_check = GENERATE(0, 1); + hipArray *A_d; + TestType *hData{nullptr}, *B_h{nullptr}; + size_t width{NUM_W * sizeof(TestType)}; + + // Initialization of data + if (memtype_check) { + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W, true); + } else { + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + } + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + // Performing API call + HIP_CHECK(hipMemcpyHtoA(A_d, 0, B_h, copy_bytes*sizeof(TestType))); + HIP_CHECK(hipMemcpy2DFromArray(hData, sizeof(TestType)*NUM_W, A_d, + 0, 0, sizeof(TestType)*NUM_W, 1, hipMemcpyDeviceToHost)); + + + // Validating the result + REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + if (memtype_check) { + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + nullptr, true) == true); + } else { + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + nullptr, false) == true); + } +} + + +/* +This testcase performs the peer device context scenario +of hipMemcpyHtoA API +Memory is allocated in GPU-0 and the API is triggered from GPU-1 +Input: "B_h" which is initialized with 1.6 +Output: "A_d" output of hipMemcpyHtoA is copied to "hData" host variable + validated the result with "B_h" +*/ +#if HT_AMD +TEMPLATE_TEST_CASE("Unit_hipMemcpyHtoA_multiDevice-PeerDeviceContext", + "[hipMemcpyHtoA]", + char, int, float) { + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + int peerAccess = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&peerAccess, 1, 0)); + if (!peerAccess) { + SUCCEED("Skipped the test as there is no peer access"); + } else { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d; + TestType *hData{nullptr}, *B_h{nullptr}; + size_t width{NUM_W * sizeof(TestType)}; + + // Initialization of data + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + // Changing the device context + HIP_CHECK(hipSetDevice(1)); + + // Performing API call + HIP_CHECK(hipMemcpyHtoA(A_d, 0, B_h, copy_bytes*sizeof(TestType))); + HIP_CHECK(hipMemcpy2DFromArray(hData, sizeof(TestType)*NUM_W, A_d, + 0, 0, sizeof(TestType)*NUM_W, 1, + hipMemcpyDeviceToHost)); + + // Validating the result + REQUIRE(HipTest::checkArray(B_h, hData, copy_bytes, NUM_H) == true); + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, + hData, B_h, + nullptr, false) == true); + } + } else { + SUCCEED("skipping the testcases as numDevices < 2"); + } +} +#endif + + +/* +This testcase verifies the negative scenarios of hipMemcpyHtoA API +*/ +TEST_CASE("Unit_hipMemcpyHtoA_Negative") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d; + float *hData{nullptr}, *B_h{nullptr}; + size_t width{NUM_W * sizeof(float)}; + + // Initialization of data + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + SECTION("Source pointer is nullptr") { + REQUIRE(hipMemcpyHtoA(A_d, 0, nullptr, copy_bytes*sizeof(float)) + != hipSuccess); + } + + SECTION("Source offset is more than allocated size") { + REQUIRE(hipMemcpyHtoA(A_d, 100, B_h, copy_bytes*sizeof(float)) + != hipSuccess); + } + + SECTION("ByteCount is greater than allocated size") { + REQUIRE(hipMemcpyHtoA(A_d, 0, B_h, 12*sizeof(float)) != hipSuccess); + } + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + nullptr, false) == true); +} + +/* +This testcase verifies the size 0 check of hipMemcpyHtoA API +This is excluded for AMD as we have a bug already raised +SWDEV-274683 +*/ +#if HT_NVIDIA +TEST_CASE("Unit_hipMemcpyHtoA_SizeCheck") { + HIP_CHECK(hipSetDevice(0)); + hipArray *A_d; + float *hData{nullptr}, *B_h{nullptr}, *def_data{nullptr}; + size_t width{NUM_W * sizeof(float)}; + + // Initialization of data + HipTest::initArrays(nullptr, nullptr, nullptr, + nullptr, &def_data, nullptr, NUM_W); + HipTest::initArrays(nullptr, nullptr, nullptr, + &hData, &B_h, nullptr, NUM_W); + HipTest::setDefaultData(NUM_W, hData, B_h, nullptr); + HipTest::setDefaultData(NUM_W, nullptr, def_data, nullptr); + hipChannelFormatDesc desc = hipCreateChannelDesc(); + HIP_CHECK(hipMallocArray(&A_d, &desc, NUM_W, NUM_H, hipArrayDefault)); + HIP_CHECK(hipMemcpy2DToArray(A_d, 0, 0, hData, width, + width, NUM_H, hipMemcpyHostToDevice)); + + SECTION("Passing 0 to copy bytes") { + REQUIRE(hipMemcpyHtoA(A_d, 0, B_h, 0) == hipSuccess); + HIP_CHECK(hipMemcpy2DFromArray(def_data, sizeof(float)*NUM_W, A_d, + 0, 0, sizeof(float)*NUM_W, 1, + hipMemcpyDeviceToHost)); + + REQUIRE(HipTest::checkArray(hData, def_data, NUM_W, NUM_H) == true); + } + + SECTION(" Source Array is nullptr") { + REQUIRE(hipMemcpyHtoA(nullptr, 0, B_h, copy_bytes*sizeof(float)) + != hipSuccess); + } + + // DeAllocating the memory + HIP_CHECK(hipFreeArray(A_d)); + REQUIRE(HipTest::freeArrays(nullptr, nullptr, nullptr, hData, B_h, + def_data, false) == true); +} +#endif From 647908ccb7ff425e04c8028b21993b65e311996d Mon Sep 17 00:00:00 2001 From: nives-vukovic <110852104+nives-vukovic@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:56:45 +0100 Subject: [PATCH 07/14] EXSWHTEC-72 - Implement hipMemcpyPeer/hipMemcpyPeerAsync and hipMemGetAddressRange tests (#34) - Reimplement and expand hipMemcpyPeer/hipMemcpyPeerAsync tests using resource guards - Implement positive and negative test for hipMemGetAddressRange --- .../config/config_amd_linux_MI2xx.json | 3 +- .../config/config_amd_linux_common.json | 3 +- .../config/config_amd_windows_MI2xx.json | 3 +- .../config/config_amd_windows_common.json | 3 +- catch/unit/memory/CMakeLists.txt | 6 + catch/unit/memory/hipMemGetAddressRange.cc | 88 ++++ catch/unit/memory/hipMemcpyPeer.cc | 300 +++++++----- catch/unit/memory/hipMemcpyPeerAsync.cc | 429 +++++++++--------- catch/unit/memory/hipMemcpyPeerAsync_old.cc | 262 +++++++++++ catch/unit/memory/hipMemcpyPeer_old.cc | 158 +++++++ 10 files changed, 910 insertions(+), 345 deletions(-) create mode 100644 catch/unit/memory/hipMemGetAddressRange.cc create mode 100644 catch/unit/memory/hipMemcpyPeerAsync_old.cc create mode 100644 catch/unit/memory/hipMemcpyPeer_old.cc diff --git a/catch/hipTestMain/config/config_amd_linux_MI2xx.json b/catch/hipTestMain/config/config_amd_linux_MI2xx.json index bf00e2e8e1..5e27deaa80 100644 --- a/catch/hipTestMain/config/config_amd_linux_MI2xx.json +++ b/catch/hipTestMain/config/config_amd_linux_MI2xx.json @@ -7,7 +7,8 @@ "Unit_hipInit_Negative", "Unit_BuiltinAtomicsRTC_fmaxCoherentGlobalMem", "Unit_BuiltinAtomicsRTC__fminCoherentGlobalMem", - "Unit_BuiltInAtomicAdd_CoherentGlobalMemWithRtc" + "Unit_BuiltInAtomicAdd_CoherentGlobalMemWithRtc", + "Unit_hipMemGetAddressRange_Negative" ] } diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index 224e85d155..864c6aa5c4 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -17,6 +17,7 @@ "Unit_hipDeviceReset_Positive_Threaded", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ClonedGrph", - "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ChldNode" + "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ChldNode", + "Unit_hipMemGetAddressRange_Negative" ] } diff --git a/catch/hipTestMain/config/config_amd_windows_MI2xx.json b/catch/hipTestMain/config/config_amd_windows_MI2xx.json index 89c484b79b..7898310ea5 100644 --- a/catch/hipTestMain/config/config_amd_windows_MI2xx.json +++ b/catch/hipTestMain/config/config_amd_windows_MI2xx.json @@ -92,6 +92,7 @@ "Unit_hipDeviceGetPCIBusId_Negative_PartialFill", "Unit_hipInit_Negative", "Unit_hipGraphAddEventRecordNode_Functional_ElapsedTime", - "Unit_hipStreamBeginCapture_captureComplexGraph" + "Unit_hipStreamBeginCapture_captureComplexGraph", + "Unit_hipMemGetAddressRange_Negative" ] } diff --git a/catch/hipTestMain/config/config_amd_windows_common.json b/catch/hipTestMain/config/config_amd_windows_common.json index 417985d83c..5586728b6f 100644 --- a/catch/hipTestMain/config/config_amd_windows_common.json +++ b/catch/hipTestMain/config/config_amd_windows_common.json @@ -96,6 +96,7 @@ "Unit_hipGraphAddEventRecordNode_Functional_WithoutFlags", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ClonedGrph", - "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ChldNode" + "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ChldNode", + "Unit_hipMemGetAddressRange_Negative" ] } diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index c89fae9bfc..962f493a60 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -70,7 +70,9 @@ set(TEST_SRC hipPtrGetAttribute.cc hipMemPoolApi.cc hipMemcpyPeer.cc + hipMemcpyPeer_old.cc hipMemcpyPeerAsync.cc + hipMemcpyPeerAsync_old.cc hipMemcpyWithStream_old.cc hipMemcpyWithStream.cc hipMemcpyWithStreamMultiThread.cc @@ -109,6 +111,7 @@ set(TEST_SRC hipMemsetAsync.cc hipMemAdvise.cc hipMemRangeGetAttributes.cc + hipMemGetAddressRange.cc ) else() set(TEST_SRC @@ -158,7 +161,9 @@ set(TEST_SRC hipPtrGetAttribute.cc hipMemPoolApi.cc hipMemcpyPeer.cc + hipMemcpyPeer_old.cc hipMemcpyPeerAsync.cc + hipMemcpyPeerAsync_old.cc hipMemcpyWithStream_old.cc hipMemcpyWithStream.cc hipMemcpyWithStreamMultiThread.cc @@ -194,6 +199,7 @@ set(TEST_SRC hipMemAdvise.cc hipMemRangeGetAttributes.cc hipGetSymbolSizeAddress.cc + hipMemGetAddressRange.cc ) endif() diff --git a/catch/unit/memory/hipMemGetAddressRange.cc b/catch/unit/memory/hipMemGetAddressRange.cc new file mode 100644 index 0000000000..7491163bda --- /dev/null +++ b/catch/unit/memory/hipMemGetAddressRange.cc @@ -0,0 +1,88 @@ +/* +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. +*/ +/* +Testcase Scenarios : +Unit_hipMemGetAddressRange_Positive - Test hipMemGetAddressRange api for various memory allocation +types and offsets Unit_hipMemGetAddressRange_Negative - Test unsuccessful execution of +hipMemGetAddressRange api when parameters are invalid +*/ +#include +#include +#include +#include + +TEST_CASE("Unit_hipMemGetAddressRange_Positive") { + hipDeviceptr_t base_ptr; + size_t mem_size = 0; + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); + const int offset = GENERATE(0, 20, 40, 60, 80); + + SECTION("Host address range") { + using LA = LinearAllocs; + LinearAllocGuard host_alloc(LA::hipHostMalloc, allocation_size); + + HIP_CHECK(hipMemGetAddressRange(&base_ptr, &mem_size, + reinterpret_cast(host_alloc.ptr() + offset))); + + REQUIRE(reinterpret_cast(host_alloc.ptr()) == base_ptr); + REQUIRE(mem_size == allocation_size); + } + SECTION("Device address range") { + using LA = LinearAllocs; + const auto device_allocation_type = GENERATE(LA::hipMalloc, LA::hipMallocManaged); + LinearAllocGuard device_alloc(device_allocation_type, allocation_size); + + HIP_CHECK(hipMemGetAddressRange(&base_ptr, &mem_size, + reinterpret_cast(device_alloc.ptr() + offset))); + + REQUIRE(reinterpret_cast(device_alloc.ptr()) == base_ptr); + REQUIRE(mem_size == allocation_size); + } + SECTION("Pitch address range") { + size_t width = 32; + size_t height = 32; + LinearAllocGuard2D device_alloc(width, height); + + HIP_CHECK(hipMemGetAddressRange(&base_ptr, &mem_size, + reinterpret_cast(device_alloc.ptr() + offset))); + + REQUIRE(reinterpret_cast(device_alloc.ptr()) == base_ptr); + REQUIRE(mem_size == (device_alloc.pitch() * height)); + } +} + +TEST_CASE("Unit_hipMemGetAddressRange_Negative") { + hipDeviceptr_t base_ptr; + size_t mem_size = 0; + const auto allocation_size = kPageSize / 2; + const int offset = kPageSize; + LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); + + hipDeviceptr_t dummy_ptr; + + SECTION("Device pointer is invalid") { + HIP_CHECK_ERROR(hipMemGetAddressRange(&base_ptr, &mem_size, dummy_ptr), hipErrorNotFound); + } + SECTION("Offset is greater than allocated size") { + HIP_CHECK_ERROR( + hipMemGetAddressRange(&base_ptr, &mem_size, + reinterpret_cast(host_alloc.ptr() + offset)), + hipErrorNotFound); + } +} diff --git a/catch/unit/memory/hipMemcpyPeer.cc b/catch/unit/memory/hipMemcpyPeer.cc index 33ee2e9f70..245b3e0b2c 100644 --- a/catch/unit/memory/hipMemcpyPeer.cc +++ b/catch/unit/memory/hipMemcpyPeer.cc @@ -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,143 +16,201 @@ 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 : +Unit_hipMemcpyPeer_Positive_Default - Test basic P2P memcpy between two devices +with hipMemcpyPeer api Unit_hipMemcpyPeer_Positive_Synchronization_Behavior - +Test synchronization behavior for hipMemcpyPeer api +Unit_hipMemcpyPeer_Positive_ZeroSize - Test that no data is copied when +sizeBytes is set to 0 Unit_hipMemcpyPeer_Negative_Parameters - Test unsuccessful +execution of hipMemcpyPeer api when parameters are invalid +*/ +#include #include -#include -#include -/* -This testfile verifies the following scenarios of hipMemcpyPeer API -1. Negative Scenarios -2. Basic scenario of hipMemcpyPeer API -*/ +#include +#include -/*This testcase verifies the negative scenarios of hipmemcpypeer -*/ -TEST_CASE("Unit_hipMemcpyPeer_Negative") { - constexpr auto numElements{10}; - constexpr auto copy_bytes{numElements*sizeof(int)}; - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - int canAccessPeer = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - // Initialization of variables - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - HIP_CHECK(hipSetDevice(0)); - HipTest::initArrays(&A_d, nullptr, nullptr, - &A_h, &B_h, nullptr, numElements*sizeof(int)); - HipTest::setDefaultData(numElements, A_h, B_h, nullptr); - HIP_CHECK(hipSetDevice(1)); - HipTest::initArrays(nullptr, &B_d, nullptr, - nullptr, nullptr, nullptr, numElements*sizeof(int)); - HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - SECTION("Nullptr to Destination Pointer") { - REQUIRE(hipMemcpyPeer(nullptr, 1, A_d, 0, copy_bytes) != hipSuccess); - } +TEST_CASE("Unit_hipMemcpyPeer_Positive_Default") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } - SECTION("Nullptr to Source Pointer") { - REQUIRE(hipMemcpyPeer(B_d, 1, nullptr, 0, copy_bytes) != hipSuccess); - } + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); - SECTION("Pass NumElements as 0") { - HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpyPeer(B_d, 1, A_d, 0, 0)); - HIP_CHECK(hipMemcpy(A_h, B_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); - HipTest::checkTest(A_h, B_h, numElements); - } + int can_access_peer = 0; + const auto src_device = GENERATE(range(0, HipTest::getDeviceCount())); + const auto dst_device = GENERATE(range(0, HipTest::getDeviceCount())); + INFO("Src device: " << src_device << ", Dst device: " << dst_device); - SECTION("Passing more than allocated size") { - REQUIRE(hipMemcpyPeer(B_d, 1, A_d, 0, - ((numElements+40)*sizeof(int))) != hipSuccess); - } + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); - SECTION("Passing invalid Destination device ID") { - REQUIRE(hipMemcpyPeer(B_d, numDevices, A_d, 0, copy_bytes) != - hipSuccess); - } + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, allocation_size); - SECTION("Passing invalid Source device ID") { - REQUIRE(hipMemcpyPeer(B_d, 1, A_d, numDevices, copy_bytes) != - hipSuccess); - } - HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); - } else { - SUCCEED("Machine Does not have P2P capability"); - } + const auto element_count = allocation_size / sizeof(*src_alloc.ptr()); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int expected_value = 22; + HIP_CHECK(hipSetDevice(src_device)); + VectorSet<<>>(src_alloc.ptr(), expected_value, element_count); + HIP_CHECK(hipGetLastError()); + + HIP_CHECK( + hipMemcpyPeer(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, allocation_size)); + + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_alloc.ptr(), allocation_size, hipMemcpyDeviceToHost)); + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); + + ArrayFindIfNot(result.host_ptr(), expected_value, element_count); } else { - SUCCEED("Number of devices are < 2"); + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); } } -/* - * This test case verifies the basic scenario of hipMemcpyPeer API - * Initializes data in GPU-0 - * Launches the kernel and performs addition in GPU-0 - * Copies the data from GPU-0 to GPU-1 using hipMemcpyPeer API - * Then performs the addition and validates the sum - */ -TEST_CASE("Unit_hipMemcpyPeer_Basic") { - constexpr auto numElements{10}; - constexpr auto copy_bytes{numElements*sizeof(int)}; - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - int canAccessPeer = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; +TEST_CASE("Unit_hipMemcpyPeer_Positive_Synchronization_Behavior") { + HIP_CHECK(hipDeviceSynchronize()); - // Initialization of Variables on GPU-0 - HIP_CHECK(hipSetDevice(0)); - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, &C_h, numElements*sizeof(int)); - HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } - // Initialization of Variables on GPU-1 - HIP_CHECK(hipSetDevice(1)); - HipTest::initArrays(&X_d, &Y_d, &Z_d, nullptr, - nullptr, nullptr, numElements*sizeof(int)); + int can_access_peer = 0; + const auto src_device = 0; + const auto dst_device = 1; - // Launching kernel and performing vector addition on GPU-0 - HIP_CHECK(hipSetDevice(0)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), - 0, 0, static_cast(A_d), - static_cast(B_d), C_d, numElements*sizeof(int)); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, C_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); - HIP_CHECK(hipSetDevice(1)); - // Copying data from GPU-0 to GPU-1 and performing vector addition - HIP_CHECK(hipMemcpyPeer(X_d, 1, A_d, 0, copy_bytes)); - HIP_CHECK(hipMemcpyPeer(Y_d, 1, B_d, 0, copy_bytes)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), - 0, 0, static_cast(X_d), - static_cast(Y_d), Z_d, numElements*sizeof(int)); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, Z_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); - // Cleaning the memory - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, nullptr, nullptr, false); - } else { - SUCCEED("Machine Does not have P2P capability"); - } + HIP_CHECK(hipSetDevice(src_device)); + LaunchDelayKernel(std::chrono::milliseconds{100}, nullptr); + + HIP_CHECK(hipMemcpyPeer(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, kPageSize)); + HIP_CHECK_ERROR(hipStreamQuery(nullptr), hipErrorNotReady); + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); } else { - SUCCEED("Number of devices are < 2"); + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); + } +} + +TEST_CASE("Unit_hipMemcpyPeer_Positive_ZeroSize") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } + + const auto allocation_size = kPageSize; + + int can_access_peer = 0; + const auto src_device = 0; + const auto dst_device = 1; + + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); + + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size, + hipHostMallocPortable); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, allocation_size); + + const auto element_count = allocation_size / sizeof(*src_alloc.ptr()); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int set_value = 22; + HIP_CHECK(hipSetDevice(src_device)); + VectorSet<<>>(src_alloc.ptr(), set_value, element_count); + HIP_CHECK(hipGetLastError()); + + constexpr int expected_value = 21; + std::fill_n(src_alloc.host_ptr(), element_count, expected_value); + + HIP_CHECK(hipMemcpyPeer(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, 0)); + + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_alloc.ptr(), allocation_size, hipMemcpyDeviceToHost)); + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); + + ArrayFindIfNot(result.host_ptr(), expected_value, element_count); + } else { + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); + } +} + +TEST_CASE("Unit_hipMemcpyPeer_Negative_Parameters") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } + + int can_access_peer = 0; + const auto src_device = 0; + const auto dst_device = 1; + + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); + + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); + + HIP_CHECK(hipSetDevice(src_device)); + + SECTION("Nullptr to Destination Pointer") { + HIP_CHECK_ERROR(hipMemcpyPeer(nullptr, dst_device, src_alloc.ptr(), src_device, kPageSize), + hipErrorInvalidValue); + } + + SECTION("Nullptr to Source Pointer") { + HIP_CHECK_ERROR(hipMemcpyPeer(dst_alloc.ptr(), dst_device, nullptr, src_device, kPageSize), + hipErrorInvalidValue); + } + + SECTION("Passing more than allocated size") { + HIP_CHECK_ERROR( + hipMemcpyPeer(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, kPageSize + 1), + hipErrorInvalidValue); + } + + SECTION("Passing invalid Destination device ID") { + HIP_CHECK_ERROR( + hipMemcpyPeer(dst_alloc.ptr(), device_count, src_alloc.ptr(), src_device, kPageSize), + hipErrorInvalidDevice); + } + + SECTION("Passing invalid Source device ID") { + HIP_CHECK_ERROR( + hipMemcpyPeer(dst_alloc.ptr(), dst_device, src_alloc.ptr(), device_count, kPageSize), + hipErrorInvalidDevice); + } + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); + } else { + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); } } diff --git a/catch/unit/memory/hipMemcpyPeerAsync.cc b/catch/unit/memory/hipMemcpyPeerAsync.cc index 159c8309e3..90f9fdb8f4 100644 --- a/catch/unit/memory/hipMemcpyPeerAsync.cc +++ b/catch/unit/memory/hipMemcpyPeerAsync.cc @@ -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,247 +16,236 @@ 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 testfile verifies the following scenarios of hipMemcpyPeerAsync API -1. Negative Scenarios -2. Memory on one GPU and stream created on another GPU -3. Basic scenario of hipMemcpyPeerAsync API +Testcase Scenarios : +Unit_hipMemcpyPeerAsync_Positive_Default - Test basic P2P async memcpy between +two devices with hipMemcpyPeerAsync api +Unit_hipMemcpyPeerAsync_Positive_Synchronization_Behavior - Test synchronization +behavior for hipMemcpyPeerAsync api Unit_hipMemcpyPeerAsync_Positive_ZeroSize - +Test that no data is copied when sizeBytes is set to 0 +Unit_hipMemcpyPeerAsync_Negative_Parameters - Test unsuccessful execution of +hipMemcpyPeerAsync api when parameters are invalid */ - - +#include #include -#include -#include -#include +#include +#include -/*This testcase verifies the negative scenarios of hipmemcpypeerAsync -*/ -TEST_CASE("Unit_hipMemcpyPeerAsync_Negative") { - constexpr auto numElements{10}; - constexpr auto copy_bytes{numElements*sizeof(int)}; - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - int canAccessPeer = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - // Initialization of variables - int *A_d{nullptr}, *B_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}; - hipStream_t stream; - HIP_CHECK(hipSetDevice(0)); - HIP_CHECK(hipStreamCreate(&stream)); - HipTest::initArrays(&A_d, nullptr, nullptr, - &A_h, &B_h, nullptr, numElements*sizeof(int)); - HipTest::setDefaultData(numElements, A_h, B_h, nullptr); - HIP_CHECK(hipSetDevice(1)); - HipTest::initArrays(nullptr, &B_d, nullptr, - nullptr, nullptr, nullptr, numElements*sizeof(int)); - HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - SECTION("Nullptr to Destination Pointer") { - REQUIRE(hipMemcpyPeerAsync(nullptr, 1, A_d, 0, copy_bytes, - stream) != hipSuccess); - } - - SECTION("Nullptr to Source Pointer") { - REQUIRE(hipMemcpyPeerAsync(B_d, 1, nullptr, 0, copy_bytes, - stream) != hipSuccess); - } - - SECTION("Pass NumElements as 0") { - HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpyPeerAsync(B_d, 1, A_d, 0, 0, - stream)); - HIP_CHECK(hipMemcpy(A_h, B_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); - HipTest::checkTest(A_h, B_h, numElements); - } - - SECTION("Passing more than allocated size") { - REQUIRE(hipMemcpyPeerAsync(B_d, 1, A_d, 0, - 100*sizeof(int), stream) != hipSuccess); - } - - SECTION("Passing invalid Destination device ID") { - REQUIRE(hipMemcpyPeerAsync(B_d, numDevices, A_d, 0, copy_bytes, - stream) != hipSuccess); - } - - SECTION("Passing invalid Source device ID") { - REQUIRE(hipMemcpyPeerAsync(B_d, 0, A_d, numDevices, copy_bytes, - stream) != hipSuccess); - } - - HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); - HIP_CHECK(hipStreamDestroy(stream)); - } else { - SUCCEED("Machine Does not have P2P capability"); - } - } else { - SUCCEED("Number of devices are < 2"); +TEST_CASE("Unit_hipMemcpyPeerAsync_Positive_Default") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; } -} -/* - * This test case verifies the basic scenario of hipMemcpyPeer API - * Initializes data in GPU-0 - * Launches the kernel and performs addition in GPU-0 - * Copies the data from GPU-0 to GPU-1 using hipMemcpyPeerAsync API - * Then performs the addition and validates the sum - */ + const auto stream_type = GENERATE(Streams::nullstream, Streams::perThread, Streams::created); + const StreamGuard stream_guard(stream_type); + const hipStream_t stream = stream_guard.stream(); -TEST_CASE("Unit_hipMemcpyPeerAsync_Basic") { - constexpr auto numElements{10}; - constexpr auto copy_bytes{numElements*sizeof(int)}; + const auto allocation_size = GENERATE(kPageSize / 2, kPageSize, kPageSize * 2); - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - int canAccessPeer = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - // Initialization of Variables on GPU-0 - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; - hipStream_t stream; - HIP_CHECK(hipSetDevice(0)); - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, &C_h, numElements*sizeof(int)); - HipTest::setDefaultData(numElements, A_h, B_h, nullptr); - HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - HIP_CHECK(hipStreamCreate(&stream)); + int can_access_peer = 0; - // Initialization of Variables in GPU-1 - HIP_CHECK(hipSetDevice(1)); - HipTest::initArrays(&X_d, &Y_d, &Z_d, nullptr, - nullptr, nullptr, numElements*sizeof(int)); + const auto src_device = GENERATE(range(0, HipTest::getDeviceCount())); + const auto dst_device = GENERATE(range(0, HipTest::getDeviceCount())); + INFO("Src device: " << src_device << ", Dst device: " << dst_device); - // Launching kernel and performing vector addition in GPU-0 - HIP_CHECK(hipSetDevice(0)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), - 0, 0, static_cast(A_d), - static_cast(B_d), C_d, numElements*sizeof(int)); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, C_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); - // Copying data from GPU-0 to GPU-1 and performing vector addition - HIP_CHECK(hipSetDevice(1)); - SECTION("Calling hipMemcpyPerAsync() using user defined stream obj") { - HIP_CHECK(hipMemcpyPeerAsync(X_d, 1, A_d, 0, copy_bytes, - stream)); - HIP_CHECK(hipMemcpyPeerAsync(Y_d, 1, B_d, 0, copy_bytes, - stream)); - HIP_CHECK(hipStreamSynchronize(stream)); - } - SECTION("Calling hipMemcpyPerAsync() using hipStreamPerThread") { - HIP_CHECK(hipMemcpyPeerAsync(X_d, 1, A_d, 0, copy_bytes, - hipStreamPerThread)); - HIP_CHECK(hipMemcpyPeerAsync(Y_d, 1, B_d, 0, copy_bytes, - hipStreamPerThread)); - HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); - } - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), - 0, 0, static_cast(X_d), - static_cast(Y_d), Z_d, numElements*sizeof(int)); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, Z_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size, + hipHostMallocPortable); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, allocation_size); - // Cleaning the Memory - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, nullptr, nullptr, false); - HIP_CHECK(hipStreamDestroy(stream)); - } else { - SUCCEED("Machine Does not have P2P capability"); - } + const auto element_count = allocation_size / sizeof(*src_alloc.ptr()); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int expected_value = 22; + HIP_CHECK(hipSetDevice(src_device)); + VectorSet<<>>(src_alloc.ptr(), expected_value, + element_count); + HIP_CHECK(hipGetLastError()); + + HIP_CHECK(hipMemcpyPeerAsync(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, + allocation_size, stream)); + + HIP_CHECK(hipStreamSynchronize(stream)); + + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_alloc.ptr(), allocation_size, hipMemcpyDeviceToHost)); + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); + + ArrayFindIfNot(result.host_ptr(), expected_value, element_count); } else { - SUCCEED("Number of devices are < 2"); + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); } } -/* - * This test case verifies the following functionality where - Memory is allocated in One GPU and - stream created on another GPU - * Initializes all the data in GPU-0 - * Creating stream in GPU-1 - * Launches the kernel and performs addition in GPU-0 - * Copies the data from GPU-0 to GPU-1 using hipMemcpyPeerAsync API - * where stream is created in GPU-1 - * Then performs the addition and validates the sum - */ -TEST_CASE("Unit_hipMemcpyPeerAsync_StreamOnDiffDevice") { - constexpr auto numElements{10}; - constexpr auto copy_bytes{numElements*sizeof(int)}; - int numDevices = 0; - HIP_CHECK(hipGetDeviceCount(&numDevices)); - if (numDevices > 1) { - int canAccessPeer = 0; - HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); - if (canAccessPeer) { - int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; - int *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; - int *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; - hipStream_t stream; - HIP_CHECK(hipSetDevice(0)); +TEST_CASE("Unit_hipMemcpyPeerAsync_Positive_Synchronization_Behavior") { + HIP_CHECK(hipDeviceSynchronize()); - // Initialization of all variables in GPU-0 - HipTest::initArrays(&A_d, &B_d, &C_d, - &A_h, &B_h, &C_h, numElements*sizeof(int)); - HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), - hipMemcpyHostToDevice)); - HipTest::initArrays(&X_d, &Y_d, &Z_d, nullptr, - nullptr, nullptr, numElements*sizeof(int)); + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } - // Stream created in GPU-1 - HIP_CHECK(hipSetDevice(1)); - HIP_CHECK(hipStreamCreate(&stream)); + const StreamGuard stream_guard(Streams::created); + const hipStream_t stream = stream_guard.stream(); - // Performing vector addition and validate the data - HIP_CHECK(hipSetDevice(0)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), - 0, 0, static_cast(A_d), - static_cast(B_d), C_d, numElements*sizeof(int)); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, C_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); - HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + int can_access_peer = 0; + const auto src_device = 0; + const auto dst_device = 1; - // Copying the data from GPU-0 to GPU-1 where stream is from diff device - HIP_CHECK(hipMemcpyPeerAsync(X_d, 1, A_d, 0, copy_bytes, - stream)); - HIP_CHECK(hipMemcpyPeerAsync(Y_d, 1, B_d, 0, copy_bytes, - stream)); - HIP_CHECK(hipStreamSynchronize(stream)); - hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), - 0, 0, static_cast(X_d), - static_cast(Y_d), Z_d, numElements*sizeof(int)); - HIP_CHECK(hipGetLastError()); - HIP_CHECK(hipMemcpy(C_h, Z_d, numElements*sizeof(int), - hipMemcpyDeviceToHost)); + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); - // Cleaning the data - HipTest::checkVectorADD(A_h, B_h, C_h, numElements); - HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); - HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, nullptr, nullptr, false); - HIP_CHECK(hipStreamDestroy(stream)); - } else { - SUCCEED("Machine Does not have P2P capability"); - } + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); + + HIP_CHECK(hipSetDevice(src_device)); + LaunchDelayKernel(std::chrono::milliseconds{100}, nullptr); + + HIP_CHECK(hipMemcpyPeerAsync(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, + kPageSize, stream)); + HIP_CHECK_ERROR(hipStreamQuery(nullptr), hipErrorNotReady); + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); } else { - SUCCEED("Number of devices are < 2"); + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); } } +TEST_CASE("Unit_hipMemcpyPeerAsync_Positive_ZeroSize") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } + + const StreamGuard stream_guard(Streams::created); + const hipStream_t stream = stream_guard.stream(); + + const auto allocation_size = kPageSize; + + int can_access_peer = 0; + const auto src_device = 0; + const auto dst_device = 1; + + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); + + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, allocation_size); + LinearAllocGuard result(LinearAllocs::hipHostMalloc, allocation_size, + hipHostMallocPortable); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, allocation_size); + + const auto element_count = allocation_size / sizeof(*src_alloc.ptr()); + constexpr auto thread_count = 1024; + const auto block_count = element_count / thread_count + 1; + constexpr int set_value = 22; + HIP_CHECK(hipSetDevice(src_device)); + VectorSet<<>>(src_alloc.ptr(), set_value, element_count); + HIP_CHECK(hipGetLastError()); + + constexpr int expected_value = 21; + std::fill_n(src_alloc.host_ptr(), element_count, expected_value); + + HIP_CHECK( + hipMemcpyPeerAsync(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, 0, stream)); + + HIP_CHECK(hipStreamSynchronize(stream)); + + HIP_CHECK( + hipMemcpy(result.host_ptr(), dst_alloc.ptr(), allocation_size, hipMemcpyDeviceToHost)); + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); + + ArrayFindIfNot(result.host_ptr(), expected_value, element_count); + } else { + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); + } +} + +TEST_CASE("Unit_hipMemcpyPeerAsync_Negative_Parameters") { + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Skipping because devices < 2"); + return; + } + + const StreamGuard stream_guard(Streams::created); + const hipStream_t stream = stream_guard.stream(); + + constexpr auto InvalidStream = [] { + StreamGuard sg(Streams::created); + return sg.stream(); + }; + + int can_access_peer = 0; + const auto src_device = 0; + const auto dst_device = 1; + + HIP_CHECK(hipSetDevice(src_device)); + HIP_CHECK(hipDeviceCanAccessPeer(&can_access_peer, src_device, dst_device)); + if (can_access_peer) { + HIP_CHECK(hipDeviceEnablePeerAccess(dst_device, 0)); + + LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); + HIP_CHECK(hipSetDevice(dst_device)); + LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); + + HIP_CHECK(hipSetDevice(src_device)); + + SECTION("Nullptr to Destination Pointer") { + HIP_CHECK_ERROR( + hipMemcpyPeerAsync(nullptr, dst_device, src_alloc.ptr(), src_device, kPageSize, stream), + hipErrorInvalidValue); + } + + SECTION("Nullptr to Source Pointer") { + HIP_CHECK_ERROR( + hipMemcpyPeerAsync(dst_alloc.ptr(), dst_device, nullptr, src_device, kPageSize, stream), + hipErrorInvalidValue); + } + + SECTION("Passing more than allocated size") { + HIP_CHECK_ERROR(hipMemcpyPeerAsync(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, + kPageSize + 1, stream), + hipErrorInvalidValue); + } + + SECTION("Passing invalid Destination device ID") { + HIP_CHECK_ERROR(hipMemcpyPeerAsync(dst_alloc.ptr(), device_count, src_alloc.ptr(), src_device, + kPageSize, stream), + hipErrorInvalidDevice); + } + + SECTION("Passing invalid Source device ID") { + HIP_CHECK_ERROR(hipMemcpyPeerAsync(dst_alloc.ptr(), dst_device, src_alloc.ptr(), device_count, + kPageSize, stream), + hipErrorInvalidDevice); + } + + SECTION("Passing invalid Stream") { + HIP_CHECK_ERROR(hipMemcpyPeerAsync(dst_alloc.ptr(), dst_device, src_alloc.ptr(), src_device, + kPageSize, InvalidStream()), + hipErrorContextIsDestroyed); + } + + HIP_CHECK(hipDeviceDisablePeerAccess(dst_device)); + } else { + INFO("Peer access cannot be enabled between devices " << src_device << " " << dst_device); + } +} diff --git a/catch/unit/memory/hipMemcpyPeerAsync_old.cc b/catch/unit/memory/hipMemcpyPeerAsync_old.cc new file mode 100644 index 0000000000..159c8309e3 --- /dev/null +++ b/catch/unit/memory/hipMemcpyPeerAsync_old.cc @@ -0,0 +1,262 @@ +/* +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 testfile verifies the following scenarios of hipMemcpyPeerAsync API +1. Negative Scenarios +2. Memory on one GPU and stream created on another GPU +3. Basic scenario of hipMemcpyPeerAsync API +*/ + + +#include +#include +#include +#include + +/*This testcase verifies the negative scenarios of hipmemcpypeerAsync +*/ +TEST_CASE("Unit_hipMemcpyPeerAsync_Negative") { + constexpr auto numElements{10}; + constexpr auto copy_bytes{numElements*sizeof(int)}; + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + int canAccessPeer = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + // Initialization of variables + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + hipStream_t stream; + HIP_CHECK(hipSetDevice(0)); + HIP_CHECK(hipStreamCreate(&stream)); + HipTest::initArrays(&A_d, nullptr, nullptr, + &A_h, &B_h, nullptr, numElements*sizeof(int)); + HipTest::setDefaultData(numElements, A_h, B_h, nullptr); + HIP_CHECK(hipSetDevice(1)); + HipTest::initArrays(nullptr, &B_d, nullptr, + nullptr, nullptr, nullptr, numElements*sizeof(int)); + HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + + SECTION("Nullptr to Destination Pointer") { + REQUIRE(hipMemcpyPeerAsync(nullptr, 1, A_d, 0, copy_bytes, + stream) != hipSuccess); + } + + SECTION("Nullptr to Source Pointer") { + REQUIRE(hipMemcpyPeerAsync(B_d, 1, nullptr, 0, copy_bytes, + stream) != hipSuccess); + } + + SECTION("Pass NumElements as 0") { + HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpyPeerAsync(B_d, 1, A_d, 0, 0, + stream)); + HIP_CHECK(hipMemcpy(A_h, B_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + HipTest::checkTest(A_h, B_h, numElements); + } + + SECTION("Passing more than allocated size") { + REQUIRE(hipMemcpyPeerAsync(B_d, 1, A_d, 0, + 100*sizeof(int), stream) != hipSuccess); + } + + SECTION("Passing invalid Destination device ID") { + REQUIRE(hipMemcpyPeerAsync(B_d, numDevices, A_d, 0, copy_bytes, + stream) != hipSuccess); + } + + SECTION("Passing invalid Source device ID") { + REQUIRE(hipMemcpyPeerAsync(B_d, 0, A_d, numDevices, copy_bytes, + stream) != hipSuccess); + } + + HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); + HIP_CHECK(hipStreamDestroy(stream)); + } else { + SUCCEED("Machine Does not have P2P capability"); + } + } else { + SUCCEED("Number of devices are < 2"); + } +} +/* + * This test case verifies the basic scenario of hipMemcpyPeer API + * Initializes data in GPU-0 + * Launches the kernel and performs addition in GPU-0 + * Copies the data from GPU-0 to GPU-1 using hipMemcpyPeerAsync API + * Then performs the addition and validates the sum + */ + +TEST_CASE("Unit_hipMemcpyPeerAsync_Basic") { + constexpr auto numElements{10}; + constexpr auto copy_bytes{numElements*sizeof(int)}; + + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + int canAccessPeer = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + // Initialization of Variables on GPU-0 + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; + hipStream_t stream; + HIP_CHECK(hipSetDevice(0)); + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, &C_h, numElements*sizeof(int)); + HipTest::setDefaultData(numElements, A_h, B_h, nullptr); + HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + HIP_CHECK(hipStreamCreate(&stream)); + + // Initialization of Variables in GPU-1 + HIP_CHECK(hipSetDevice(1)); + HipTest::initArrays(&X_d, &Y_d, &Z_d, nullptr, + nullptr, nullptr, numElements*sizeof(int)); + + // Launching kernel and performing vector addition in GPU-0 + HIP_CHECK(hipSetDevice(0)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), + 0, 0, static_cast(A_d), + static_cast(B_d), C_d, numElements*sizeof(int)); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, C_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + + // Copying data from GPU-0 to GPU-1 and performing vector addition + HIP_CHECK(hipSetDevice(1)); + SECTION("Calling hipMemcpyPerAsync() using user defined stream obj") { + HIP_CHECK(hipMemcpyPeerAsync(X_d, 1, A_d, 0, copy_bytes, + stream)); + HIP_CHECK(hipMemcpyPeerAsync(Y_d, 1, B_d, 0, copy_bytes, + stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + } + SECTION("Calling hipMemcpyPerAsync() using hipStreamPerThread") { + HIP_CHECK(hipMemcpyPeerAsync(X_d, 1, A_d, 0, copy_bytes, + hipStreamPerThread)); + HIP_CHECK(hipMemcpyPeerAsync(Y_d, 1, B_d, 0, copy_bytes, + hipStreamPerThread)); + HIP_CHECK(hipStreamSynchronize(hipStreamPerThread)); + } + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), + 0, 0, static_cast(X_d), + static_cast(Y_d), Z_d, numElements*sizeof(int)); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, Z_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + + // Cleaning the Memory + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, nullptr, nullptr, false); + HIP_CHECK(hipStreamDestroy(stream)); + } else { + SUCCEED("Machine Does not have P2P capability"); + } + } else { + SUCCEED("Number of devices are < 2"); + } +} + +/* + * This test case verifies the following functionality where + Memory is allocated in One GPU and + stream created on another GPU + * Initializes all the data in GPU-0 + * Creating stream in GPU-1 + * Launches the kernel and performs addition in GPU-0 + * Copies the data from GPU-0 to GPU-1 using hipMemcpyPeerAsync API + * where stream is created in GPU-1 + * Then performs the addition and validates the sum + */ +TEST_CASE("Unit_hipMemcpyPeerAsync_StreamOnDiffDevice") { + constexpr auto numElements{10}; + constexpr auto copy_bytes{numElements*sizeof(int)}; + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + int canAccessPeer = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; + hipStream_t stream; + HIP_CHECK(hipSetDevice(0)); + + // Initialization of all variables in GPU-0 + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, &C_h, numElements*sizeof(int)); + HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + HipTest::initArrays(&X_d, &Y_d, &Z_d, nullptr, + nullptr, nullptr, numElements*sizeof(int)); + + // Stream created in GPU-1 + HIP_CHECK(hipSetDevice(1)); + HIP_CHECK(hipStreamCreate(&stream)); + + // Performing vector addition and validate the data + HIP_CHECK(hipSetDevice(0)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), + 0, 0, static_cast(A_d), + static_cast(B_d), C_d, numElements*sizeof(int)); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, C_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + + // Copying the data from GPU-0 to GPU-1 where stream is from diff device + HIP_CHECK(hipMemcpyPeerAsync(X_d, 1, A_d, 0, copy_bytes, + stream)); + HIP_CHECK(hipMemcpyPeerAsync(Y_d, 1, B_d, 0, copy_bytes, + stream)); + HIP_CHECK(hipStreamSynchronize(stream)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), + 0, 0, static_cast(X_d), + static_cast(Y_d), Z_d, numElements*sizeof(int)); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, Z_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + + // Cleaning the data + HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, nullptr, nullptr, false); + HIP_CHECK(hipStreamDestroy(stream)); + } else { + SUCCEED("Machine Does not have P2P capability"); + } + } else { + SUCCEED("Number of devices are < 2"); + } +} + diff --git a/catch/unit/memory/hipMemcpyPeer_old.cc b/catch/unit/memory/hipMemcpyPeer_old.cc new file mode 100644 index 0000000000..33ee2e9f70 --- /dev/null +++ b/catch/unit/memory/hipMemcpyPeer_old.cc @@ -0,0 +1,158 @@ +/* +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. +*/ + +#include +#include +#include +/* +This testfile verifies the following scenarios of hipMemcpyPeer API +1. Negative Scenarios +2. Basic scenario of hipMemcpyPeer API +*/ + +/*This testcase verifies the negative scenarios of hipmemcpypeer +*/ +TEST_CASE("Unit_hipMemcpyPeer_Negative") { + constexpr auto numElements{10}; + constexpr auto copy_bytes{numElements*sizeof(int)}; + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + int canAccessPeer = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + // Initialization of variables + int *A_d{nullptr}, *B_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}; + HIP_CHECK(hipSetDevice(0)); + HipTest::initArrays(&A_d, nullptr, nullptr, + &A_h, &B_h, nullptr, numElements*sizeof(int)); + HipTest::setDefaultData(numElements, A_h, B_h, nullptr); + HIP_CHECK(hipSetDevice(1)); + HipTest::initArrays(nullptr, &B_d, nullptr, + nullptr, nullptr, nullptr, numElements*sizeof(int)); + HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + + SECTION("Nullptr to Destination Pointer") { + REQUIRE(hipMemcpyPeer(nullptr, 1, A_d, 0, copy_bytes) != hipSuccess); + } + + SECTION("Nullptr to Source Pointer") { + REQUIRE(hipMemcpyPeer(B_d, 1, nullptr, 0, copy_bytes) != hipSuccess); + } + + SECTION("Pass NumElements as 0") { + HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpyPeer(B_d, 1, A_d, 0, 0)); + HIP_CHECK(hipMemcpy(A_h, B_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + HipTest::checkTest(A_h, B_h, numElements); + } + + SECTION("Passing more than allocated size") { + REQUIRE(hipMemcpyPeer(B_d, 1, A_d, 0, + ((numElements+40)*sizeof(int))) != hipSuccess); + } + + SECTION("Passing invalid Destination device ID") { + REQUIRE(hipMemcpyPeer(B_d, numDevices, A_d, 0, copy_bytes) != + hipSuccess); + } + + SECTION("Passing invalid Source device ID") { + REQUIRE(hipMemcpyPeer(B_d, 1, A_d, numDevices, copy_bytes) != + hipSuccess); + } + HipTest::freeArrays(A_d, B_d, nullptr, A_h, B_h, nullptr, false); + } else { + SUCCEED("Machine Does not have P2P capability"); + } + } else { + SUCCEED("Number of devices are < 2"); + } +} + +/* + * This test case verifies the basic scenario of hipMemcpyPeer API + * Initializes data in GPU-0 + * Launches the kernel and performs addition in GPU-0 + * Copies the data from GPU-0 to GPU-1 using hipMemcpyPeer API + * Then performs the addition and validates the sum + */ +TEST_CASE("Unit_hipMemcpyPeer_Basic") { + constexpr auto numElements{10}; + constexpr auto copy_bytes{numElements*sizeof(int)}; + int numDevices = 0; + HIP_CHECK(hipGetDeviceCount(&numDevices)); + if (numDevices > 1) { + int canAccessPeer = 0; + HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, 0, 1)); + if (canAccessPeer) { + int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr}; + int *X_d{nullptr}, *Y_d{nullptr}, *Z_d{nullptr}; + int *A_h{nullptr}, *B_h{nullptr}, *C_h{nullptr}; + + // Initialization of Variables on GPU-0 + HIP_CHECK(hipSetDevice(0)); + HipTest::initArrays(&A_d, &B_d, &C_d, + &A_h, &B_h, &C_h, numElements*sizeof(int)); + HIP_CHECK(hipMemcpy(A_d, A_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + HIP_CHECK(hipMemcpy(B_d, B_h, numElements*sizeof(int), + hipMemcpyHostToDevice)); + + // Initialization of Variables on GPU-1 + HIP_CHECK(hipSetDevice(1)); + HipTest::initArrays(&X_d, &Y_d, &Z_d, nullptr, + nullptr, nullptr, numElements*sizeof(int)); + + // Launching kernel and performing vector addition on GPU-0 + HIP_CHECK(hipSetDevice(0)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), + 0, 0, static_cast(A_d), + static_cast(B_d), C_d, numElements*sizeof(int)); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, C_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + + HIP_CHECK(hipSetDevice(1)); + // Copying data from GPU-0 to GPU-1 and performing vector addition + HIP_CHECK(hipMemcpyPeer(X_d, 1, A_d, 0, copy_bytes)); + HIP_CHECK(hipMemcpyPeer(Y_d, 1, B_d, 0, copy_bytes)); + hipLaunchKernelGGL(HipTest::vectorADD, dim3(1), dim3(1), + 0, 0, static_cast(X_d), + static_cast(Y_d), Z_d, numElements*sizeof(int)); + HIP_CHECK(hipGetLastError()); + HIP_CHECK(hipMemcpy(C_h, Z_d, numElements*sizeof(int), + hipMemcpyDeviceToHost)); + HipTest::checkVectorADD(A_h, B_h, C_h, numElements); + + // Cleaning the memory + HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false); + HipTest::freeArrays(X_d, Y_d, Z_d, nullptr, nullptr, nullptr, false); + } else { + SUCCEED("Machine Does not have P2P capability"); + } + } else { + SUCCEED("Number of devices are < 2"); + } +} From 203b99423062610df25bfb5b075a4379f2424267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirza=20Halil=C4=8Devi=C4=87?= <109971222+mirza-halilcevic@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:57:58 +0100 Subject: [PATCH 08/14] EXSWHTEC-69 - Implement tests for hipMemRangeGetAttributes (#51) - Negative parameter tests for hipMemRangeGetAttribute and hipMemRangeGetAttributes - Validate the behavior of hipMemRangeGetAttribute for hipMemRangeAttributeReadMostly - Validate the behavior of hipMemRangeGetAttribute for hipMemRangeAttributePreferredLocation - Validate the behavior of hipMemRangeGetAttribute for hipMemRangeAttributeLastPrefetchLocation - Validate the behavior of hipMemRangeGetAttribute for hipMemRangeAttributeAccessedBy - Validate the behavior of hipMemRangeGetAttributes --- catch/include/resource_guards.hh | 57 +- catch/unit/memory/CMakeLists.txt | 4 + catch/unit/memory/hipMemRangeGetAttribute.cc | 693 ++++++++---------- .../memory/hipMemRangeGetAttribute_old.cc | 408 +++++++++++ catch/unit/memory/hipMemRangeGetAttributes.cc | 424 ++++------- .../memory/hipMemRangeGetAttributes_old.cc | 325 ++++++++ 6 files changed, 1241 insertions(+), 670 deletions(-) create mode 100644 catch/unit/memory/hipMemRangeGetAttribute_old.cc create mode 100644 catch/unit/memory/hipMemRangeGetAttributes_old.cc diff --git a/catch/include/resource_guards.hh b/catch/include/resource_guards.hh index d50ae05baa..af4c285e2a 100644 --- a/catch/include/resource_guards.hh +++ b/catch/include/resource_guards.hh @@ -196,11 +196,11 @@ template class DrvArrayAllocGuard { const hipExtent extent_; }; -enum class Streams { nullstream, perThread, created }; +enum class Streams { nullstream, perThread, created, withFlags, withPriority }; class StreamGuard { public: - StreamGuard(const Streams stream_type) : stream_type_{stream_type} { + StreamGuard(const Streams stream_type, unsigned int flags = hipStreamDefault, int priority = 0) : stream_type_{stream_type}, flags_{flags}, priority_{priority} { switch (stream_type_) { case Streams::nullstream: stream_ = nullptr; @@ -210,6 +210,11 @@ class StreamGuard { break; case Streams::created: HIP_CHECK(hipStreamCreate(&stream_)); + break; + case Streams::withFlags: + HIP_CHECK(hipStreamCreateWithFlags(&stream_, flags_)); + case Streams::withPriority: + HIP_CHECK(hipStreamCreateWithPriority(&stream_, flags_, priority_)); } } @@ -226,5 +231,53 @@ class StreamGuard { private: const Streams stream_type_; + unsigned int flags_; + int priority_; hipStream_t stream_; }; + +class EventsGuard { +public: + EventsGuard(size_t N) : events_(N) { + for (auto &e : events_) HIP_CHECK(hipEventCreate(&e)); + } + + EventsGuard(const EventsGuard&) = delete; + EventsGuard(EventsGuard&&) = delete; + + ~EventsGuard() { + for (auto &e : events_) static_cast(hipEventDestroy(e)); + } + + hipEvent_t& operator[](int index) { return events_[index]; } + + operator hipEvent_t() const { return events_.at(0); } + + std::vector& event_list() { return events_; } + +private: + std::vector events_; +}; + +class StreamsGuard { +public: + StreamsGuard(size_t N) : streams_(N) { + for (auto &s : streams_) HIP_CHECK(hipStreamCreate(&s)); + } + + StreamsGuard(const StreamsGuard&) = delete; + StreamsGuard(StreamsGuard&&) = delete; + + ~StreamsGuard() { + for (auto &s : streams_) static_cast(hipStreamDestroy(s)); + } + + hipStream_t& operator[](int index) { return streams_[index]; } + + operator hipStream_t() const { return streams_.at(0); } + + std::vector& stream_list() { return streams_; } + +private: + std::vector streams_; +}; diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index 962f493a60..32ea04d3c1 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -66,6 +66,7 @@ set(TEST_SRC hipMemCoherencyTst.cc hipMallocManaged.cc hipMemRangeGetAttribute.cc + hipMemRangeGetAttribute_old.cc hipMemcpyFromSymbol.cc hipPtrGetAttribute.cc hipMemPoolApi.cc @@ -111,6 +112,7 @@ set(TEST_SRC hipMemsetAsync.cc hipMemAdvise.cc hipMemRangeGetAttributes.cc + hipMemRangeGetAttributes_old.cc hipMemGetAddressRange.cc ) else() @@ -157,6 +159,7 @@ set(TEST_SRC hipMemAdviseMmap.cc hipMallocManaged.cc hipMemRangeGetAttribute.cc + hipMemRangeGetAttribute_old.cc hipMemcpyFromSymbol.cc hipPtrGetAttribute.cc hipMemPoolApi.cc @@ -198,6 +201,7 @@ set(TEST_SRC hipMemsetAsync.cc hipMemAdvise.cc hipMemRangeGetAttributes.cc + hipMemRangeGetAttributes_old.cc hipGetSymbolSizeAddress.cc hipMemGetAddressRange.cc ) diff --git a/catch/unit/memory/hipMemRangeGetAttribute.cc b/catch/unit/memory/hipMemRangeGetAttribute.cc index 64fc8e6be4..447a3a0b56 100644 --- a/catch/unit/memory/hipMemRangeGetAttribute.cc +++ b/catch/unit/memory/hipMemRangeGetAttribute.cc @@ -1,408 +1,359 @@ /* -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. */ -/* Test Case Description: - Scenario-1: The following function tests the count parameter(last param) to - hipMemRangeGetAttribute api by passing possible extreme values. - Curently the only way to test if count param working properly is to verify - the first parameter of hipMemRangeGetAttribute() api has value 1 stored - - Scenario-2: This test case checks the behavior of hipMemRangeGetAttribute() with - AccessedBy flag is consistent with cuda's counter part - - Scenario-3: Allocate 4 * page size of memory with the flag hipMemAttachGloal. Advise - AccessedBy, ReadMostly and PreferredLocation to first half(2*pageSz) of the - memory and probe the for the flags which are set earlier using - hipMemRangeGetAttribute() api for the full size(4*PageSz). - - - Scenario-4: The following scenarios tests that probing the attributes which are not set - by hipMemAdvise() but being probed using hipMemRangeGetAttribute() should - not result in a crash - - Scenario-5: The following scenario is a simple test which does the following: - Allocate Hmm memory --> hipMemPrefetchAsync() to device 0 and then - probe LastPrefetchLocation attribute using hipMemRangeGetAttribute - - Scenario-6: The following Test Case does negative tests on hipMemRangeGetAttribute()*/ - +#include #include -#include -#ifdef __linux__ - #include - #include -#endif +#include +#include -static bool CheckError(hipError_t err, int LineNo) { - if (err == hipSuccess) { - WARN("Error expected but received hipSuccess at line no.:" - << LineNo); - return false; - } else { - return true; +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_ReadMostly_Basic") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeReadMostly, + allocation.ptr(), kPageSize)); + + REQUIRE(data == 0); + + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetReadMostly, 0)); + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeReadMostly, + allocation.ptr(), kPageSize)); + + REQUIRE(data == 1); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_ReadMostly_Partial_Range") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, 2 * kPageSize); + + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetReadMostly, 0)); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeReadMostly, + allocation.ptr(), 2 * kPageSize)); + + REQUIRE(data == 0); + + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeReadMostly, + allocation.ptr(), kPageSize)); + + REQUIRE(data == 1); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_PreferredLocation_Basic") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributePreferredLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == hipInvalidDeviceId); + + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetPreferredLocation, 0)); + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributePreferredLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == 0); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_PreferredLocation_CPU") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); + + HIP_CHECK( + hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetPreferredLocation, hipCpuDeviceId)); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributePreferredLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == hipCpuDeviceId); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_PreferredLocation_Partial_Range") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, 2 * kPageSize); + + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetPreferredLocation, 0)); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributePreferredLocation, + allocation.ptr(), 2 * kPageSize)); + + REQUIRE(data == hipInvalidDeviceId); + + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributePreferredLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == 0); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_LastPrefetchLocation_Basic") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeLastPrefetchLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == hipInvalidDeviceId); + + HIP_CHECK(hipMemPrefetchAsync(allocation.ptr(), kPageSize, 0)); + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeLastPrefetchLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == 0); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_LastPrefetchLocation_CPU") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); + + HIP_CHECK(hipMemPrefetchAsync(allocation.ptr(), kPageSize, hipCpuDeviceId)); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeLastPrefetchLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == hipCpuDeviceId); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_LastPrefetchLocation_Partial_Range") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, 2 * kPageSize); + + HIP_CHECK(hipMemPrefetchAsync(allocation.ptr(), kPageSize, 0)); + + int32_t data; + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeLastPrefetchLocation, + allocation.ptr(), 2 * kPageSize)); + + REQUIRE(data == hipInvalidDeviceId); + + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(data), hipMemRangeAttributeLastPrefetchLocation, + allocation.ptr(), kPageSize)); + + REQUIRE(data == 0); +} + +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_AccessedBy_Basic") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); + + std::array data; + HIP_CHECK(hipMemRangeGetAttribute(data.data(), sizeof(data), hipMemRangeAttributeAccessedBy, + allocation.ptr(), kPageSize)); + + for (auto device : data) { + REQUIRE(device == hipInvalidDeviceId); + } + + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetAccessedBy, hipCpuDeviceId)); + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetAccessedBy, 0)); + HIP_CHECK(hipMemRangeGetAttribute(data.data(), sizeof(data), hipMemRangeAttributeAccessedBy, + allocation.ptr(), kPageSize)); + + // Use std::find since there is no guaranteed order in which devices will be returned + REQUIRE(std::find(cbegin(data), cend(data), hipCpuDeviceId) != cend(data)); + REQUIRE(std::find(cbegin(data), cend(data), 0) != cend(data)); + + // All the unused slots should be at the end + for (auto it = cbegin(data) + 2; it != cend(data); ++it) { + REQUIRE(*it == hipInvalidDeviceId); } } +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_AccessedBy_Partial_Range") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } -static int HmmAttrPrint() { - int managed = 0; - WARN("The following are the attribute values related to HMM for" - " device 0:\n"); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributeDirectManagedMemAccessFromHost, 0)); - WARN("hipDeviceAttributeDirectManagedMemAccessFromHost: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributeConcurrentManagedAccess, 0)); - WARN("hipDeviceAttributeConcurrentManagedAccess: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributePageableMemoryAccess, 0)); - WARN("hipDeviceAttributePageableMemoryAccess: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributePageableMemoryAccessUsesHostPageTables, 0)); - WARN("hipDeviceAttributePageableMemoryAccessUsesHostPageTables:" - << managed); + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, 2 * kPageSize); - HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory, - 0)); - WARN("hipDeviceAttributeManagedMemory: " << managed); - return managed; -} + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetAccessedBy, hipCpuDeviceId)); + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetAccessedBy, 0)); -// The following function tests the count parameter(last param) to -// hipMemRangeGetAttribute api by passing possible extreme values. -// Curently the only way to test if count param working properly is to verify -// the first parameter of hipMemRangeGetAttribute() api has value 1 stored -TEST_CASE("Unit_hipMemRangeGetAttribute_TstCountParam") { - int MangdMem = HmmAttrPrint(); - if (MangdMem == 1) { - int MEM_SIZE = 4096, RND_NUM = 9999, FLG_READMOSTLY_ENBLD = 1; - bool IfTestPassed = true; - int data = RND_NUM, *devPtr = nullptr; - size_t TotGpuMem, TotGpuFreeMem; - HIP_CHECK(hipMemGetInfo(&TotGpuFreeMem, &TotGpuMem)); + std::array data; + HIP_CHECK(hipMemRangeGetAttribute(data.data(), sizeof(data), hipMemRangeAttributeAccessedBy, + allocation.ptr(), 2 * kPageSize)); - HIP_CHECK(hipMallocManaged(&devPtr, MEM_SIZE, hipMemAttachGlobal)); - HIP_CHECK(hipMemAdvise(devPtr, MEM_SIZE, hipMemAdviseSetReadMostly, 0)); - HIP_CHECK(hipMemRangeGetAttribute(reinterpret_cast(&data), - sizeof(int), - hipMemRangeAttributeReadMostly, - devPtr, MEM_SIZE)); - if (data != FLG_READMOSTLY_ENBLD) { - WARN("hipMemRangeGetAttribute() api didnt return expected value!\n"); - IfTestPassed = false; - } - HIP_CHECK(hipFree(devPtr)); - HIP_CHECK(hipMallocManaged(&devPtr, TotGpuFreeMem, hipMemAttachGlobal)); - HIP_CHECK(hipMemAdvise(devPtr, TotGpuFreeMem, hipMemAdviseSetReadMostly, - 0)); - HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(int), - hipMemRangeAttributeReadMostly, - devPtr, TotGpuFreeMem)); + for (auto device : data) { + REQUIRE(device == hipInvalidDeviceId); + } - if (data != FLG_READMOSTLY_ENBLD) { - WARN("hipMemRangeGetAttribute() api didnt return expected value!\n"); - IfTestPassed = false; - } - HIP_CHECK(hipFree(devPtr)); - HIP_CHECK(hipMallocManaged(&devPtr, (TotGpuFreeMem - 1), - hipMemAttachGlobal)); - HIP_CHECK(hipMemAdvise(devPtr, (TotGpuFreeMem - 1), - hipMemAdviseSetReadMostly, 0)); - HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(int), - hipMemRangeAttributeReadMostly, - devPtr, (TotGpuFreeMem - 1))); + HIP_CHECK(hipMemRangeGetAttribute(data.data(), sizeof(data), hipMemRangeAttributeAccessedBy, + allocation.ptr(), kPageSize)); - if (data != FLG_READMOSTLY_ENBLD) { - WARN("hipMemRangeGetAttribute() api didnt return expected value!\n"); - IfTestPassed = false; - } - HIP_CHECK(hipFree(devPtr)); + // Use std::find since there is no guaranteed order in which devices will be returned + REQUIRE(std::find(cbegin(data), cend(data), hipCpuDeviceId) != cend(data)); + REQUIRE(std::find(cbegin(data), cend(data), 0) != cend(data)); - REQUIRE(IfTestPassed); - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); + // All the unused slots should be at the end + for (auto it = cbegin(data) + 2; it != cend(data); ++it) { + REQUIRE(*it == hipInvalidDeviceId); } } -/* The following Test Case does negative tests on hipMemRangeGetAttribute()*/ +TEST_CASE("Unit_hipMemRangeGetAttribute_Positive_AccessedBy_MultiDevice") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } -TEST_CASE("Unit_hipMemRangeGetAttribute_NegativeTests") { - int MangdMem = HmmAttrPrint(); - if (MangdMem == 1) { - int MEM_SIZE = 4096, RND_NUM = 9999; - float *devPtr = nullptr; - int NumDevs; - HIP_CHECK(hipGetDeviceCount(&NumDevs)); - int data = RND_NUM; - int *OutData = new int[NumDevs]; - for (int m = 0; m < NumDevs; ++m) { - OutData[m] = RND_NUM; - } - HIP_CHECK(hipMallocManaged(&devPtr, MEM_SIZE, hipMemAttachGlobal)); - HIP_CHECK(hipMemAdvise(devPtr, MEM_SIZE, hipMemAdviseSetReadMostly, 0)); + const auto device_count = HipTest::getDeviceCount(); + if (device_count < 2) { + HipTest::HIP_SKIP_TEST("Two or more device are required"); + return; + } - // checking the behavior with dataSize 0 - SECTION("checking the behavior with dataSize 0") { - REQUIRE(CheckError(hipMemRangeGetAttribute(&data, 0, - hipMemRangeAttributeReadMostly, - devPtr, MEM_SIZE), __LINE__)); - } - // checking the behavior with dataSize > 4 and even - SECTION("checking the behavior with dataSize > 4 and even") { - REQUIRE(CheckError(hipMemRangeGetAttribute(OutData, 6, - hipMemRangeAttributeReadMostly, - devPtr, MEM_SIZE), __LINE__)); - } - // checking the behavior with dataSize > 4 and odd - SECTION("checking the behavior with dataSize > 4 and odd") { - REQUIRE(CheckError(hipMemRangeGetAttribute(OutData, 7, - hipMemRangeAttributeReadMostly, - devPtr, MEM_SIZE), __LINE__)); - } - // checking the behavior with dataSize which is not multiple of 4 - SECTION("checking the behavior with dataSize which is not multiple of 4") { - REQUIRE(CheckError(hipMemRangeGetAttribute(OutData, 27, - hipMemRangeAttributeReadMostly, - devPtr, MEM_SIZE), __LINE__)); - } - // checking the behaviour with devPtr(4th param) as NULL - SECTION("checking the behaviour with devPtr(4th param) as NULL") { - REQUIRE(CheckError(hipMemRangeGetAttribute(&data, sizeof(int), - hipMemRangeAttributeReadMostly, - NULL, MEM_SIZE), __LINE__)); - } - // checking the behaviour with count(5th param) as 0 - SECTION("checking the behaviour with count(5th param) as 0") { - REQUIRE(CheckError(hipMemRangeGetAttribute(&data, sizeof(int), - hipMemRangeAttributeReadMostly, - devPtr, 0), __LINE__)); - } - // checking the behavior with invalid attribute (3rd param) as 0 - // as it is attribute hence avoiding the negative tests with 3rd param + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); - // checking the behaviour of the api with ptr allocated using - // hipHostMalloc - void *ptr = nullptr; - SECTION("Checking behavior with hipHostMalloc ptr") { - HIP_CHECK(hipHostMalloc(&ptr, MEM_SIZE, 0)); - REQUIRE(CheckError(hipMemRangeGetAttribute(&data, sizeof(int), - hipMemRangeAttributeReadMostly, - ptr, MEM_SIZE), __LINE__)); - HIP_CHECK(hipHostFree(ptr)); - } - HIP_CHECK(hipFree(devPtr)); - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); + std::vector data(device_count); + HIP_CHECK(hipMemRangeGetAttribute(data.data(), sizeof(data), hipMemRangeAttributeAccessedBy, + allocation.ptr(), kPageSize)); + + for (auto device : data) { + REQUIRE(device == hipInvalidDeviceId); + } + + for (auto device = 0; device < device_count; ++device) { + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetAccessedBy, device)); + } + + HIP_CHECK(hipMemRangeGetAttribute(data.data(), sizeof(data), hipMemRangeAttributeAccessedBy, + allocation.ptr(), kPageSize)); + + // Use std::find since there is no guaranteed order in which devices will be returned + for (auto device = 0; device < device_count; ++device) { + REQUIRE(std::find(cbegin(data), cend(data), device) != cend(data)); } } -/* This test case checks the behavior of hipMemRangeGetAttribute() with - AccessedBy flag is consistent with cuda's counter part*/ -TEST_CASE("Unit_hipMemRangeGetAttribute_AccessedBy1") { - int managed = HmmAttrPrint(); - if (managed == 1) { - int Ngpus = 0, *Hmm = NULL, MEM_SZ = 4096, RND_NUM = 999; - HIP_CHECK(hipGetDeviceCount(&Ngpus)); - int *OutData = new int[Ngpus]; - for (int i = 0; i < Ngpus; ++i) { - OutData[Ngpus] = RND_NUM; - } - HIP_CHECK(hipMallocManaged(&Hmm, MEM_SZ)); - HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ, hipMemAdviseSetAccessedBy, 0)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, - hipMemRangeAttributeAccessedBy, - Hmm, MEM_SZ)); - if (OutData[0] != 0) { - WARN("Didn't receive expected value at line: " << __LINE__); - REQUIRE(false); - } - for (int i = 1; i < Ngpus; ++i) { - if (OutData[i] != -2) { - WARN("Didn't receive expected value at line: " << __LINE__); - REQUIRE(false); - } - } - if (Ngpus >= 2) { - for (int i = 0; i < Ngpus; ++i) { - HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ, hipMemAdviseSetAccessedBy, i)); - } - // checking the behavior with dataSize less than the number of gpus - // This should not result in segfault. - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*(Ngpus-1), - hipMemRangeAttributeAccessedBy, - Hmm, MEM_SZ)); - // OutData should have stored the gpu ordinals for which AccessedBy is - // assigned except for the last element which should have -2 stored - // so as to be consistent with cuda's behavior - for (int i = 0; i < (Ngpus - 1); ++i) { - if (OutData[i] != i) { - WARN("Didn't receive expected value at line: " << __LINE__); - REQUIRE(false); - } - } - if (OutData[Ngpus - 1] != -2) { - WARN("Didn't receive expected value at line: " << __LINE__); - REQUIRE(false); - } - } - HIP_CHECK(hipFree(Hmm)); - delete[] OutData; - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); +TEST_CASE("Unit_hipMemRangeGetAttribute_Negative_Parameters") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + int32_t data; + LinearAllocGuard managed(LinearAllocs::hipMallocManaged, kPageSize); + + SECTION("data == nullptr") { + HIP_CHECK_ERROR(hipMemRangeGetAttribute(nullptr, 4, hipMemRangeAttributeReadMostly, + managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("data_size == 0") { + HIP_CHECK_ERROR( + hipMemRangeGetAttribute(&data, 0, hipMemRangeAttributeReadMostly, managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("data_size != 4 with hipMemRangeAttributeReadMostly") { + HIP_CHECK_ERROR( + hipMemRangeGetAttribute(&data, 8, hipMemRangeAttributeReadMostly, managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("data_size != 4 with hipMemRangeAttributePreferredLocation") { + HIP_CHECK_ERROR(hipMemRangeGetAttribute(&data, 8, hipMemRangeAttributePreferredLocation, + managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("data_size != 4 with hipMemRangeAttributeLastPrefetchLocation") { + HIP_CHECK_ERROR(hipMemRangeGetAttribute(&data, 8, hipMemRangeAttributeLastPrefetchLocation, + managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("data_size is not a multiple of 4 with hipMemRangeAttributeAccessedBy") { + HIP_CHECK_ERROR(hipMemRangeGetAttribute(&data, 10, hipMemRangeAttributeAccessedBy, + managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("invalid attribute") { + HIP_CHECK_ERROR(hipMemRangeGetAttribute(&data, 4, static_cast(999), + managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("dev_ptr == nullptr") { + HIP_CHECK_ERROR( + hipMemRangeGetAttribute(&data, 4, hipMemRangeAttributeReadMostly, nullptr, kPageSize), + hipErrorInvalidValue); + } + + SECTION("dev_ptr is not managed memory") { + LinearAllocGuard non_managed(LinearAllocs::hipMalloc, kPageSize); + HIP_CHECK_ERROR(hipMemRangeGetAttribute(&data, 4, hipMemRangeAttributeReadMostly, + non_managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("count == 0") { + HIP_CHECK_ERROR( + hipMemRangeGetAttribute(&data, 4, hipMemRangeAttributeReadMostly, managed.ptr(), 0), + hipErrorInvalidValue); } } - - - - -/* Allocate 4 * page size of memory with the flag hipMemAttachGloal. Advise - AccessedBy, ReadMostly and PreferredLocation to first half(2*pageSz) of the - memory and probe the for the flags which are set earlier using - hipMemRangeGetAttribute() api for the full size(4*PageSz).*/ -/* Need to discuss the difference in behavior w.r.t cuda*/ - -TEST_CASE("Unit_hipMemRangeGetAttribte_3") { - int managed = HmmAttrPrint(); - if (managed == 1) { - int Ngpus = 0, *Hmm = NULL, MEM_SZ = 4096*4, RND_NUM = 999; - HIP_CHECK(hipGetDeviceCount(&Ngpus)); - int *OutData = new int[Ngpus]; - for (int i = 0; i < Ngpus; ++i) { - OutData[Ngpus] = RND_NUM; - } - HIP_CHECK(hipMallocManaged(&Hmm, MEM_SZ)); - HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ/2, hipMemAdviseSetAccessedBy, 0)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, - hipMemRangeAttributeAccessedBy, - (Hmm), MEM_SZ)); - - HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ/2, hipMemAdviseSetReadMostly, 0)); - // The Api called below should not fail - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, - hipMemRangeAttributeReadMostly, - (Hmm), MEM_SZ)); - - HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ/2, hipMemAdviseSetPreferredLocation, 0)); - // The api called below should not fail - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, - hipMemRangeAttributePreferredLocation, - (Hmm), MEM_SZ)); - HIP_CHECK(hipFree(Hmm)); - delete[] OutData; - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); - } -} - - -/* The following scenarios tests that probing the attributes which are not set - by hipMemAdvise() but being probed using hipMemRangeGetAttribute() should - not result in a crash*/ - -TEST_CASE("Unit_hipMemRangeGetAttribute_4") { - int managed = HmmAttrPrint(); - if (managed == 1) { - int *Hmm = NULL, PageSz = 4096, Ngpus, RND_NUM = 999; - HIP_CHECK(hipGetDeviceCount(&Ngpus)); - int *OutData = new int[Ngpus]; - for (int i = 0; i < Ngpus; ++i) { - OutData[i] = RND_NUM; - } - HIP_CHECK(hipMallocManaged(&Hmm, 4*PageSz)); - SECTION("Set ReadMostly & probe other flags") { - HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseSetReadMostly, 0)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, - hipMemRangeAttributeAccessedBy, - Hmm, 4*PageSz)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, - hipMemRangeAttributePreferredLocation, - Hmm, 4*PageSz)); - HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseUnsetReadMostly, 0)); - } - SECTION("Set AccessedBy & probe other flags") { - HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseSetAccessedBy, 0)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, - hipMemRangeAttributeReadMostly, - Hmm, 4*PageSz)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, - hipMemRangeAttributePreferredLocation, - Hmm, 4*PageSz)); - HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseUnsetAccessedBy, 0)); - } - SECTION("Set AccessedBy & probe other flags") { - HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseSetPreferredLocation, - 0)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, - hipMemRangeAttributeReadMostly, - Hmm, 4*PageSz)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, - hipMemRangeAttributeAccessedBy, - Hmm, 4*PageSz)); - HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseUnsetPreferredLocation, - 0)); - } - HIP_CHECK(hipFree(Hmm)); - delete[] OutData; - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); - } -} - - -/* The following scenario is a simple test which does the following: - Allocate Hmm memory --> hipMemPrefetchAsync() to device 0 and then - probe LastPrefetchLocation attribute using hipMemRangeGetAttribute*/ - -TEST_CASE("Unit_hipMemRangeGetAttribute_PrefetchAndGtAttr") { - int managed = HmmAttrPrint(); - if (managed == 1) { - int Ngpus = 0, *Hmm = NULL, RND_NUM = 999; - size_t PageSz = 4096; - HIP_CHECK(hipGetDeviceCount(&Ngpus)); - - int *OutData = new int[Ngpus]; - for (int i = 0; i < Ngpus; ++i) { - OutData[Ngpus] = RND_NUM; - } - HIP_CHECK(hipMallocManaged(&Hmm, PageSz*4)); - hipStream_t strm; - HIP_CHECK(hipStreamCreate(&strm)); - HIP_CHECK(hipMemPrefetchAsync(Hmm, PageSz*4, 0, strm)); - HIP_CHECK(hipStreamSynchronize(strm)); - HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, - hipMemRangeAttributeLastPrefetchLocation, - Hmm, PageSz*4)); - HIP_CHECK(hipStreamDestroy(strm)); - HIP_CHECK(hipFree(Hmm)); - if (OutData[0] != 0) { - WARN("Didnt receive expected value at line: " << __LINE__); - delete[] OutData; - REQUIRE(false); - } - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); - } -} - diff --git a/catch/unit/memory/hipMemRangeGetAttribute_old.cc b/catch/unit/memory/hipMemRangeGetAttribute_old.cc new file mode 100644 index 0000000000..64fc8e6be4 --- /dev/null +++ b/catch/unit/memory/hipMemRangeGetAttribute_old.cc @@ -0,0 +1,408 @@ +/* +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. +*/ + +/* Test Case Description: + Scenario-1: The following function tests the count parameter(last param) to + hipMemRangeGetAttribute api by passing possible extreme values. + Curently the only way to test if count param working properly is to verify + the first parameter of hipMemRangeGetAttribute() api has value 1 stored + + Scenario-2: This test case checks the behavior of hipMemRangeGetAttribute() with + AccessedBy flag is consistent with cuda's counter part + + Scenario-3: Allocate 4 * page size of memory with the flag hipMemAttachGloal. Advise + AccessedBy, ReadMostly and PreferredLocation to first half(2*pageSz) of the + memory and probe the for the flags which are set earlier using + hipMemRangeGetAttribute() api for the full size(4*PageSz). + + + Scenario-4: The following scenarios tests that probing the attributes which are not set + by hipMemAdvise() but being probed using hipMemRangeGetAttribute() should + not result in a crash + + Scenario-5: The following scenario is a simple test which does the following: + Allocate Hmm memory --> hipMemPrefetchAsync() to device 0 and then + probe LastPrefetchLocation attribute using hipMemRangeGetAttribute + + Scenario-6: The following Test Case does negative tests on hipMemRangeGetAttribute()*/ + +#include +#include +#ifdef __linux__ + #include + #include +#endif + +static bool CheckError(hipError_t err, int LineNo) { + if (err == hipSuccess) { + WARN("Error expected but received hipSuccess at line no.:" + << LineNo); + return false; + } else { + return true; + } +} + + +static int HmmAttrPrint() { + int managed = 0; + WARN("The following are the attribute values related to HMM for" + " device 0:\n"); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributeDirectManagedMemAccessFromHost, 0)); + WARN("hipDeviceAttributeDirectManagedMemAccessFromHost: " << managed); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributeConcurrentManagedAccess, 0)); + WARN("hipDeviceAttributeConcurrentManagedAccess: " << managed); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributePageableMemoryAccess, 0)); + WARN("hipDeviceAttributePageableMemoryAccess: " << managed); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributePageableMemoryAccessUsesHostPageTables, 0)); + WARN("hipDeviceAttributePageableMemoryAccessUsesHostPageTables:" + << managed); + + HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory, + 0)); + WARN("hipDeviceAttributeManagedMemory: " << managed); + return managed; +} + +// The following function tests the count parameter(last param) to +// hipMemRangeGetAttribute api by passing possible extreme values. +// Curently the only way to test if count param working properly is to verify +// the first parameter of hipMemRangeGetAttribute() api has value 1 stored +TEST_CASE("Unit_hipMemRangeGetAttribute_TstCountParam") { + int MangdMem = HmmAttrPrint(); + if (MangdMem == 1) { + int MEM_SIZE = 4096, RND_NUM = 9999, FLG_READMOSTLY_ENBLD = 1; + bool IfTestPassed = true; + int data = RND_NUM, *devPtr = nullptr; + size_t TotGpuMem, TotGpuFreeMem; + HIP_CHECK(hipMemGetInfo(&TotGpuFreeMem, &TotGpuMem)); + + HIP_CHECK(hipMallocManaged(&devPtr, MEM_SIZE, hipMemAttachGlobal)); + HIP_CHECK(hipMemAdvise(devPtr, MEM_SIZE, hipMemAdviseSetReadMostly, 0)); + HIP_CHECK(hipMemRangeGetAttribute(reinterpret_cast(&data), + sizeof(int), + hipMemRangeAttributeReadMostly, + devPtr, MEM_SIZE)); + if (data != FLG_READMOSTLY_ENBLD) { + WARN("hipMemRangeGetAttribute() api didnt return expected value!\n"); + IfTestPassed = false; + } + HIP_CHECK(hipFree(devPtr)); + HIP_CHECK(hipMallocManaged(&devPtr, TotGpuFreeMem, hipMemAttachGlobal)); + HIP_CHECK(hipMemAdvise(devPtr, TotGpuFreeMem, hipMemAdviseSetReadMostly, + 0)); + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(int), + hipMemRangeAttributeReadMostly, + devPtr, TotGpuFreeMem)); + + if (data != FLG_READMOSTLY_ENBLD) { + WARN("hipMemRangeGetAttribute() api didnt return expected value!\n"); + IfTestPassed = false; + } + HIP_CHECK(hipFree(devPtr)); + HIP_CHECK(hipMallocManaged(&devPtr, (TotGpuFreeMem - 1), + hipMemAttachGlobal)); + HIP_CHECK(hipMemAdvise(devPtr, (TotGpuFreeMem - 1), + hipMemAdviseSetReadMostly, 0)); + HIP_CHECK(hipMemRangeGetAttribute(&data, sizeof(int), + hipMemRangeAttributeReadMostly, + devPtr, (TotGpuFreeMem - 1))); + + if (data != FLG_READMOSTLY_ENBLD) { + WARN("hipMemRangeGetAttribute() api didnt return expected value!\n"); + IfTestPassed = false; + } + HIP_CHECK(hipFree(devPtr)); + + REQUIRE(IfTestPassed); + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} + +/* The following Test Case does negative tests on hipMemRangeGetAttribute()*/ + +TEST_CASE("Unit_hipMemRangeGetAttribute_NegativeTests") { + int MangdMem = HmmAttrPrint(); + if (MangdMem == 1) { + int MEM_SIZE = 4096, RND_NUM = 9999; + float *devPtr = nullptr; + int NumDevs; + HIP_CHECK(hipGetDeviceCount(&NumDevs)); + int data = RND_NUM; + int *OutData = new int[NumDevs]; + for (int m = 0; m < NumDevs; ++m) { + OutData[m] = RND_NUM; + } + HIP_CHECK(hipMallocManaged(&devPtr, MEM_SIZE, hipMemAttachGlobal)); + HIP_CHECK(hipMemAdvise(devPtr, MEM_SIZE, hipMemAdviseSetReadMostly, 0)); + + // checking the behavior with dataSize 0 + SECTION("checking the behavior with dataSize 0") { + REQUIRE(CheckError(hipMemRangeGetAttribute(&data, 0, + hipMemRangeAttributeReadMostly, + devPtr, MEM_SIZE), __LINE__)); + } + // checking the behavior with dataSize > 4 and even + SECTION("checking the behavior with dataSize > 4 and even") { + REQUIRE(CheckError(hipMemRangeGetAttribute(OutData, 6, + hipMemRangeAttributeReadMostly, + devPtr, MEM_SIZE), __LINE__)); + } + // checking the behavior with dataSize > 4 and odd + SECTION("checking the behavior with dataSize > 4 and odd") { + REQUIRE(CheckError(hipMemRangeGetAttribute(OutData, 7, + hipMemRangeAttributeReadMostly, + devPtr, MEM_SIZE), __LINE__)); + } + // checking the behavior with dataSize which is not multiple of 4 + SECTION("checking the behavior with dataSize which is not multiple of 4") { + REQUIRE(CheckError(hipMemRangeGetAttribute(OutData, 27, + hipMemRangeAttributeReadMostly, + devPtr, MEM_SIZE), __LINE__)); + } + // checking the behaviour with devPtr(4th param) as NULL + SECTION("checking the behaviour with devPtr(4th param) as NULL") { + REQUIRE(CheckError(hipMemRangeGetAttribute(&data, sizeof(int), + hipMemRangeAttributeReadMostly, + NULL, MEM_SIZE), __LINE__)); + } + // checking the behaviour with count(5th param) as 0 + SECTION("checking the behaviour with count(5th param) as 0") { + REQUIRE(CheckError(hipMemRangeGetAttribute(&data, sizeof(int), + hipMemRangeAttributeReadMostly, + devPtr, 0), __LINE__)); + } + // checking the behavior with invalid attribute (3rd param) as 0 + // as it is attribute hence avoiding the negative tests with 3rd param + + // checking the behaviour of the api with ptr allocated using + // hipHostMalloc + void *ptr = nullptr; + SECTION("Checking behavior with hipHostMalloc ptr") { + HIP_CHECK(hipHostMalloc(&ptr, MEM_SIZE, 0)); + REQUIRE(CheckError(hipMemRangeGetAttribute(&data, sizeof(int), + hipMemRangeAttributeReadMostly, + ptr, MEM_SIZE), __LINE__)); + HIP_CHECK(hipHostFree(ptr)); + } + HIP_CHECK(hipFree(devPtr)); + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} + +/* This test case checks the behavior of hipMemRangeGetAttribute() with + AccessedBy flag is consistent with cuda's counter part*/ +TEST_CASE("Unit_hipMemRangeGetAttribute_AccessedBy1") { + int managed = HmmAttrPrint(); + if (managed == 1) { + int Ngpus = 0, *Hmm = NULL, MEM_SZ = 4096, RND_NUM = 999; + HIP_CHECK(hipGetDeviceCount(&Ngpus)); + int *OutData = new int[Ngpus]; + for (int i = 0; i < Ngpus; ++i) { + OutData[Ngpus] = RND_NUM; + } + HIP_CHECK(hipMallocManaged(&Hmm, MEM_SZ)); + HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ, hipMemAdviseSetAccessedBy, 0)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, + hipMemRangeAttributeAccessedBy, + Hmm, MEM_SZ)); + if (OutData[0] != 0) { + WARN("Didn't receive expected value at line: " << __LINE__); + REQUIRE(false); + } + for (int i = 1; i < Ngpus; ++i) { + if (OutData[i] != -2) { + WARN("Didn't receive expected value at line: " << __LINE__); + REQUIRE(false); + } + } + if (Ngpus >= 2) { + for (int i = 0; i < Ngpus; ++i) { + HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ, hipMemAdviseSetAccessedBy, i)); + } + // checking the behavior with dataSize less than the number of gpus + // This should not result in segfault. + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*(Ngpus-1), + hipMemRangeAttributeAccessedBy, + Hmm, MEM_SZ)); + // OutData should have stored the gpu ordinals for which AccessedBy is + // assigned except for the last element which should have -2 stored + // so as to be consistent with cuda's behavior + for (int i = 0; i < (Ngpus - 1); ++i) { + if (OutData[i] != i) { + WARN("Didn't receive expected value at line: " << __LINE__); + REQUIRE(false); + } + } + if (OutData[Ngpus - 1] != -2) { + WARN("Didn't receive expected value at line: " << __LINE__); + REQUIRE(false); + } + } + HIP_CHECK(hipFree(Hmm)); + delete[] OutData; + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} + + + + +/* Allocate 4 * page size of memory with the flag hipMemAttachGloal. Advise + AccessedBy, ReadMostly and PreferredLocation to first half(2*pageSz) of the + memory and probe the for the flags which are set earlier using + hipMemRangeGetAttribute() api for the full size(4*PageSz).*/ +/* Need to discuss the difference in behavior w.r.t cuda*/ + +TEST_CASE("Unit_hipMemRangeGetAttribte_3") { + int managed = HmmAttrPrint(); + if (managed == 1) { + int Ngpus = 0, *Hmm = NULL, MEM_SZ = 4096*4, RND_NUM = 999; + HIP_CHECK(hipGetDeviceCount(&Ngpus)); + int *OutData = new int[Ngpus]; + for (int i = 0; i < Ngpus; ++i) { + OutData[Ngpus] = RND_NUM; + } + HIP_CHECK(hipMallocManaged(&Hmm, MEM_SZ)); + HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ/2, hipMemAdviseSetAccessedBy, 0)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, + hipMemRangeAttributeAccessedBy, + (Hmm), MEM_SZ)); + + HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ/2, hipMemAdviseSetReadMostly, 0)); + // The Api called below should not fail + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, + hipMemRangeAttributeReadMostly, + (Hmm), MEM_SZ)); + + HIP_CHECK(hipMemAdvise(Hmm, MEM_SZ/2, hipMemAdviseSetPreferredLocation, 0)); + // The api called below should not fail + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, + hipMemRangeAttributePreferredLocation, + (Hmm), MEM_SZ)); + HIP_CHECK(hipFree(Hmm)); + delete[] OutData; + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} + + +/* The following scenarios tests that probing the attributes which are not set + by hipMemAdvise() but being probed using hipMemRangeGetAttribute() should + not result in a crash*/ + +TEST_CASE("Unit_hipMemRangeGetAttribute_4") { + int managed = HmmAttrPrint(); + if (managed == 1) { + int *Hmm = NULL, PageSz = 4096, Ngpus, RND_NUM = 999; + HIP_CHECK(hipGetDeviceCount(&Ngpus)); + int *OutData = new int[Ngpus]; + for (int i = 0; i < Ngpus; ++i) { + OutData[i] = RND_NUM; + } + HIP_CHECK(hipMallocManaged(&Hmm, 4*PageSz)); + SECTION("Set ReadMostly & probe other flags") { + HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseSetReadMostly, 0)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, + hipMemRangeAttributeAccessedBy, + Hmm, 4*PageSz)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, + hipMemRangeAttributePreferredLocation, + Hmm, 4*PageSz)); + HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseUnsetReadMostly, 0)); + } + SECTION("Set AccessedBy & probe other flags") { + HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseSetAccessedBy, 0)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, + hipMemRangeAttributeReadMostly, + Hmm, 4*PageSz)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, + hipMemRangeAttributePreferredLocation, + Hmm, 4*PageSz)); + HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseUnsetAccessedBy, 0)); + } + SECTION("Set AccessedBy & probe other flags") { + HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseSetPreferredLocation, + 0)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, + hipMemRangeAttributeReadMostly, + Hmm, 4*PageSz)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4*Ngpus, + hipMemRangeAttributeAccessedBy, + Hmm, 4*PageSz)); + HIP_CHECK(hipMemAdvise(Hmm, 4*PageSz, hipMemAdviseUnsetPreferredLocation, + 0)); + } + HIP_CHECK(hipFree(Hmm)); + delete[] OutData; + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} + + +/* The following scenario is a simple test which does the following: + Allocate Hmm memory --> hipMemPrefetchAsync() to device 0 and then + probe LastPrefetchLocation attribute using hipMemRangeGetAttribute*/ + +TEST_CASE("Unit_hipMemRangeGetAttribute_PrefetchAndGtAttr") { + int managed = HmmAttrPrint(); + if (managed == 1) { + int Ngpus = 0, *Hmm = NULL, RND_NUM = 999; + size_t PageSz = 4096; + HIP_CHECK(hipGetDeviceCount(&Ngpus)); + + int *OutData = new int[Ngpus]; + for (int i = 0; i < Ngpus; ++i) { + OutData[Ngpus] = RND_NUM; + } + HIP_CHECK(hipMallocManaged(&Hmm, PageSz*4)); + hipStream_t strm; + HIP_CHECK(hipStreamCreate(&strm)); + HIP_CHECK(hipMemPrefetchAsync(Hmm, PageSz*4, 0, strm)); + HIP_CHECK(hipStreamSynchronize(strm)); + HIP_CHECK(hipMemRangeGetAttribute(OutData, 4, + hipMemRangeAttributeLastPrefetchLocation, + Hmm, PageSz*4)); + HIP_CHECK(hipStreamDestroy(strm)); + HIP_CHECK(hipFree(Hmm)); + if (OutData[0] != 0) { + WARN("Didnt receive expected value at line: " << __LINE__); + delete[] OutData; + REQUIRE(false); + } + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} + diff --git a/catch/unit/memory/hipMemRangeGetAttributes.cc b/catch/unit/memory/hipMemRangeGetAttributes.cc index e1240f4330..a3a9a5bf2a 100644 --- a/catch/unit/memory/hipMemRangeGetAttributes.cc +++ b/catch/unit/memory/hipMemRangeGetAttributes.cc @@ -1,325 +1,155 @@ /* 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. */ -/* Test Case Description: - Scenario-1: Testing basic working of hipMemRangeGetAttributes() - api with different flags - Scenario-2: Negative testing with hipMemRangeGetAttributes() api -*/ - +#include #include -#define MEM_SIZE 8192 +#include +#include -static bool CheckError(hipError_t err, int LineNo) { - if (err == hipSuccess) { - WARN("Error expected but received hipSuccess at line no.:" - << LineNo); - return false; - } else { - return true; +TEST_CASE("Unit_hipMemRangeGetAttributes_Positive_Basic") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } + + LinearAllocGuard allocation(LinearAllocs::hipMallocManaged, kPageSize); + + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetReadMostly, 0)); + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetPreferredLocation, 0)); + HIP_CHECK(hipMemPrefetchAsync(allocation.ptr(), kPageSize, hipCpuDeviceId)); + HIP_CHECK(hipMemAdvise(allocation.ptr(), kPageSize, hipMemAdviseSetAccessedBy, 0)); + + constexpr size_t num_attributes = 4; + std::array attributes = { + hipMemRangeAttributeReadMostly, hipMemRangeAttributePreferredLocation, + hipMemRangeAttributeLastPrefetchLocation, hipMemRangeAttributeAccessedBy}; + + std::array data; + for (auto& ptr : data) { + ptr = new int32_t; + } + std::array data_sizes = {4, 4, 4, 4}; + + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(data.data()), data_sizes.data(), + attributes.data(), num_attributes, allocation.ptr(), + kPageSize)); + + REQUIRE(data[0][0] == 1); + REQUIRE(data[1][0] == 0); + REQUIRE(data[2][0] == hipCpuDeviceId); + REQUIRE(data[3][0] == 0); + + for (auto ptr : data) { + delete ptr; } } -static int HmmAttrPrint() { - int managed = 0; - WARN("The following are the attribute values related to HMM for" - " device 0:\n"); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributeDirectManagedMemAccessFromHost, 0)); - WARN("hipDeviceAttributeDirectManagedMemAccessFromHost: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributeConcurrentManagedAccess, 0)); - WARN("hipDeviceAttributeConcurrentManagedAccess: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributePageableMemoryAccess, 0)); - WARN("hipDeviceAttributePageableMemoryAccess: " << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, - hipDeviceAttributePageableMemoryAccessUsesHostPageTables, 0)); - WARN("hipDeviceAttributePageableMemoryAccessUsesHostPageTables:" - << managed); - HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory, - 0)); - WARN("hipDeviceAttributeManagedMemory: " << managed); - return managed; -} +TEST_CASE("Unit_hipMemRangeGetAttributes_Negative_Parameters") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory not supported"); + return; + } -#ifdef __linux__ -/* Test Scenario: Testing basic working of hipMemRangeGetAttributes() - api with different flags */ + constexpr size_t num_attributes = 4; + hipMemRangeAttribute attributes[] = { + hipMemRangeAttributeReadMostly, hipMemRangeAttributePreferredLocation, + hipMemRangeAttributeLastPrefetchLocation, hipMemRangeAttributeAccessedBy}; -TEST_CASE("Unit_hipMemRangeGetAttributes_TstFlgs") { - int MangdMem = HmmAttrPrint(); - if (MangdMem == 1) { - bool IfTestPassed = true; - int NumDevs = 0; - int *Outpt[4], *AcsdBy = nullptr; - float *Hmm = nullptr; - hipStream_t strm; - hipMemRangeAttribute AttrArr[4] = - {hipMemRangeAttributeReadMostly, - hipMemRangeAttributePreferredLocation, - hipMemRangeAttributeAccessedBy, - hipMemRangeAttributeLastPrefetchLocation}; - HIP_CHECK(hipGetDeviceCount(&NumDevs)); - AcsdBy = new int(NumDevs); - size_t dataSizes[4] = {sizeof(int), sizeof(int), - (NumDevs * sizeof(int)), sizeof(int)}; - Outpt[0] = new int; - Outpt[1] = new int; - Outpt[2] = new int[NumDevs]; - Outpt[3] = new int; - HIP_CHECK(hipMallocManaged(&Hmm, MEM_SIZE, hipMemAttachGlobal)); - for (int i = 0; i < NumDevs; ++i) { - HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseSetReadMostly, i)); - HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), - dataSizes, AttrArr, 4, Hmm, - MEM_SIZE)); - if (*(Outpt[0]) != 1) { - WARN("Attempt to set hipMemAdviseSetReadMostly flag failed!\n"); - IfTestPassed = false; - } - HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseUnsetReadMostly, i)); - HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE)); + int32_t* data[num_attributes]; + for (auto& ptr : data) { + ptr = new int32_t; + } + size_t data_sizes[] = {4, 4, 4, 4}; - if (*(Outpt[0]) != 0) { - WARN("Attempt to set hipMemAdviseUnsetReadMostly flag failed!\n"); - IfTestPassed = false; - } + LinearAllocGuard managed(LinearAllocs::hipMallocManaged, kPageSize); - HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, - hipMemAdviseSetPreferredLocation, i)); - HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE)); - if (*(Outpt[1]) != i) { - WARN("Attempt to set hipMemAdviseSetPreferredLocation flag"); - WARN(" failed!\n"); - IfTestPassed = false; - } - HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseSetAccessedBy, i)); - HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE)); - if ((Outpt[2][0]) != i) { - WARN("Attempt to set hipMemAdviseSetAccessedBy flag"); - WARN(" failed!\n"); - IfTestPassed = false; - } + SECTION("data == nullptr") { + HIP_CHECK_ERROR(hipMemRangeGetAttributes(nullptr, data_sizes, attributes, num_attributes, + managed.ptr(), kPageSize), + hipErrorInvalidValue); + } - HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseUnsetAccessedBy, i)); - HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE)); - if (!((Outpt[2][i]) < 0)) { - WARN("Attempt to set hipMemAdviseUnsetAccessedBy flag failed!\n"); - IfTestPassed = false; - } - HIP_CHECK(hipStreamCreate(&strm)); - HIP_CHECK(hipMemPrefetchAsync(Hmm, MEM_SIZE, i, strm)); - HIP_CHECK(hipStreamSynchronize(strm)); - HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE)); - if (*(Outpt[3]) != i) { - WARN("Attempt to prefetch memory to device: " << i); - WARN("failed!\n"); - IfTestPassed = false; - } - // Prefetching back to Host - HIP_CHECK(hipMemPrefetchAsync(Hmm, MEM_SIZE, -1, strm)); - HIP_CHECK(hipStreamSynchronize(strm)); - HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE)); - if (*(Outpt[3]) != -1) { - WARN("Attempt to prefetch memory to Host failed!\n"); - IfTestPassed = false; - } - } + SECTION("data contains invalid pointers") { + void* invalid_data[num_attributes] = {nullptr}; + HIP_CHECK_ERROR(hipMemRangeGetAttributes(invalid_data, data_sizes, attributes, num_attributes, + managed.ptr(), kPageSize), + hipErrorInvalidValue); + } - HIP_CHECK(hipFree(Hmm)); - delete[] AcsdBy; - for (int i = 0; i < 4; ++i) { - delete Outpt[i]; - } - REQUIRE(IfTestPassed); - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); + SECTION("data_sizes == nullptr") { + HIP_CHECK_ERROR(hipMemRangeGetAttributes(reinterpret_cast(data), nullptr, attributes, + num_attributes, managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("data_sizes contains invalid values") { + size_t invalid_data_sizes[] = {4, 5, 4, 6}; + HIP_CHECK_ERROR(hipMemRangeGetAttributes(reinterpret_cast(data), invalid_data_sizes, + attributes, num_attributes, managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("attributes == nullptr") { + HIP_CHECK_ERROR(hipMemRangeGetAttributes(reinterpret_cast(data), data_sizes, nullptr, + num_attributes, managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("attributes contains invalid attributes") { + hipMemRangeAttribute invalid_attributes[] = { + hipMemRangeAttributeReadMostly, hipMemRangeAttributePreferredLocation, + static_cast(999), hipMemRangeAttributeAccessedBy}; + HIP_CHECK_ERROR( + hipMemRangeGetAttributes(reinterpret_cast(data), data_sizes, invalid_attributes, + num_attributes, managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("num_attributes == 0") { + HIP_CHECK_ERROR(hipMemRangeGetAttributes(reinterpret_cast(data), data_sizes, attributes, + 0, managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("dev_ptr == nullptr") { + HIP_CHECK_ERROR(hipMemRangeGetAttributes(reinterpret_cast(data), data_sizes, attributes, + num_attributes, nullptr, kPageSize), + hipErrorInvalidValue); + } + + SECTION("dev_ptr is not managed memory") { + LinearAllocGuard non_managed(LinearAllocs::hipMalloc, kPageSize); + HIP_CHECK_ERROR(hipMemRangeGetAttributes(reinterpret_cast(data), data_sizes, attributes, + num_attributes, non_managed.ptr(), kPageSize), + hipErrorInvalidValue); + } + + SECTION("count == 0") { + HIP_CHECK_ERROR(hipMemRangeGetAttributes(reinterpret_cast(data), data_sizes, attributes, + num_attributes, managed.ptr(), 0), + hipErrorInvalidValue); + } + + for (auto ptr : data) { + delete ptr; } } - -/* Test Scenario: Negative testing with hipMemRangeGetAttributes() api*/ -TEST_CASE("Unit_hipMemRangeGetAttributes_NegativeTst") { - int MangdMem = HmmAttrPrint(); - if (MangdMem == 1) { - bool IfTestPassed = true; - int NumDevs = 0, *Outpt[4]; - float *Hmm = nullptr; - hipMemRangeAttribute AttrArr[4] = - {hipMemRangeAttributeReadMostly, - hipMemRangeAttributePreferredLocation, - hipMemRangeAttributeAccessedBy, - hipMemRangeAttributeLastPrefetchLocation}; - HIP_CHECK(hipGetDeviceCount(&NumDevs)); - size_t dataSizes[4] = {sizeof(int), sizeof(int), - (NumDevs * sizeof(int)), sizeof(int)}; - Outpt[0] = new int; - Outpt[1] = new int; - Outpt[2] = new int[NumDevs]; - Outpt[3] = new int; - HIP_CHECK(hipMallocManaged(&Hmm, MEM_SIZE, hipMemAttachGlobal)); - HIP_CHECK(hipMemAdvise(Hmm , MEM_SIZE, hipMemAdviseSetReadMostly, 0)); - // passing zero for num of attributes param(4th) - SECTION("passing zero for num of attributes param(4th)") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 0, Hmm, MEM_SIZE), __LINE__)) { - IfTestPassed = false; - } - } - - // the first dataSize element passed as 0 - dataSizes[0] = 0; - dataSizes[1] = sizeof(int); - dataSizes[2] = NumDevs * sizeof(int); - dataSizes[3] = sizeof(int); - SECTION("the first dataSize element passed as 0") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE), - __LINE__)) { - IfTestPassed = false; - } - } - // passing datasize as 2 while the requirement is multiple of 4 - dataSizes[0] = 2; - dataSizes[1] = sizeof(int); - dataSizes[2] = NumDevs * sizeof(int); - dataSizes[3] = sizeof(int); - SECTION("datasize as 2 while the requirement is multiple of 4") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE), - __LINE__)) { - IfTestPassed = false; - } - } - // passing datasize as 6 while the requirement is multiple of 4 - dataSizes[0] = 6; - dataSizes[1] = sizeof(int); - dataSizes[2] = NumDevs * sizeof(int); - dataSizes[3] = sizeof(int); - SECTION("datasize as 6 while the requirement is multiple of 4") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE), - __LINE__)) { - IfTestPassed = false; - } - } - // passing datasize as 7 while the requirement is multiple of 4 - dataSizes[0] = 7; - dataSizes[1] = sizeof(int); - dataSizes[2] = NumDevs * sizeof(int); - dataSizes[3] = sizeof(int); - SECTION("datasize as 7 while the requirement is multiple of 4") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE), - __LINE__)) { - IfTestPassed = false; - } - } - // passing dataSize as 7 for attribute hipMemRangeAttributeAccessedBy - hipMemRangeAttribute AttrArr1[1] = {hipMemRangeAttributeAccessedBy}; - dataSizes[2] = {7}; - SECTION("passing dataSize as 7 for attribute hipMemRangeAttrAccessedBy") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr1, 1, Hmm, MEM_SIZE), __LINE__)) { - IfTestPassed = false; - } - } - // Passing NULL as first parameter - SECTION("Passing NULL as first parameter") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(NULL), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, MEM_SIZE), - __LINE__)) { - IfTestPassed = false; - } - } - // Passing count parameter as zero - SECTION("Passing count parameter as zero") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - AttrArr, 4, Hmm, 0), - __LINE__)) { - IfTestPassed = false; - } - } - // Passing NULL for Attribute array(3rd param) - SECTION("Passing NULL for Attribute array(3rd param)") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - NULL, 4, Hmm, MEM_SIZE), - __LINE__)) { - IfTestPassed = false; - } - } - // Passing 0 for Attribute array(3rd param) - SECTION("Passing 0 for Attribute array(3rd param)") { - if (!CheckError(hipMemRangeGetAttributes( - reinterpret_cast(Outpt), - reinterpret_cast(dataSizes), - 0, 4, Hmm, MEM_SIZE), - __LINE__)) { - IfTestPassed = false; - } - } - for (int i = 0; i < 4; ++i) { - delete Outpt[i]; - } - REQUIRE(IfTestPassed); - - // The following scenarios have been removed considering the nature of the - // api. With Consultation with Maneesh Gupta, the following scenarios - // have been removed. - // passing numAttributes as 4 while the attributes array has only 2 members - // passing numAttributes as 10 while the attributes array has only 2 members - // length of the list of dataSizes less than the number of - // attributes being probed - } else { - SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " - "attribute. Hence skipping the testing with Pass result.\n"); - } -} -#endif diff --git a/catch/unit/memory/hipMemRangeGetAttributes_old.cc b/catch/unit/memory/hipMemRangeGetAttributes_old.cc new file mode 100644 index 0000000000..e1240f4330 --- /dev/null +++ b/catch/unit/memory/hipMemRangeGetAttributes_old.cc @@ -0,0 +1,325 @@ +/* +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. +*/ + +/* Test Case Description: + Scenario-1: Testing basic working of hipMemRangeGetAttributes() + api with different flags + Scenario-2: Negative testing with hipMemRangeGetAttributes() api +*/ + +#include +#define MEM_SIZE 8192 + +static bool CheckError(hipError_t err, int LineNo) { + if (err == hipSuccess) { + WARN("Error expected but received hipSuccess at line no.:" + << LineNo); + return false; + } else { + return true; + } +} + +static int HmmAttrPrint() { + int managed = 0; + WARN("The following are the attribute values related to HMM for" + " device 0:\n"); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributeDirectManagedMemAccessFromHost, 0)); + WARN("hipDeviceAttributeDirectManagedMemAccessFromHost: " << managed); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributeConcurrentManagedAccess, 0)); + WARN("hipDeviceAttributeConcurrentManagedAccess: " << managed); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributePageableMemoryAccess, 0)); + WARN("hipDeviceAttributePageableMemoryAccess: " << managed); + HIP_CHECK(hipDeviceGetAttribute(&managed, + hipDeviceAttributePageableMemoryAccessUsesHostPageTables, 0)); + WARN("hipDeviceAttributePageableMemoryAccessUsesHostPageTables:" + << managed); + HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory, + 0)); + WARN("hipDeviceAttributeManagedMemory: " << managed); + return managed; +} + +#ifdef __linux__ +/* Test Scenario: Testing basic working of hipMemRangeGetAttributes() + api with different flags */ + +TEST_CASE("Unit_hipMemRangeGetAttributes_TstFlgs") { + int MangdMem = HmmAttrPrint(); + if (MangdMem == 1) { + bool IfTestPassed = true; + int NumDevs = 0; + int *Outpt[4], *AcsdBy = nullptr; + float *Hmm = nullptr; + hipStream_t strm; + hipMemRangeAttribute AttrArr[4] = + {hipMemRangeAttributeReadMostly, + hipMemRangeAttributePreferredLocation, + hipMemRangeAttributeAccessedBy, + hipMemRangeAttributeLastPrefetchLocation}; + HIP_CHECK(hipGetDeviceCount(&NumDevs)); + AcsdBy = new int(NumDevs); + size_t dataSizes[4] = {sizeof(int), sizeof(int), + (NumDevs * sizeof(int)), sizeof(int)}; + Outpt[0] = new int; + Outpt[1] = new int; + Outpt[2] = new int[NumDevs]; + Outpt[3] = new int; + HIP_CHECK(hipMallocManaged(&Hmm, MEM_SIZE, hipMemAttachGlobal)); + for (int i = 0; i < NumDevs; ++i) { + HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseSetReadMostly, i)); + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), + dataSizes, AttrArr, 4, Hmm, + MEM_SIZE)); + if (*(Outpt[0]) != 1) { + WARN("Attempt to set hipMemAdviseSetReadMostly flag failed!\n"); + IfTestPassed = false; + } + HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseUnsetReadMostly, i)); + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE)); + + if (*(Outpt[0]) != 0) { + WARN("Attempt to set hipMemAdviseUnsetReadMostly flag failed!\n"); + IfTestPassed = false; + } + + HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, + hipMemAdviseSetPreferredLocation, i)); + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE)); + if (*(Outpt[1]) != i) { + WARN("Attempt to set hipMemAdviseSetPreferredLocation flag"); + WARN(" failed!\n"); + IfTestPassed = false; + } + HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseSetAccessedBy, i)); + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE)); + if ((Outpt[2][0]) != i) { + WARN("Attempt to set hipMemAdviseSetAccessedBy flag"); + WARN(" failed!\n"); + IfTestPassed = false; + } + + HIP_CHECK(hipMemAdvise(Hmm, MEM_SIZE, hipMemAdviseUnsetAccessedBy, i)); + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE)); + if (!((Outpt[2][i]) < 0)) { + WARN("Attempt to set hipMemAdviseUnsetAccessedBy flag failed!\n"); + IfTestPassed = false; + } + HIP_CHECK(hipStreamCreate(&strm)); + HIP_CHECK(hipMemPrefetchAsync(Hmm, MEM_SIZE, i, strm)); + HIP_CHECK(hipStreamSynchronize(strm)); + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE)); + if (*(Outpt[3]) != i) { + WARN("Attempt to prefetch memory to device: " << i); + WARN("failed!\n"); + IfTestPassed = false; + } + // Prefetching back to Host + HIP_CHECK(hipMemPrefetchAsync(Hmm, MEM_SIZE, -1, strm)); + HIP_CHECK(hipStreamSynchronize(strm)); + HIP_CHECK(hipMemRangeGetAttributes(reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE)); + if (*(Outpt[3]) != -1) { + WARN("Attempt to prefetch memory to Host failed!\n"); + IfTestPassed = false; + } + } + + HIP_CHECK(hipFree(Hmm)); + delete[] AcsdBy; + for (int i = 0; i < 4; ++i) { + delete Outpt[i]; + } + REQUIRE(IfTestPassed); + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} + +/* Test Scenario: Negative testing with hipMemRangeGetAttributes() api*/ +TEST_CASE("Unit_hipMemRangeGetAttributes_NegativeTst") { + int MangdMem = HmmAttrPrint(); + if (MangdMem == 1) { + bool IfTestPassed = true; + int NumDevs = 0, *Outpt[4]; + float *Hmm = nullptr; + hipMemRangeAttribute AttrArr[4] = + {hipMemRangeAttributeReadMostly, + hipMemRangeAttributePreferredLocation, + hipMemRangeAttributeAccessedBy, + hipMemRangeAttributeLastPrefetchLocation}; + HIP_CHECK(hipGetDeviceCount(&NumDevs)); + size_t dataSizes[4] = {sizeof(int), sizeof(int), + (NumDevs * sizeof(int)), sizeof(int)}; + Outpt[0] = new int; + Outpt[1] = new int; + Outpt[2] = new int[NumDevs]; + Outpt[3] = new int; + HIP_CHECK(hipMallocManaged(&Hmm, MEM_SIZE, hipMemAttachGlobal)); + HIP_CHECK(hipMemAdvise(Hmm , MEM_SIZE, hipMemAdviseSetReadMostly, 0)); + // passing zero for num of attributes param(4th) + SECTION("passing zero for num of attributes param(4th)") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 0, Hmm, MEM_SIZE), __LINE__)) { + IfTestPassed = false; + } + } + + // the first dataSize element passed as 0 + dataSizes[0] = 0; + dataSizes[1] = sizeof(int); + dataSizes[2] = NumDevs * sizeof(int); + dataSizes[3] = sizeof(int); + SECTION("the first dataSize element passed as 0") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE), + __LINE__)) { + IfTestPassed = false; + } + } + // passing datasize as 2 while the requirement is multiple of 4 + dataSizes[0] = 2; + dataSizes[1] = sizeof(int); + dataSizes[2] = NumDevs * sizeof(int); + dataSizes[3] = sizeof(int); + SECTION("datasize as 2 while the requirement is multiple of 4") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE), + __LINE__)) { + IfTestPassed = false; + } + } + // passing datasize as 6 while the requirement is multiple of 4 + dataSizes[0] = 6; + dataSizes[1] = sizeof(int); + dataSizes[2] = NumDevs * sizeof(int); + dataSizes[3] = sizeof(int); + SECTION("datasize as 6 while the requirement is multiple of 4") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE), + __LINE__)) { + IfTestPassed = false; + } + } + // passing datasize as 7 while the requirement is multiple of 4 + dataSizes[0] = 7; + dataSizes[1] = sizeof(int); + dataSizes[2] = NumDevs * sizeof(int); + dataSizes[3] = sizeof(int); + SECTION("datasize as 7 while the requirement is multiple of 4") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE), + __LINE__)) { + IfTestPassed = false; + } + } + // passing dataSize as 7 for attribute hipMemRangeAttributeAccessedBy + hipMemRangeAttribute AttrArr1[1] = {hipMemRangeAttributeAccessedBy}; + dataSizes[2] = {7}; + SECTION("passing dataSize as 7 for attribute hipMemRangeAttrAccessedBy") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr1, 1, Hmm, MEM_SIZE), __LINE__)) { + IfTestPassed = false; + } + } + // Passing NULL as first parameter + SECTION("Passing NULL as first parameter") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(NULL), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, MEM_SIZE), + __LINE__)) { + IfTestPassed = false; + } + } + // Passing count parameter as zero + SECTION("Passing count parameter as zero") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + AttrArr, 4, Hmm, 0), + __LINE__)) { + IfTestPassed = false; + } + } + // Passing NULL for Attribute array(3rd param) + SECTION("Passing NULL for Attribute array(3rd param)") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + NULL, 4, Hmm, MEM_SIZE), + __LINE__)) { + IfTestPassed = false; + } + } + // Passing 0 for Attribute array(3rd param) + SECTION("Passing 0 for Attribute array(3rd param)") { + if (!CheckError(hipMemRangeGetAttributes( + reinterpret_cast(Outpt), + reinterpret_cast(dataSizes), + 0, 4, Hmm, MEM_SIZE), + __LINE__)) { + IfTestPassed = false; + } + } + for (int i = 0; i < 4; ++i) { + delete Outpt[i]; + } + REQUIRE(IfTestPassed); + + // The following scenarios have been removed considering the nature of the + // api. With Consultation with Maneesh Gupta, the following scenarios + // have been removed. + // passing numAttributes as 4 while the attributes array has only 2 members + // passing numAttributes as 10 while the attributes array has only 2 members + // length of the list of dataSizes less than the number of + // attributes being probed + } else { + SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory " + "attribute. Hence skipping the testing with Pass result.\n"); + } +} +#endif From 8ae538a3418967a4ebd19122ad57cf66af574000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirza=20Halil=C4=8Devi=C4=87?= <109971222+mirza-halilcevic@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:58:57 +0100 Subject: [PATCH 09/14] EXSWHTEC-99 - Reimplement tests for hipStreamAttachMemAsync (#52) - Negative parameter tests - Validate basic behavior - Validate the behavior when pageable memory access is supported - Validate the behavior for hipMemAttachGlobal - Validate the behavior for hipMemAttachHost - Validate the behavior for hipMemAttachSingle --- catch/unit/memory/CMakeLists.txt | 2 + catch/unit/memory/hipStreamAttachMemAsync.cc | 224 ++++++++++++++++++ catch/unit/stream/CMakeLists.txt | 6 +- .../stream/hipStreamAttachMemAsync_old.cc | 143 +++++++++++ 4 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 catch/unit/memory/hipStreamAttachMemAsync.cc create mode 100644 catch/unit/stream/hipStreamAttachMemAsync_old.cc diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index 32ea04d3c1..0fbb7b7ce5 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -112,6 +112,7 @@ set(TEST_SRC hipMemsetAsync.cc hipMemAdvise.cc hipMemRangeGetAttributes.cc + hipStreamAttachMemAsync.cc hipMemRangeGetAttributes_old.cc hipMemGetAddressRange.cc ) @@ -203,6 +204,7 @@ set(TEST_SRC hipMemRangeGetAttributes.cc hipMemRangeGetAttributes_old.cc hipGetSymbolSizeAddress.cc + hipStreamAttachMemAsync.cc hipMemGetAddressRange.cc ) endif() diff --git a/catch/unit/memory/hipStreamAttachMemAsync.cc b/catch/unit/memory/hipStreamAttachMemAsync.cc new file mode 100644 index 0000000000..05a73e5cc7 --- /dev/null +++ b/catch/unit/memory/hipStreamAttachMemAsync.cc @@ -0,0 +1,224 @@ +/* +Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include + +TEST_CASE("Unit_hipStreamAttachMemAsync_Positive_Basic") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory is not supported"); + return; + } + + StreamGuard stream(Streams::created); + LinearAllocGuard managed(LinearAllocs::hipMallocManaged, kPageSize, + hipMemAttachHost); + + HIP_CHECK(hipStreamAttachMemAsync(stream.stream(), managed.ptr(), 0)); + HIP_CHECK(hipStreamSynchronize(stream.stream())); +} + +TEST_CASE("Unit_hipStreamAttachMemAsync_Positive_Pageable") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory is not supported"); + return; + } + + if (!DeviceAttributesSupport(0, hipDeviceAttributePageableMemoryAccess)) { + HipTest::HIP_SKIP_TEST("Pageable memory access is not supported"); + return; + } + + StreamGuard stream(Streams::created); + LinearAllocGuard pageable(LinearAllocs::malloc, kPageSize); + + HIP_CHECK(hipStreamAttachMemAsync(stream.stream(), pageable.ptr(), kPageSize)); + HIP_CHECK(hipStreamSynchronize(stream.stream())); +} + +// CUDA docs: +// If the cudaMemAttachGlobal flag is specified, the memory can be accessed by any stream on any +// device. +TEST_CASE("Unit_hipStreamAttachMemAsync_Positive_AttachGlobal") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory is not supported"); + return; + } + + const auto device_count = HipTest::getDeviceCount(); + const auto stream_count = device_count < 2 ? 8 : device_count; + + std::vector> streams; + streams.reserve(stream_count); + for (int i = 0; i < stream_count; ++i) { + if (device_count > 1) { + HIP_CHECK(hipSetDevice(i)); + } + streams.push_back(std::make_unique(Streams::created)); + } + + LinearAllocGuard managed_global(LinearAllocs::hipMallocManaged, sizeof(int) * stream_count, + hipMemAttachHost); + + HIP_CHECK(hipStreamAttachMemAsync( + nullptr, reinterpret_cast(managed_global.ptr()), 0, hipMemAttachGlobal)); + HIP_CHECK(hipStreamSynchronize(nullptr)); + + for (int i = 0; i < stream_count; ++i) { + HipTest::launchKernel(Set, 1, 1, 0, streams.at(i)->stream(), managed_global.ptr() + i, i); + } + + for (auto&& stream : streams) { + HIP_CHECK(hipStreamSynchronize(stream->stream())); + } + + for (int i = 0; i < stream_count; ++i) { + REQUIRE(managed_global.ptr()[i] == i); + } +} + +// CUDA docs: +// If the cudaMemAttachHost flag is specified, the program makes a guarantee that it won't access +// the memory on the device from any stream on a device that has a zero value for the device +// attribute cudaDevAttrConcurrentManagedAccess. +TEST_CASE("Unit_hipStreamAttachMemAsync_Positive_AttachHost") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory is not supported"); + return; + } + + if (DeviceAttributesSupport(0, hipDeviceAttributeConcurrentManagedAccess)) { + HipTest::HIP_SKIP_TEST("Device supports concurrent managed access"); + return; + } + + StreamGuard stream(Streams::created); + LinearAllocGuard managed_global(LinearAllocs::hipMallocManaged, sizeof(int)); + LinearAllocGuard managed_host(LinearAllocs::hipMallocManaged, sizeof(int)); + + HIP_CHECK(hipStreamAttachMemAsync( + stream.stream(), reinterpret_cast(managed_host.ptr()), 0, hipMemAttachHost)); + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + HipTest::launchKernel(Set, 1, 1, 0, stream.stream(), managed_global.ptr(), 32); + *managed_host.ptr() = 64; + HIP_CHECK(hipStreamSynchronize(stream.stream())); + + REQUIRE(*managed_global.ptr() == 32); + REQUIRE(*managed_host.ptr() == 64); +} + +// CUDA docs: +// If the cudaMemAttachSingle flag is specified and stream is associated with a device that has a +// zero value for the device attribute cudaDevAttrConcurrentManagedAccess, the program makes a +// guarantee that it will only access the memory on the device from stream. +TEST_CASE("Unit_hipStreamAttachMemAsync_Positive_AttachSingle") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory is not supported"); + return; + } + + if (DeviceAttributesSupport(0, hipDeviceAttributeConcurrentManagedAccess)) { + HipTest::HIP_SKIP_TEST("Device supports concurrent managed access"); + return; + } + + StreamGuard stream1(Streams::created); + StreamGuard stream2(Streams::created); + + LinearAllocGuard managed_global(LinearAllocs::hipMallocManaged, sizeof(int)); + LinearAllocGuard managed_single(LinearAllocs::hipMallocManaged, sizeof(int), + hipMemAttachHost); + + HIP_CHECK(hipStreamAttachMemAsync(stream1.stream(), + reinterpret_cast(managed_single.ptr()), 0, + hipMemAttachSingle)); + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + + HipTest::launchKernel(Set, 1, 1, 0, stream1.stream(), managed_single.ptr(), 64); + HIP_CHECK(hipStreamSynchronize(stream1.stream())); + + HipTest::launchKernel(Set, 1, 1, 0, stream2.stream(), managed_global.ptr(), 32); + + REQUIRE(*managed_single.ptr() == 64); + *managed_single.ptr() = 128; + + HIP_CHECK(hipStreamSynchronize(stream2.stream())); + + REQUIRE(*managed_global.ptr() == 32); + REQUIRE(*managed_single.ptr() == 128); +} + +TEST_CASE("Unit_hipStreamAttachMemAsync_Negative_Parameters") { + if (!DeviceAttributesSupport(0, hipDeviceAttributeManagedMemory)) { + HipTest::HIP_SKIP_TEST("Managed memory is not supported"); + return; + } + + StreamGuard stream(Streams::created); + LinearAllocGuard managed(LinearAllocs::hipMallocManaged, kPageSize, + hipMemAttachHost); + + SECTION("invalid stream") { + HIP_CHECK(hipStreamDestroy(stream.stream())); + HIP_CHECK_ERROR(hipStreamAttachMemAsync(stream.stream(), managed.ptr()), + hipErrorContextIsDestroyed); + } + + SECTION("dev_ptr == nullptr") { + HIP_CHECK_ERROR(hipStreamAttachMemAsync(stream.stream(), nullptr), hipErrorInvalidValue); + } + + SECTION("length is not zero nor entire allocation size") { + HIP_CHECK_ERROR(hipStreamAttachMemAsync(stream.stream(), managed.ptr(), kPageSize / 2), + hipErrorInvalidValue); + } + + SECTION("invalid flags") { + HIP_CHECK_ERROR( + hipStreamAttachMemAsync(stream.stream(), managed.ptr(), 0, + hipMemAttachGlobal | hipMemAttachHost | hipMemAttachSingle), + hipErrorInvalidValue); + } + + SECTION("attach single to nullstream") { + HIP_CHECK_ERROR(hipStreamAttachMemAsync(nullptr, managed.ptr(), 0, hipMemAttachSingle), + hipErrorInvalidValue); + } + + LinearAllocGuard pageable(LinearAllocs::malloc, kPageSize); + + if (!DeviceAttributesSupport(0, hipDeviceAttributePageableMemoryAccess)) { + SECTION("dev_ptr is pageable memory") { + HIP_CHECK_ERROR(hipStreamAttachMemAsync(stream.stream(), pageable.ptr(), kPageSize), + hipErrorInvalidValue); + } + } else { + SECTION("length is zero for pageable memory") { + HIP_CHECK_ERROR(hipStreamAttachMemAsync(stream.stream(), pageable.ptr(), 0), + hipErrorInvalidValue); + } + } +} \ No newline at end of file diff --git a/catch/unit/stream/CMakeLists.txt b/catch/unit/stream/CMakeLists.txt index 548bb2e52c..38e651e5b7 100644 --- a/catch/unit/stream/CMakeLists.txt +++ b/catch/unit/stream/CMakeLists.txt @@ -32,8 +32,8 @@ set(TEST_SRC hipStreamCreateWithPriority.cc hipStreamDestroy.cc hipAPIStreamDisable.cc - # hipStreamAttachMemAsync.cc # Disabling it on nvidia due to issue in function definition of hipStreamAttachMemAsync - # Fixing would break ABI, to be re-enabled when the fix is made. + # hipStreamAttachMemAsync_old.cc # Disabling it on nvidia due to issue in function definition of hipStreamAttachMemAsync + # Fixing would break ABI, to be re-enabled when the fix is made. streamCommon.cc hipStreamValue.cc hipStreamSynchronize.cc @@ -42,7 +42,7 @@ set(TEST_SRC hipStreamACb_StrmSyncTiming.cc ) - # set_source_files_properties(hipStreamAttachMemAsync.cc PROPERTIES COMPILE_FLAGS -std=c++17) + # set_source_files_properties(hipStreamAttachMemAsync_old.cc PROPERTIES COMPILE_FLAGS -std=c++17) endif() hip_add_exe_to_target(NAME StreamTest diff --git a/catch/unit/stream/hipStreamAttachMemAsync_old.cc b/catch/unit/stream/hipStreamAttachMemAsync_old.cc new file mode 100644 index 0000000000..e02754dfbb --- /dev/null +++ b/catch/unit/stream/hipStreamAttachMemAsync_old.cc @@ -0,0 +1,143 @@ +/* +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. +*/ + +// TODO Enable it after hipStreamAttachMemAsync is feature complete on HIP + +#include +#include + +__device__ __managed__ int var = 0; + +enum class StreamAttachTestType { NullStream = 0, StreamPerThread, CreatedStream }; + +TEST_CASE("Unit_hipStreamAttachMemAsync_Negative") { + hipStream_t stream{nullptr}; + + auto streamType = + GENERATE(StreamAttachTestType::NullStream, StreamAttachTestType::StreamPerThread, + StreamAttachTestType::CreatedStream); + + if (streamType == StreamAttachTestType::StreamPerThread) { + stream = hipStreamPerThread; + } else if (streamType == StreamAttachTestType::CreatedStream) { + HIP_CHECK(hipStreamCreate(&stream)); + REQUIRE(stream != nullptr); + } + + SECTION("Invalid Resource Handle") { + int definitelyNotAManagedVariable = 0; + HIP_CHECK_ERROR( + hipStreamAttachMemAsync(stream, reinterpret_cast(&definitelyNotAManagedVariable), + sizeof(int), hipMemAttachSingle), + hipErrorInvalidValue); + } + + SECTION("Invalid devptr") { + HIP_CHECK_ERROR(hipStreamAttachMemAsync(stream, nullptr, sizeof(int), hipMemAttachSingle), + hipErrorInvalidValue); + } + + SECTION("Invalid Resource Size") { + HIP_CHECK_ERROR(hipStreamAttachMemAsync(stream, reinterpret_cast(&var), sizeof(int) - 1, + hipMemAttachSingle), + hipErrorInvalidValue); + } + + SECTION("Invalid Flags") { + HIP_CHECK_ERROR( + hipStreamAttachMemAsync(stream, reinterpret_cast(&var), sizeof(int) - 1, + hipMemAttachSingle | hipMemAttachHost | hipMemAttachGlobal), + hipErrorInvalidValue); + } + + if (streamType == StreamAttachTestType::CreatedStream) { + HIP_CHECK(hipStreamDestroy(stream)); + } +} + +__global__ void kernel(int* ptr, size_t size) { + auto i = threadIdx.x; + if (i < size) { + ptr[i] = 1024; + } +} + +constexpr size_t size = 1024; +__device__ __managed__ int m_memory[size]; + +TEST_CASE("Unit_hipStreamAttachMemAsync_UseCase") { + hipStream_t stream{nullptr}; + + auto streamType = + GENERATE(StreamAttachTestType::NullStream, StreamAttachTestType::StreamPerThread, + StreamAttachTestType::CreatedStream); + + if (streamType == StreamAttachTestType::CreatedStream) { + HIP_CHECK(hipStreamCreate(&stream)); + REQUIRE(stream != nullptr); + } + + SECTION("Size zero is valid") { + int* d_memory{nullptr}; + HIP_CHECK(hipMallocManaged(&d_memory, sizeof(int) * size, hipMemAttachHost)); + HIP_CHECK( + hipStreamAttachMemAsync(stream, reinterpret_cast(d_memory), 0, hipMemAttachHost)); + HIP_CHECK(hipStreamSynchronize(stream)); // Wait for command to complete + HIP_CHECK(hipFree(d_memory)); + } + + SECTION("Access from device and host") { + int* d_memory{nullptr}; + + HIP_CHECK(hipMallocManaged(&d_memory, sizeof(int) * size, hipMemAttachHost)); + HIP_CHECK(hipMemset(d_memory, 0, sizeof(int) * size)); + HIP_CHECK( + hipStreamAttachMemAsync(stream, reinterpret_cast(d_memory), 0, hipMemAttachHost)); + HIP_CHECK(hipStreamSynchronize(stream)); // Wait for the command to complete + + kernel<<<1, size, 0, stream>>>(d_memory, size); + HIP_CHECK(hipStreamSynchronize(stream)); // Wait for the kernel to complete + + auto ptr = std::make_unique(size); + std::copy(d_memory, d_memory + size, ptr.get()); + + HIP_CHECK(hipFree(d_memory)); + + REQUIRE(std::all_of(ptr.get(), ptr.get() + size, [](int n) { return n == size; })); + } + + SECTION("Access ManagedMemory") { + HIP_CHECK(hipMemset(m_memory, 0, sizeof(int) * size)); + HIP_CHECK( + hipStreamAttachMemAsync(stream, reinterpret_cast(m_memory), 0, hipMemAttachHost)); + HIP_CHECK(hipStreamSynchronize(stream)); // Wait for the command to complete + + kernel<<<1, size, 0, stream>>>(m_memory, size); + HIP_CHECK(hipStreamSynchronize(stream)); // Wait for the kernel to complete + + auto ptr = std::make_unique(size); + std::copy(m_memory, m_memory + size, ptr.get()); + + REQUIRE(std::all_of(ptr.get(), ptr.get() + size, [](int n) { return n == size; })); + } + + if (streamType == StreamAttachTestType::CreatedStream) { + HIP_CHECK(hipStreamDestroy(stream)); + } +} \ No newline at end of file From 75bb521e18035086f09a9622af72924bca7efd14 Mon Sep 17 00:00:00 2001 From: Maneesh Gupta Date: Tue, 17 Jan 2023 21:54:27 +0530 Subject: [PATCH 10/14] Fix merge issues in memory unit tests (#123) --- catch/unit/memory/CMakeLists.txt | 2 - catch/unit/memory/hipMemGetAddressRange.cc | 2 +- catch/unit/memory/hipMemPrefetchAsync.cc | 4 +- catch/unit/memory/hipMemcpy.cc | 101 ------------------ .../unit/memory/hipMemcpy2DFromArrayAsync.cc | 2 + catch/unit/memory/hipMemcpy2DToArrayAsync.cc | 2 + 6 files changed, 7 insertions(+), 106 deletions(-) delete mode 100644 catch/unit/memory/hipMemcpy.cc diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index 0fbb7b7ce5..ecb2411180 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -85,7 +85,6 @@ set(TEST_SRC hipMemcpyDtoDAsync.cc hipHostMalloc.cc hipMemcpy_old.cc - hipMemcpy.cc hipMemcpy_derivatives.cc hipMemcpyAsync.cc hipMemsetFunctional.cc @@ -179,7 +178,6 @@ set(TEST_SRC hipMemcpyDtoDAsync.cc hipHostMalloc.cc hipMemcpy_old.cc - hipMemcpy.cc hipMemcpy_derivatives.cc hipMemcpyAsync.cc hipMemsetFunctional.cc diff --git a/catch/unit/memory/hipMemGetAddressRange.cc b/catch/unit/memory/hipMemGetAddressRange.cc index 7491163bda..d63433c696 100644 --- a/catch/unit/memory/hipMemGetAddressRange.cc +++ b/catch/unit/memory/hipMemGetAddressRange.cc @@ -74,7 +74,7 @@ TEST_CASE("Unit_hipMemGetAddressRange_Negative") { const int offset = kPageSize; LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, allocation_size); - hipDeviceptr_t dummy_ptr; + hipDeviceptr_t dummy_ptr = NULL; SECTION("Device pointer is invalid") { HIP_CHECK_ERROR(hipMemGetAddressRange(&base_ptr, &mem_size, dummy_ptr), hipErrorNotFound); diff --git a/catch/unit/memory/hipMemPrefetchAsync.cc b/catch/unit/memory/hipMemPrefetchAsync.cc index 8ff869d56f..6bdd027942 100644 --- a/catch/unit/memory/hipMemPrefetchAsync.cc +++ b/catch/unit/memory/hipMemPrefetchAsync.cc @@ -116,11 +116,11 @@ TEST_CASE("Unit_hipMemPrefetchAsync_Rounding_Behavior") { HIP_CHECK(hipMemRangeGetAttribute(&attribute, sizeof(attribute), hipMemRangeAttributeLastPrefetchLocation, reinterpret_cast(base), rounded_up)); - REQUIRE(device == attribute); + REQUIRE(device == static_cast(attribute)); HIP_CHECK(hipMemRangeGetAttribute(&attribute, sizeof(attribute), hipMemRangeAttributeLastPrefetchLocation, alloc.ptr(), 3 * kPageSize)); - REQUIRE((rounded_up == 3 * kPageSize ? device : hipInvalidDeviceId) == attribute); + REQUIRE((rounded_up == 3 * kPageSize ? device : hipInvalidDeviceId) == static_cast(attribute)); } TEST_CASE("Unit_hipMemPrefetchAsync_Negative_Parameters") { diff --git a/catch/unit/memory/hipMemcpy.cc b/catch/unit/memory/hipMemcpy.cc deleted file mode 100644 index 24e11c25f3..0000000000 --- a/catch/unit/memory/hipMemcpy.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include - -TEST_CASE("Unit_hipMemcpy_Positive_Basic") { MemcpyWithDirectionCommonTests(hipMemcpy); } - -TEST_CASE("Unit_hipMemcpy_Positive_Synchronization_Behavior") { - using namespace std::placeholders; - HIP_CHECK(hipDeviceSynchronize()); - - // For transfers from pageable host memory to device memory, a stream sync is performed before - // the copy is initiated. The function will return once the pageable buffer has been copied to - // the staging memory for DMA transfer to device memory, but the DMA to final destination may - // not have completed. - // For transfers from pinned host memory to device memory, the function is synchronous with - // respect to the host - SECTION("Host memory to device memory") { - MemcpyHtoDSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyHostToDevice), true); - } - - // For transfers from device to either pageable or pinned host memory, the function returns only - // once the copy has completed - SECTION("Device memory to host memory") { - const auto f = std::bind(hipMemcpy, _1, _2, _3, hipMemcpyDeviceToHost); - MemcpyDtoHPageableSyncBehavior(f, true); - MemcpyDtoHPinnedSyncBehavior(f, true); - } - - // For transfers from device memory to device memory, no host-side synchronization is performed. - SECTION("Device memory to device memory") { - // This behavior differs on NVIDIA and AMD, on AMD the hipMemcpy calls is synchronous with - // respect to the host -#if HT_AMD - HipTest::HIP_SKIP_TEST( - "EXSWCPHIPT-127 - Memcpy from device to device memory behavior differs on AMD and Nvidia"); - return; -#endif - MemcpyDtoDSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyDeviceToDevice), false); - } - - // For transfers from any host memory to any host memory, the function is fully synchronous with - // respect to the host - SECTION("Host memory to host memory") { - MemcpyHtoHSyncBehavior(std::bind(hipMemcpy, _1, _2, _3, hipMemcpyHostToHost), true); - } -} - -TEST_CASE("Unit_hipMemcpy_Negative_Parameters") { - using namespace std::placeholders; - - SECTION("Host to device") { - LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); - LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); - MemcpyWithDirectionCommonNegativeTests(hipMemcpy, device_alloc.ptr(), host_alloc.ptr(), - kPageSize, hipMemcpyHostToDevice); - } - - SECTION("Device to host") { - LinearAllocGuard device_alloc(LinearAllocs::hipMalloc, kPageSize); - LinearAllocGuard host_alloc(LinearAllocs::hipHostMalloc, kPageSize); - MemcpyWithDirectionCommonNegativeTests(hipMemcpy, host_alloc.ptr(), device_alloc.ptr(), - kPageSize, hipMemcpyDeviceToHost); - } - - SECTION("Host to host") { - LinearAllocGuard src_alloc(LinearAllocs::hipHostMalloc, kPageSize); - LinearAllocGuard dst_alloc(LinearAllocs::hipHostMalloc, kPageSize); - MemcpyWithDirectionCommonNegativeTests(hipMemcpy, dst_alloc.ptr(), src_alloc.ptr(), kPageSize, - hipMemcpyHostToHost); - } - - SECTION("Device to device") { - LinearAllocGuard src_alloc(LinearAllocs::hipMalloc, kPageSize); - LinearAllocGuard dst_alloc(LinearAllocs::hipMalloc, kPageSize); - MemcpyWithDirectionCommonNegativeTests(hipMemcpy, dst_alloc.ptr(), src_alloc.ptr(), kPageSize, - hipMemcpyDeviceToDevice); - } -} diff --git a/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc b/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc index 5dcd4085c6..35cb11a566 100644 --- a/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc +++ b/catch/unit/memory/hipMemcpy2DFromArrayAsync.cc @@ -165,10 +165,12 @@ TEST_CASE("Unit_hipMemcpy2DFromArrayAsync_Negative_Parameters") { const unsigned int flag = hipArrayDefault; +#if HT_NVIDIA constexpr auto InvalidStream = [] { StreamGuard sg(Streams::created); return sg.stream(); }; +#endif ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); LinearAllocGuard2D device_alloc(width, height); diff --git a/catch/unit/memory/hipMemcpy2DToArrayAsync.cc b/catch/unit/memory/hipMemcpy2DToArrayAsync.cc index 2cf02a3e01..7e3414e184 100644 --- a/catch/unit/memory/hipMemcpy2DToArrayAsync.cc +++ b/catch/unit/memory/hipMemcpy2DToArrayAsync.cc @@ -160,10 +160,12 @@ TEST_CASE("Unit_hipMemcpy2DToArrayAsync_Negative_Parameters") { const unsigned int flag = hipArrayDefault; +#if HT_NVIDIA constexpr auto InvalidStream = [] { StreamGuard sg(Streams::created); return sg.stream(); }; +#endif ArrayAllocGuard array_alloc(make_hipExtent(width, height, 0), flag); LinearAllocGuard2D device_alloc(width, height); From 961245e9f4893d90d9a1b3d38ac2db93b50ea798 Mon Sep 17 00:00:00 2001 From: Rahul Garg Date: Thu, 19 Jan 2023 14:09:14 -0800 Subject: [PATCH 11/14] Catch2 standalone exe (#129) * SWDEV-359379 - catch2: Standalone single exe per file -workaround for rsp file issue. -Creates single exe per file -tests detection is still during execution time and NOT compile time Change-Id: Iddfb83d57b2d767212f3d9307a276b7d572da6cd * SWDEV-359379 - Update CMakeList * Update Catch.cmake * Temporarily disable failing tests --- .jenkins/jenkinsfile | 6 +- catch/CMakeLists.txt | 8 + .../external/Catch2/cmake/Catch2/Catch.cmake | 270 ++++++++++++++---- .../Catch2/cmake/Catch2/CatchAddTests.cmake | 2 +- .../Catch2/cmake/Catch2/catch_include.cmake | 41 +++ .../cmake/Catch2/catch_include.cmake.in | 34 --- catch/multiproc/CMakeLists.txt | 12 +- ...hipMemGetInfo.cc => hipMemGetInfoMProc.cc} | 0 catch/stress/memory/CMakeLists.txt | 2 +- ...ipHostMalloc.cc => hipHostMallocStress.cc} | 0 catch/unit/device/CMakeLists.txt | 6 +- catch/unit/device/hipDeviceGetP2PAttribute.cc | 22 +- catch/unit/graph/CMakeLists.txt | 1 - catch/unit/memory/CMakeLists.txt | 11 +- catch/unit/printf/CMakeLists.txt | 8 +- catch/unit/printf/printfFlags.cc | 2 +- catch/unit/printf/printfSpecifiers.cc | 2 +- catch/unit/stream/CMakeLists.txt | 7 +- catch/unit/texture/CMakeLists.txt | 3 - 19 files changed, 306 insertions(+), 131 deletions(-) create mode 100644 catch/external/Catch2/cmake/Catch2/catch_include.cmake delete mode 100644 catch/external/Catch2/cmake/Catch2/catch_include.cmake.in rename catch/multiproc/{hipMemGetInfo.cc => hipMemGetInfoMProc.cc} (100%) rename catch/stress/memory/{hipHostMalloc.cc => hipHostMallocStress.cc} (100%) diff --git a/.jenkins/jenkinsfile b/.jenkins/jenkinsfile index 4e3ed8504f..48b654429f 100644 --- a/.jenkins/jenkinsfile +++ b/.jenkins/jenkinsfile @@ -83,7 +83,11 @@ def hipBuildTest(String backendLabel) { sh """#!/usr/bin/env bash set -x cd build - ctest + if [[ $backendLabel =~ amd ]]; then + ctest + else + ctest -E 'Unit_hipMemcpyHtoD_Positive_Synchronization_Behavior|Unit_hipFreeNegativeHost' + fi """ } } diff --git a/catch/CMakeLists.txt b/catch/CMakeLists.txt index 3f6c992059..9d5477f098 100644 --- a/catch/CMakeLists.txt +++ b/catch/CMakeLists.txt @@ -6,6 +6,9 @@ set(CMAKE_CXX_COMPILER_WORKS 1) project(hiptests) +#forcing exe per file. +set(STANDALONE_TESTS 1) +message(STATUS "STANDALONE_TESTS : ${STANDALONE_TESTS}") # Check if platform and compiler are set if(HIP_PLATFORM STREQUAL "amd") @@ -125,7 +128,12 @@ set(CATCH_BUILD_DIR catch_tests) file(COPY ./hipTestMain/config DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/${CATCH_BUILD_DIR}/hipTestMain) file(COPY ./external/Catch2/cmake/Catch2/CatchAddTests.cmake DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/${CATCH_BUILD_DIR}/script) +file(COPY ./external/Catch2/cmake/Catch2/catch_include.cmake + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/${CATCH_BUILD_DIR}/script) set(ADD_SCRIPT_PATH ${CMAKE_CURRENT_BINARY_DIR}/${CATCH_BUILD_DIR}/script/CatchAddTests.cmake) +set(CATCH_INCLUDE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${CATCH_BUILD_DIR}/script/catch_include.cmake) + + if (WIN32) configure_file(catchProp_in_rc.in ${CMAKE_CURRENT_BINARY_DIR}/catchProp.rc @ONLY) diff --git a/catch/external/Catch2/cmake/Catch2/Catch.cmake b/catch/external/Catch2/cmake/Catch2/Catch.cmake index 868ccfa739..6960d0ee3a 100644 --- a/catch/external/Catch2/cmake/Catch2/Catch.cmake +++ b/catch/external/Catch2/cmake/Catch2/Catch.cmake @@ -118,8 +118,10 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. #]=======================================================================] + #------------------------------------------------------------------------------ -function(catch_discover_tests TARGET) +# TARGET_LIST TEST_SET +function(catch_discover_tests_compile_time_detection TARGET TEST_SET) cmake_parse_arguments( "" "" @@ -140,28 +142,46 @@ function(catch_discover_tests TARGET) string(SUBSTRING ${args_hash} 0 7 args_hash) # Define rule to generate test list for aforementioned test executable - set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") - set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") + set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TEST_SET}_include-${args_hash}.cmake") + set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TEST_SET}_tests-${args_hash}.cmake") + + foreach(EXE_NAME ${TARGET}) + + add_custom_command( + TARGET ${EXE_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" + -D "TEST_TARGET=${EXE_NAME}" + -D "TEST_EXECUTABLE=$" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" + -D "TEST_SPEC=${_TEST_SPEC}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX=${_TEST_PREFIX}" + -D "TEST_SUFFIX=${_TEST_SUFFIX}" + -D "TEST_LIST=${_TEST_LIST}" + -D "TEST_REPORTER=${_REPORTER}" + -D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}" + -D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}" + -D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}" + -D "CTEST_FILE=${ctest_tests_file}" + -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" + VERBATIM + ) + endforeach() + file(RELATIVE_PATH ctestincludepath ${CMAKE_CURRENT_BINARY_DIR} ${ctest_include_file}) file(RELATIVE_PATH ctestfilepath ${CMAKE_CURRENT_BINARY_DIR} ${ctest_tests_file}) - file(RELATIVE_PATH _workdir ${CMAKE_CURRENT_BINARY_DIR} ${_WORKING_DIRECTORY}) - file(RELATIVE_PATH _CATCH_ADD_TEST_SCRIPT ${CMAKE_CURRENT_BINARY_DIR} ${ADD_SCRIPT_PATH}) - get_property(crosscompiling_emulator - TARGET ${TARGET} - PROPERTY CROSSCOMPILING_EMULATOR + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctestfilepath}\")\n" + " include(\"${ctestfilepath}\")\n" + "else()\n" + " message(WARNING \"Test ${TARGET} not built yet.\")\n" + "endif()\n" ) - set(EXEC_NAME ${TARGET}) - if(WIN32) - set(EXEC_NAME ${EXEC_NAME}.exe) - endif() - - # uses catch_include.cmake.in file to generate the *_include.cmake file - # *_include.cmake is used to generate the *_test.cmake during execution of ctest cmd - configure_file(${CATCH2_INCLUDE} ${TARGET}_include-${args_hash}.cmake @ONLY) - - if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") # Add discovered tests to directory TEST_INCLUDE_FILES set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${ctestincludepath}" @@ -184,17 +204,61 @@ endfunction() ############################################################################### + + + +#------------------------------------------------------------------------------ +# current staging +function(catch_discover_tests TARGET_LIST TEST_SET) + cmake_parse_arguments( + "" + "" + "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX" + "TEST_SPEC;EXTRA_ARGS;PROPERTIES" + ${ARGN} + ) + ## Generate a unique name based on the extra arguments + string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}") + string(SUBSTRING ${args_hash} 0 7 args_hash) + # Define rule to generate test list for aforementioned test executable + set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TEST_SET}_include-${args_hash}.cmake") + set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TEST_SET}_tests-${args_hash}.cmake") + file(RELATIVE_PATH ctestincludepath ${CMAKE_CURRENT_BINARY_DIR} ${ctest_include_file}) + file(RELATIVE_PATH ctestfilepath ${CMAKE_CURRENT_BINARY_DIR} ${ctest_tests_file}) + file(RELATIVE_PATH _CATCH_ADD_TEST_SCRIPT ${CMAKE_CURRENT_BINARY_DIR} ${ADD_SCRIPT_PATH}) + file(RELATIVE_PATH CATCH_INCLUDE_PATH ${CMAKE_CURRENT_BINARY_DIR} ${CATCH_INCLUDE_PATH}) + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") + file(WRITE ${ctest_include_file} "set(exc_names ${TARGET_LIST})\n") + file(APPEND ${ctest_include_file} "set(TARGET ${TEST_SET})\n") + file(APPEND ${ctest_include_file} "set(_TEST_LIST ${TEST_SET}_TESTS)\n") + file(APPEND ${ctest_include_file} "set(ctestfilepath ${ctestfilepath})\n") + file(APPEND ${ctest_include_file} "set(_CATCH_ADD_TEST_SCRIPT ${_CATCH_ADD_TEST_SCRIPT})\n") + file(APPEND ${ctest_include_file} "set(crosscompiling_emulator ${crosscompiling_emulator})\n") + file(APPEND ${ctest_include_file} "set(_PROPERTIES ${_PROPERTIES})\n") + file(APPEND ${ctest_include_file} "include(${CATCH_INCLUDE_PATH})\n") + # Add discovered tests to directory TEST_INCLUDE_FILES + set_property(DIRECTORY + APPEND PROPERTY TEST_INCLUDE_FILES "${ctestincludepath}" + ) + endif() + +endfunction() + +############################################################################### + set(_CATCH_DISCOVER_TESTS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file" ) + ############################################################################### # function to be called by all tests -function(hip_add_exe_to_target) +function(hip_add_exe_to_target_compile_time_detection) set(options) + # NAME EventTest, TEST_SRC src, TEST_TARGET_NAME build_tests set(args NAME TEST_TARGET_NAME PLATFORM COMPILE_OPTIONS) - set(list_args TEST_SRC LINKER_LIBS PROPERTY) + set(list_args TEST_SRC LINKER_LIBS COMMON_SHARED_SRC PROPERTY) cmake_parse_arguments( PARSE_ARGV 0 "" # variable prefix @@ -202,48 +266,148 @@ function(hip_add_exe_to_target) "${args}" "${list_args}" ) - # Create shared lib of all tests - if(NOT RTC_TESTING) - add_executable(${_NAME} EXCLUDE_FROM_ALL ${_TEST_SRC} $ $) - else () - add_executable(${_NAME} EXCLUDE_FROM_ALL ${_TEST_SRC} $) - if(HIP_PLATFORM STREQUAL "amd") - target_link_libraries(${_NAME} hiprtc) + + foreach(SRC_NAME ${TEST_SRC}) + if(NOT STANDALONE_TESTS EQUAL "1") + set(_EXE_NAME ${_NAME}) + # take the entire source set for building the executable + set(SRC_NAME ${TEST_SRC}) else() - target_link_libraries(${_NAME} nvrtc) + # strip extension of src and use exe name as src name + get_filename_component(_EXE_NAME ${SRC_NAME} NAME_WLE) endif() - endif() - catch_discover_tests(${_NAME} PROPERTIES SKIP_REGULAR_EXPRESSION "HIP_SKIP_THIS_TEST") - if(UNIX) - set(_LINKER_LIBS ${_LINKER_LIBS} stdc++fs) - set(_LINKER_LIBS ${_LINKER_LIBS} -ldl) - else() - # res files are built resource files using rc files. - # use llvm-rc exe to build the res files - # Thes are used to populate the properties of the built executables - if(EXISTS "${PROP_RC}/catchProp.res") - set(_LINKER_LIBS ${_LINKER_LIBS} "${PROP_RC}/catchProp.res") + + if(NOT RTC_TESTING) + add_executable(${_EXE_NAME} EXCLUDE_FROM_ALL ${SRC_NAME} ${COMMON_SHARED_SRC} $ $) + else () + add_executable(${_EXE_NAME} EXCLUDE_FROM_ALL ${SRC_NAME} ${COMMON_SHARED_SRC} $) + if(HIP_PLATFORM STREQUAL "amd") + target_link_libraries(${_EXE_NAME} hiprtc) + else() + target_link_libraries(${_EXE_NAME} nvrtc) + endif() endif() - endif() - if(DEFINED _LINKER_LIBS) - target_link_libraries(${_NAME} ${_LINKER_LIBS}) - endif() - # Add dependency on build_tests to build it on this custom target - add_dependencies(${_TEST_TARGET_NAME} ${_NAME}) - if (DEFINED _PROPERTY) - set_property(TARGET ${_NAME} PROPERTY ${_PROPERTY}) - endif() + if(UNIX) + set(_LINKER_LIBS ${_LINKER_LIBS} stdc++fs) + set(_LINKER_LIBS ${_LINKER_LIBS} -ldl) + else() + # res files are built resource files using rc files. + # use llvm-rc exe to build the res files + # Thes are used to populate the properties of the built executables + if(EXISTS "${PROP_RC}/catchProp.res") + set(_LINKER_LIBS ${_LINKER_LIBS} "${PROP_RC}/catchProp.res") + endif() + #set(_LINKER_LIBS ${_LINKER_LIBS} -noAutoResponse) + endif() - if (DEFINED _COMPILE_OPTIONS) - target_compile_options(${_NAME} PUBLIC ${_COMPILE_OPTIONS}) - endif() + if(DEFINED _LINKER_LIBS) + target_link_libraries(${_EXE_NAME} ${_LINKER_LIBS}) + endif() - foreach(arg IN LISTS _UNPARSED_ARGUMENTS) - message(WARNING "Unparsed arguments: ${arg}") + # Add dependency on build_tests to build it on this custom target + add_dependencies(${_TEST_TARGET_NAME} ${_EXE_NAME}) + # add_dependencies(${_TEST_TARGET_NAME} ${_EXE_NAME}) + + if (DEFINED _PROPERTY) + set_property(TARGET ${_EXE_NAME} PROPERTY ${_PROPERTY}) + endif() + + if (DEFINED _COMPILE_OPTIONS) + target_compile_options(${_EXE_NAME} PUBLIC ${_COMPILE_OPTIONS}) + endif() + foreach(arg IN LISTS _UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments: ${arg}") + endforeach() + get_property(crosscompiling_emulator + TARGET ${_EXE_NAME} + PROPERTY CROSSCOMPILING_EMULATOR + ) + set(_EXE_NAME_LIST ${_EXE_NAME_LIST} ${_EXE_NAME}) + if(NOT STANDALONE_TESTS EQUAL "1") + break() + endif() endforeach() + catch_discover_tests("${_EXE_NAME_LIST}" "${_NAME}" PROPERTIES SKIP_REGULAR_EXPRESSION "HIP_SKIP_THIS_TEST") endfunction() +############################################################################### +# current staging +# function to be called by all tests +function(hip_add_exe_to_target) + set(options) + set(args NAME TEST_TARGET_NAME PLATFORM COMPILE_OPTIONS) + set(list_args TEST_SRC LINKER_LIBS COMMON_SHARED_SRC PROPERTY) + cmake_parse_arguments( + PARSE_ARGV 0 + "" # variable prefix + "${options}" + "${args}" + "${list_args}" + ) + foreach(SRC_NAME ${TEST_SRC}) + + if(NOT STANDALONE_TESTS EQUAL "1") + set(_EXE_NAME ${_NAME}) + set(SRC_NAME ${TEST_SRC}) + else() + # strip extension of src and use exe name as src name + get_filename_component(_EXE_NAME ${SRC_NAME} NAME_WLE) + endif() + + # Create shared lib of all tests + if(NOT RTC_TESTING) + add_executable(${_EXE_NAME} EXCLUDE_FROM_ALL ${SRC_NAME} ${COMMON_SHARED_SRC} $ $) + else () + add_executable(${_EXE_NAME} EXCLUDE_FROM_ALL ${SRC_NAME} ${COMMON_SHARED_SRC} $) + if(HIP_PLATFORM STREQUAL "amd") + target_link_libraries(${_EXE_NAME} hiprtc) + else() + target_link_libraries(${_EXE_NAME} nvrtc) + endif() + endif() + if (DEFINED _PROPERTY) + set_property(TARGET ${_EXE_NAME} PROPERTY ${_PROPERTY}) + endif() + if(UNIX) + set(_LINKER_LIBS ${_LINKER_LIBS} stdc++fs) + set(_LINKER_LIBS ${_LINKER_LIBS} -ldl) + else() + # res files are built resource files using rc files. + # use llvm-rc exe to build the res files + # Thes are used to populate the properties of the built executables + if(EXISTS "${PROP_RC}/catchProp.res") + set(_LINKER_LIBS ${_LINKER_LIBS} "${PROP_RC}/catchProp.res") + endif() + endif() + + if(DEFINED _LINKER_LIBS) + target_link_libraries(${_EXE_NAME} ${_LINKER_LIBS}) + endif() + + # Add dependency on build_tests to build it on this custom target + add_dependencies(${_TEST_TARGET_NAME} ${_EXE_NAME}) + + if (DEFINED _COMPILE_OPTIONS) + target_compile_options(${_EXE_NAME} PUBLIC ${_COMPILE_OPTIONS}) + endif() + + foreach(arg IN LISTS _UNPARSED_ARGUMENTS) + message(WARNING "Unparsed arguments: ${arg}") + endforeach() + get_property(crosscompiling_emulator + TARGET ${_EXE_NAME} + PROPERTY CROSSCOMPILING_EMULATOR + ) + set(_EXE_NAME_LIST ${_EXE_NAME_LIST} ${_EXE_NAME}) + if(NOT STANDALONE_TESTS EQUAL "1") + break() + endif() + + endforeach() + + catch_discover_tests("${_EXE_NAME_LIST}" "${_NAME}" PROPERTIES SKIP_REGULAR_EXPRESSION "HIP_SKIP_THIS_TEST") +endfunction() diff --git a/catch/external/Catch2/cmake/Catch2/CatchAddTests.cmake b/catch/external/Catch2/cmake/Catch2/CatchAddTests.cmake index 97ccac4eff..2b13187a7d 100644 --- a/catch/external/Catch2/cmake/Catch2/CatchAddTests.cmake +++ b/catch/external/Catch2/cmake/Catch2/CatchAddTests.cmake @@ -135,4 +135,4 @@ endforeach() add_command(set ${TEST_LIST} ${tests}) # Write CTest script -file(WRITE "${CTEST_FILE}" "${script}") +file(APPEND "${CTEST_FILE}" "${script}") diff --git a/catch/external/Catch2/cmake/Catch2/catch_include.cmake b/catch/external/Catch2/cmake/Catch2/catch_include.cmake new file mode 100644 index 0000000000..b698e8b96c --- /dev/null +++ b/catch/external/Catch2/cmake/Catch2/catch_include.cmake @@ -0,0 +1,41 @@ +# when ctest is ran, each submodule includes this file to generate the _tests.cmake file. +# _tests.cmake contains the add_test macro which runs the individual test. + +get_filename_component(_cmake_path cmake ABSOLUTE) + +foreach(EXEC_NAME ${exc_names}) + if(WIN32) + set(EXEC_NAME ${EXEC_NAME}.exe) + endif() + if(EXISTS "${EXEC_NAME}") + execute_process( + COMMAND "${_cmake_path}" + -D "TEST_TARGET=${TARGET}" + -D "TEST_EXECUTABLE=${EXEC_NAME}" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_workdir}" + -D "TEST_SPEC=${_TEST_SPEC}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX=${_TEST_PREFIX}" + -D "TEST_SUFFIX=${_TEST_SUFFIX}" + -D "TEST_LIST=${_TEST_LIST}" + -D "TEST_REPORTER=${_REPORTER}" + -D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}" + -D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}" + -D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}" + -D "CTEST_FILE=${ctestfilepath}" + -P "${_CATCH_ADD_TEST_SCRIPT}" + OUTPUT_VARIABLE output + RESULT_VARIABLE result + WORKING_DIRECTORY "${TEST_WORKING_DIR}" + ) + else() + message("executable not found : ${EXEC_NAME}" ) + endif() +endforeach() + +if(EXISTS "${ctestfilepath}") +# include the generated ctest file for execution +include(${ctestfilepath}) +endif() diff --git a/catch/external/Catch2/cmake/Catch2/catch_include.cmake.in b/catch/external/Catch2/cmake/Catch2/catch_include.cmake.in deleted file mode 100644 index c318ebc5df..0000000000 --- a/catch/external/Catch2/cmake/Catch2/catch_include.cmake.in +++ /dev/null @@ -1,34 +0,0 @@ -# File @ctestincludepath@ is generated by cmake. -# For changes please modify hip/tests/catch/external/Catch2/cmake/Catch2/catch_include.cmake.in - -get_filename_component(_cmake_path cmake ABSOLUTE) - -if(EXISTS "@EXEC_NAME@") - execute_process( - COMMAND "${_cmake_path}" - -D "TEST_TARGET=@TARGET@" - -D "TEST_EXECUTABLE=@EXEC_NAME@" - -D "TEST_EXECUTOR=@crosscompiling_emulator@" - -D "TEST_WORKING_DIR=@_workdir@" - -D "TEST_SPEC=@_TEST_SPEC@" - -D "TEST_EXTRA_ARGS=@_EXTRA_ARGS@" - -D "TEST_PROPERTIES=@_PROPERTIES@" - -D "TEST_PREFIX=@_TEST_PREFIX@" - -D "TEST_SUFFIX=@_TEST_SUFFIX@" - -D "TEST_LIST=@_TEST_LIST@" - -D "TEST_REPORTER=@_REPORTER@" - -D "TEST_OUTPUT_DIR=@_OUTPUT_DIR@" - -D "TEST_OUTPUT_PREFIX=@_OUTPUT_PREFIX@" - -D "TEST_OUTPUT_SUFFIX=@_OUTPUT_SUFFIX@" - -D "CTEST_FILE=@ctestfilepath@" - -P "@_CATCH_ADD_TEST_SCRIPT@" - OUTPUT_VARIABLE output - RESULT_VARIABLE result - WORKING_DIRECTORY "@TEST_WORKING_DIR@" - ) - # include the generated ctest file for execution - include(@ctestfilepath@) -else() - message(STATUS "executable not built : @EXEC_NAME@" ) -endif() - diff --git a/catch/multiproc/CMakeLists.txt b/catch/multiproc/CMakeLists.txt index 5485ee9ca5..7a83b7d058 100644 --- a/catch/multiproc/CMakeLists.txt +++ b/catch/multiproc/CMakeLists.txt @@ -1,5 +1,5 @@ # Common Tests -set(LINUX_TEST_SRC +set(TEST_SRC childMalloc.cc hipDeviceComputeCapabilityMproc.cc hipDeviceGetPCIBusIdMproc.cc @@ -12,10 +12,9 @@ set(LINUX_TEST_SRC hipMallocConcurrencyMproc.cc hipMemCoherencyTstMProc.cc hipIpcEventHandle.cc - hipIpcMemAccessTest.cc deviceAllocationMproc.cc hipNoGpuTsts.cc - hipMemGetInfo.cc + hipMemGetInfoMProc.cc ) add_custom_target(dummy_kernel.code COMMAND ${CMAKE_CXX_COMPILER} --genco ${CMAKE_CURRENT_SOURCE_DIR}/dummy_kernel.cpp -o ${CMAKE_CURRENT_BINARY_DIR}/../multiproc/dummy_kernel.code -I${CMAKE_CURRENT_SOURCE_DIR}/../../../../include/ -I${CMAKE_CURRENT_SOURCE_DIR}/../../include) @@ -23,14 +22,13 @@ add_custom_target(dummy_kernel.code COMMAND ${CMAKE_CXX_COMPILER} --genco ${CMAK # the last argument linker libraries is required for this test but optional to the function if(HIP_PLATFORM MATCHES "nvidia") hip_add_exe_to_target(NAME MultiProc - TEST_SRC ${LINUX_TEST_SRC} + TEST_SRC ${TEST_SRC} TEST_TARGET_NAME build_tests LINKER_LIBS nvrtc) elseif(HIP_PLATFORM MATCHES "amd") hip_add_exe_to_target(NAME MultiProc - TEST_SRC ${LINUX_TEST_SRC} - TEST_TARGET_NAME build_tests - LINKER_LIBS ${CMAKE_DL_LIBS}) + TEST_SRC ${TEST_SRC} + TEST_TARGET_NAME build_tests) endif() add_dependencies(build_tests dummy_kernel.code) diff --git a/catch/multiproc/hipMemGetInfo.cc b/catch/multiproc/hipMemGetInfoMProc.cc similarity index 100% rename from catch/multiproc/hipMemGetInfo.cc rename to catch/multiproc/hipMemGetInfoMProc.cc diff --git a/catch/stress/memory/CMakeLists.txt b/catch/stress/memory/CMakeLists.txt index e55869f8e3..bf172bd642 100644 --- a/catch/stress/memory/CMakeLists.txt +++ b/catch/stress/memory/CMakeLists.txt @@ -4,7 +4,7 @@ set(TEST_SRC hipMemcpyMThreadMSize.cc hipMallocManagedStress.cc hipMemPrftchAsyncStressTst.cc - hipHostMalloc.cc + hipHostMallocStress.cc ) hip_add_exe_to_target(NAME memory diff --git a/catch/stress/memory/hipHostMalloc.cc b/catch/stress/memory/hipHostMallocStress.cc similarity index 100% rename from catch/stress/memory/hipHostMalloc.cc rename to catch/stress/memory/hipHostMallocStress.cc diff --git a/catch/unit/device/CMakeLists.txt b/catch/unit/device/CMakeLists.txt index 1e26944d0f..2cb03d0575 100644 --- a/catch/unit/device/CMakeLists.txt +++ b/catch/unit/device/CMakeLists.txt @@ -41,12 +41,12 @@ set_source_files_properties(hipGetDeviceCount.cc PROPERTIES COMPILE_FLAGS -std=c set_source_files_properties(hipDeviceGetP2PAttribute.cc PROPERTIES COMPILE_FLAGS -std=c++17) add_executable(getDeviceCount EXCLUDE_FROM_ALL getDeviceCount_exe.cc) -add_executable(hipDeviceGetP2PAttribute EXCLUDE_FROM_ALL hipDeviceGetP2PAttribute_exe.cc) +add_executable(hipDeviceGetP2PAttribute_exe EXCLUDE_FROM_ALL hipDeviceGetP2PAttribute_exe.cc) hip_add_exe_to_target(NAME DeviceTest TEST_SRC ${TEST_SRC} TEST_TARGET_NAME build_tests COMPILE_OPTIONS -std=c++14) -add_dependencies(DeviceTest getDeviceCount) -add_dependencies(DeviceTest hipDeviceGetP2PAttribute) +add_dependencies(build_tests getDeviceCount) +add_dependencies(build_tests hipDeviceGetP2PAttribute_exe) diff --git a/catch/unit/device/hipDeviceGetP2PAttribute.cc b/catch/unit/device/hipDeviceGetP2PAttribute.cc index 584257e616..e126856d1d 100644 --- a/catch/unit/device/hipDeviceGetP2PAttribute.cc +++ b/catch/unit/device/hipDeviceGetP2PAttribute.cc @@ -122,20 +122,20 @@ TEST_CASE("Unit_hipDeviceGetP2PAttribute_Negative") { /* https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#env-vars */ SECTION("Hidden devices using environment variables") { - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("") == hipSuccess); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("0") == hipErrorInvalidDevice); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("1") == hipErrorInvalidDevice); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("0,1") == hipSuccess); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("-1,0") == hipErrorNoDevice); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("0,-1") == hipErrorInvalidDevice); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("0,1,-1") == hipSuccess); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("0,-1,1") == hipErrorInvalidDevice); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("") == hipSuccess); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("0") == hipErrorInvalidDevice); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("1") == hipErrorInvalidDevice); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("0,1") == hipSuccess); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("-1,0") == hipErrorNoDevice); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("0,-1") == hipErrorInvalidDevice); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("0,1,-1") == hipSuccess); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("0,-1,1") == hipErrorInvalidDevice); if (deviceCount > 2) { - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("2,1") == hipSuccess); - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("2") == hipErrorInvalidDevice); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("2,1") == hipSuccess); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("2") == hipErrorInvalidDevice); } else { - REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute").run("2,1") == hipErrorNoDevice); + REQUIRE(hip::SpawnProc("hipDeviceGetP2PAttribute_exe").run("2,1") == hipErrorNoDevice); } } #endif diff --git a/catch/unit/graph/CMakeLists.txt b/catch/unit/graph/CMakeLists.txt index c9d1bb34ea..dcb9b9ac5d 100644 --- a/catch/unit/graph/CMakeLists.txt +++ b/catch/unit/graph/CMakeLists.txt @@ -59,7 +59,6 @@ set(TEST_SRC hipGraphEventWaitNodeGetEvent.cc hipGraphExecMemcpyNodeSetParams.cc hipStreamBeginCapture.cc - hipGraphAddMemcpyNode1D.cc hipStreamIsCapturing.cc hipStreamGetCaptureInfo.cc hipStreamEndCapture.cc diff --git a/catch/unit/memory/CMakeLists.txt b/catch/unit/memory/CMakeLists.txt index ecb2411180..876dae382b 100644 --- a/catch/unit/memory/CMakeLists.txt +++ b/catch/unit/memory/CMakeLists.txt @@ -18,10 +18,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +set(COMMON_SHARED_SRC DriverContext.cc) + # Common Tests - Test independent of all platforms if(HIP_PLATFORM MATCHES "amd") set(TEST_SRC - DriverContext.cc memset.cc malloc.cc hipMemcpy2DToArray.cc @@ -46,7 +47,6 @@ set(TEST_SRC hipMemcpy_MultiThread.cc hipHostRegister.cc hipHostUnregister.cc - hipMallocPitch.cc hipMemPtrGetInfo.cc hipPointerGetAttributes.cc hipHostGetFlags.cc @@ -79,7 +79,6 @@ set(TEST_SRC hipMemcpyWithStreamMultiThread.cc hipMemsetAsyncAndKernel.cc hipMemset2DAsyncMultiThreadAndKernel.cc - hipMallocManaged.cc hipMallocConcurrency.cc hipMemcpyDtoD.cc hipMemcpyDtoDAsync.cc @@ -117,7 +116,6 @@ set(TEST_SRC ) else() set(TEST_SRC - DriverContext.cc memset.cc malloc.cc hipMemcpy2DToArray.cc @@ -142,7 +140,6 @@ set(TEST_SRC hipMemcpy_MultiThread.cc hipHostRegister.cc hipHostUnregister.cc - hipMallocPitch.cc hipHostGetFlags.cc hipHostGetDevicePointer.cc hipMallocManaged_MultiScenario.cc @@ -172,7 +169,6 @@ set(TEST_SRC hipMemcpyWithStreamMultiThread.cc hipMemsetAsyncAndKernel.cc hipMemset2DAsyncMultiThreadAndKernel.cc - hipMallocManaged.cc hipMallocConcurrency.cc hipMemcpyDtoD.cc hipMemcpyDtoDAsync.cc @@ -217,4 +213,5 @@ endif() hip_add_exe_to_target(NAME MemoryTest TEST_SRC ${TEST_SRC} - TEST_TARGET_NAME build_tests) + TEST_TARGET_NAME build_tests + COMMON_SHARED_SRC ${COMMON_SHARED_SRC}) diff --git a/catch/unit/printf/CMakeLists.txt b/catch/unit/printf/CMakeLists.txt index d2a9d6eced..74d4421a77 100644 --- a/catch/unit/printf/CMakeLists.txt +++ b/catch/unit/printf/CMakeLists.txt @@ -18,8 +18,8 @@ elseif (HIP_PLATFORM MATCHES "nvidia") endif() # Standalone exes -add_executable(printfFlags EXCLUDE_FROM_ALL printfFlags_exe.cc) -add_executable(printfSpecifiers EXCLUDE_FROM_ALL printfSpecifiers_exe.cc) +add_executable(printfFlags_exe EXCLUDE_FROM_ALL printfFlags_exe.cc) +add_executable(printfSpecifiers_exe EXCLUDE_FROM_ALL printfSpecifiers_exe.cc) -add_dependencies(printfTests printfFlags) -add_dependencies(printfTests printfSpecifiers) +add_dependencies(build_tests printfFlags_exe) +add_dependencies(build_tests printfSpecifiers_exe) diff --git a/catch/unit/printf/printfFlags.cc b/catch/unit/printf/printfFlags.cc index b9955b0b60..8fa5181ba2 100644 --- a/catch/unit/printf/printfFlags.cc +++ b/catch/unit/printf/printfFlags.cc @@ -37,7 +37,7 @@ xyzzy 00000042 )here"); - hip::SpawnProc proc("printfFlags", true); + hip::SpawnProc proc("printfFlags_exe", true); REQUIRE(proc.run() == 0); REQUIRE(proc.getOutput() == reference); } diff --git a/catch/unit/printf/printfSpecifiers.cc b/catch/unit/printf/printfSpecifiers.cc index 223a3c338b..0f366f8c58 100644 --- a/catch/unit/printf/printfSpecifiers.cc +++ b/catch/unit/printf/printfSpecifiers.cc @@ -89,7 +89,7 @@ x )here"); #endif - hip::SpawnProc proc("printfSpecifiers", true); + hip::SpawnProc proc("printfSpecifiers_exe", true); REQUIRE(0 == proc.run()); REQUIRE(proc.getOutput() == reference); } diff --git a/catch/unit/stream/CMakeLists.txt b/catch/unit/stream/CMakeLists.txt index 38e651e5b7..74024fce2a 100644 --- a/catch/unit/stream/CMakeLists.txt +++ b/catch/unit/stream/CMakeLists.txt @@ -1,3 +1,5 @@ +set(COMMON_SHARED_SRC streamCommon.cc) + if(HIP_PLATFORM MATCHES "amd") set(TEST_SRC hipStreamCreate.cc @@ -10,7 +12,6 @@ set(TEST_SRC hipStreamDestroy.cc hipStreamGetCUMask.cc hipAPIStreamDisable.cc - streamCommon.cc hipStreamValue.cc hipStreamWithCUMask.cc hipStreamACb_MultiThread.cc @@ -34,7 +35,6 @@ set(TEST_SRC hipAPIStreamDisable.cc # hipStreamAttachMemAsync_old.cc # Disabling it on nvidia due to issue in function definition of hipStreamAttachMemAsync # Fixing would break ABI, to be re-enabled when the fix is made. - streamCommon.cc hipStreamValue.cc hipStreamSynchronize.cc hipStreamQuery.cc @@ -48,4 +48,5 @@ endif() hip_add_exe_to_target(NAME StreamTest TEST_SRC ${TEST_SRC} TEST_TARGET_NAME build_tests - COMPILE_OPTIONS -std=c++17) + COMPILE_OPTIONS -std=c++17 + COMMON_SHARED_SRC ${COMMON_SHARED_SRC}) diff --git a/catch/unit/texture/CMakeLists.txt b/catch/unit/texture/CMakeLists.txt index c05db818e6..93a188683c 100644 --- a/catch/unit/texture/CMakeLists.txt +++ b/catch/unit/texture/CMakeLists.txt @@ -37,9 +37,6 @@ set(TEST_SRC hipGetChanDesc.cc hipTexObjPitch.cc hipTextureObj1DFetch.cc - hipBindTex2DPitch.cc - hipBindTexRef1DFetch.cc - hipTex1DFetchCheckModes.cc hipTextureObj1DCheckModes.cc hipTextureObj2DCheckModes.cc hipTextureObj3DCheckModes.cc From 888983f1d727d0d26252fc34dd113516cb9de1af Mon Sep 17 00:00:00 2001 From: Rahul Garg Date: Fri, 20 Jan 2023 11:12:00 -0800 Subject: [PATCH 12/14] Temporarily disable failing tests (#131) Disable testing Unit_hipMemcpy_Positive_Synchronization_Behavior on NV. --- .jenkins/jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins/jenkinsfile b/.jenkins/jenkinsfile index 48b654429f..ae4984e5a6 100644 --- a/.jenkins/jenkinsfile +++ b/.jenkins/jenkinsfile @@ -86,7 +86,7 @@ def hipBuildTest(String backendLabel) { if [[ $backendLabel =~ amd ]]; then ctest else - ctest -E 'Unit_hipMemcpyHtoD_Positive_Synchronization_Behavior|Unit_hipFreeNegativeHost' + ctest -E 'Unit_hipMemcpyHtoD_Positive_Synchronization_Behavior|Unit_hipMemcpy_Positive_Synchronization_Behavior|Unit_hipFreeNegativeHost' fi """ } From 500dc6f69a96cc140ad6fc5b683a80573fa57359 Mon Sep 17 00:00:00 2001 From: Siu Chi Chan Date: Fri, 20 Jan 2023 20:22:13 +0000 Subject: [PATCH 13/14] Link tests to required libraries (#124) A lot of the unit tests make use of the C++ thread library but the cmakefile doesn't explicitly link them to the pthread library but instead rely on hipcc to implicitly link in the pthread library. Some tests that rely on librt have a similar issue. The tests break when we are cleaning up hipcc by removing the implcit linking to those libraries. --- catch/external/Catch2/cmake/Catch2/Catch.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/catch/external/Catch2/cmake/Catch2/Catch.cmake b/catch/external/Catch2/cmake/Catch2/Catch.cmake index 6960d0ee3a..0a76641fe5 100644 --- a/catch/external/Catch2/cmake/Catch2/Catch.cmake +++ b/catch/external/Catch2/cmake/Catch2/Catch.cmake @@ -374,6 +374,8 @@ function(hip_add_exe_to_target) if(UNIX) set(_LINKER_LIBS ${_LINKER_LIBS} stdc++fs) set(_LINKER_LIBS ${_LINKER_LIBS} -ldl) + set(_LINKER_LIBS ${_LINKER_LIBS} pthread) + set(_LINKER_LIBS ${_LINKER_LIBS} rt) else() # res files are built resource files using rc files. # use llvm-rc exe to build the res files From 315b14fe4ee25fa090dd7310155b5d6ba808da30 Mon Sep 17 00:00:00 2001 From: Maneesh Gupta Date: Mon, 23 Jan 2023 10:47:56 +0530 Subject: [PATCH 14/14] SWDEV-1 - Disable newly added failing tests (#132) Disabled the following tests on Linux: - Unit_hipMemRangeGetAttribute_Positive_AccessedBy_Basic - Unit_hipMemRangeGetAttribute_Positive_AccessedBy_Partial_Range - Unit_hipMemRangeGetAttributes_Negative_Parameters - Unit_hipStreamAttachMemAsync_Positive_Basic - Unit_hipStreamAttachMemAsync_Positive_AttachGlobal - Unit_hipStreamAttachMemAsync_Negative_Parameters - Unit_hipMemGetAddressRange_Positive Disabled the following tests on Windows: - Unit_hipMemGetAddressRange_Positive --- catch/hipTestMain/config/config_amd_linux_common.json | 9 ++++++++- catch/hipTestMain/config/config_amd_windows_common.json | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/catch/hipTestMain/config/config_amd_linux_common.json b/catch/hipTestMain/config/config_amd_linux_common.json index 864c6aa5c4..a9f9aa37e3 100644 --- a/catch/hipTestMain/config/config_amd_linux_common.json +++ b/catch/hipTestMain/config/config_amd_linux_common.json @@ -18,6 +18,13 @@ "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ClonedGrph", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ChldNode", - "Unit_hipMemGetAddressRange_Negative" + "Unit_hipMemGetAddressRange_Negative", + "Unit_hipMemRangeGetAttribute_Positive_AccessedBy_Basic", + "Unit_hipMemRangeGetAttribute_Positive_AccessedBy_Partial_Range", + "Unit_hipMemRangeGetAttributes_Negative_Parameters", + "Unit_hipStreamAttachMemAsync_Positive_Basic", + "Unit_hipStreamAttachMemAsync_Positive_AttachGlobal", + "Unit_hipStreamAttachMemAsync_Negative_Parameters", + "Unit_hipMemGetAddressRange_Positive" ] } diff --git a/catch/hipTestMain/config/config_amd_windows_common.json b/catch/hipTestMain/config/config_amd_windows_common.json index 5586728b6f..6bb11e842c 100644 --- a/catch/hipTestMain/config/config_amd_windows_common.json +++ b/catch/hipTestMain/config/config_amd_windows_common.json @@ -97,6 +97,7 @@ "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ClonedGrph", "Unit_hipGraphDestroyNode_Complx_ChkNumOfNodesNDep_ChldNode", - "Unit_hipMemGetAddressRange_Negative" + "Unit_hipMemGetAddressRange_Negative", + "Unit_hipMemGetAddressRange_Positive" ] }