SWDEV-355313 - Merge remote-tracking branch 'hip/amd-staging' into amd-staging
Change-Id: I96ef1395f75089ad701ca637f5c0273d280e849b
[ROCm/hip-tests commit: 0571115bed]
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
add.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME ABMAddKernels
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME build_tests)
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <hip_test_common.hh>
|
||||
#include <iostream>
|
||||
|
||||
template <typename T> __global__ void add(T* a, T* b, T* c, size_t size) {
|
||||
size_t i = threadIdx.x;
|
||||
if (i < size) c[i] = a[i] + b[i];
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE("ABM_AddKernel_MultiTypeMultiSize", "", int, long, float, long long, double) {
|
||||
auto size = GENERATE(as<size_t>{}, 100, 500, 1000);
|
||||
TestType *d_a, *d_b, *d_c;
|
||||
auto res = hipMalloc(&d_a, sizeof(TestType) * size);
|
||||
REQUIRE(res == hipSuccess);
|
||||
res = hipMalloc(&d_b, sizeof(TestType) * size);
|
||||
REQUIRE(res == hipSuccess);
|
||||
res = hipMalloc(&d_c, sizeof(TestType) * size);
|
||||
REQUIRE(res == hipSuccess);
|
||||
|
||||
std::vector<TestType> a, b, c;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
a.push_back(i + 1);
|
||||
b.push_back(i + 1);
|
||||
c.push_back(2 * (i + 1));
|
||||
}
|
||||
|
||||
res = hipMemcpy(d_a, a.data(), sizeof(TestType) * size, hipMemcpyHostToDevice);
|
||||
REQUIRE(res == hipSuccess);
|
||||
res = hipMemcpy(d_b, b.data(), sizeof(TestType) * size, hipMemcpyHostToDevice);
|
||||
REQUIRE(res == hipSuccess);
|
||||
|
||||
hipLaunchKernelGGL(add<TestType>, 1, size, 0, 0, d_a, d_b, d_c, size);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
|
||||
res = hipMemcpy(a.data(), d_c, sizeof(TestType) * size, hipMemcpyDeviceToHost);
|
||||
REQUIRE(res == hipSuccess);
|
||||
|
||||
HIP_CHECK(hipFree(d_a));
|
||||
HIP_CHECK(hipFree(d_b));
|
||||
HIP_CHECK(hipFree(d_c));
|
||||
REQUIRE(a == c);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(AddKernels)
|
||||
@@ -0,0 +1,252 @@
|
||||
cmake_minimum_required(VERSION 3.16.8)
|
||||
|
||||
# to skip the simple compiler test
|
||||
set(CMAKE_C_COMPILER_WORKS 1)
|
||||
set(CMAKE_CXX_COMPILER_WORKS 1)
|
||||
|
||||
project(hiptests)
|
||||
|
||||
|
||||
# Check if platform and compiler are set
|
||||
if(HIP_PLATFORM STREQUAL "amd")
|
||||
if(HIP_COMPILER STREQUAL "nvcc")
|
||||
message(FATAL_ERROR "Unexpected HIP_COMPILER:${HIP_COMPILER} is set for HIP_PLATFOR:amd")
|
||||
endif()
|
||||
elseif(HIP_PLATFORM STREQUAL "nvidia")
|
||||
if(DEFINED HIP_COMPILER AND NOT HIP_COMPILER STREQUAL "nvcc")
|
||||
message(FATAL_ERROR "Unexpected HIP_COMPILER: ${HIP_COMPILER} is set for HIP_PLATFORM:nvidia")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Unexpected HIP_PLATFORM: " ${HIP_PLATFORM})
|
||||
endif()
|
||||
|
||||
# Set HIP Path
|
||||
if(NOT DEFINED HIP_PATH)
|
||||
if(DEFINED ENV{HIP_PATH})
|
||||
set(HIP_PATH $ENV{HIP_PATH} CACHE STRING "HIP Path")
|
||||
else()
|
||||
set(HIP_PATH "${PROJECT_BINARY_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "HIP Path: ${HIP_PATH}")
|
||||
|
||||
# Set ROCM Path
|
||||
if(NOT DEFINED ROCM_PATH)
|
||||
if(DEFINED ENV{ROCM_PATH})
|
||||
set(ROCM_PATH $ENV{ROCM_PATH} CACHE STRING "ROCM Path")
|
||||
else()
|
||||
cmake_path(GET HIP_PATH PARENT_PATH ROCM_PATH)
|
||||
if (NOT EXISTS "${ROCM_PATH}/bin/rocm_agent_enumerator")
|
||||
set(ROCM_PATH "/opt/rocm/")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
file(TO_CMAKE_PATH "${ROCM_PATH}" ROCM_PATH)
|
||||
message(STATUS "ROCM Path: ${ROCM_PATH}")
|
||||
|
||||
|
||||
if(UNIX)
|
||||
set(CMAKE_CXX_COMPILER "${HIP_PATH}/bin/hipcc")
|
||||
set(CMAKE_C_COMPILER "${HIP_PATH}/bin/hipcc")
|
||||
set(HIPCONFIG_EXECUTABLE "${HIP_PATH}/bin/hipconfig")
|
||||
execute_process(COMMAND perl ${HIPCONFIG_EXECUTABLE} --version
|
||||
OUTPUT_VARIABLE HIP_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
else()
|
||||
# using cmake_path as it handles path correctly.
|
||||
# Set both compilers else windows cmake complains of mismatch
|
||||
cmake_path(SET CMAKE_CXX_COMPILER "${HIP_PATH}/bin/hipcc.bat")
|
||||
cmake_path(SET CMAKE_C_COMPILER "${HIP_PATH}/bin/hipcc.bat")
|
||||
set(HIPCONFIG_EXECUTABLE "${HIP_PATH}/bin/hipconfig.bat")
|
||||
execute_process(COMMAND ${HIPCONFIG_EXECUTABLE} --version
|
||||
OUTPUT_VARIABLE HIP_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
if(HIP_PLATFORM STREQUAL "amd")
|
||||
# prioritize -DROCM_PATH over env{ROCM_PATH} for amd platform only
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --rocm-path=${ROCM_PATH}")
|
||||
endif()
|
||||
# enforce c++17
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++17")
|
||||
|
||||
string(REPLACE "." ";" VERSION_LIST ${HIP_VERSION})
|
||||
list(GET VERSION_LIST 0 HIP_VERSION_MAJOR)
|
||||
list(GET VERSION_LIST 1 HIP_VERSION_MINOR)
|
||||
list(GET VERSION_LIST 2 HIP_VERSION_PATCH_GITHASH)
|
||||
string(REPLACE "-" ";" VERSION_LIST ${HIP_VERSION_PATCH_GITHASH})
|
||||
list(GET VERSION_LIST 0 HIP_VERSION_PATCH)
|
||||
|
||||
if(NOT DEFINED CATCH2_PATH)
|
||||
if(DEFINED ENV{CATCH2_PATH})
|
||||
set(CATCH2_PATH $ENV{CATCH2_PATH} CACHE STRING "Catch2 Path")
|
||||
else()
|
||||
set(CATCH2_PATH "${CMAKE_CURRENT_LIST_DIR}/external/Catch2")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "Catch2 Path: ${CATCH2_PATH}")
|
||||
|
||||
# Set JSON Parser path
|
||||
if(NOT DEFINED JSON_PARSER)
|
||||
if(DEFINED ENV{JSON_PARSER})
|
||||
set(JSON_PARSER $ENV{JSON_PARSER} CACHE STRING "JSON Parser Path")
|
||||
else()
|
||||
set(JSON_PARSER "${CMAKE_CURRENT_LIST_DIR}/external/picojson")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Searching Catch2 in: ${CMAKE_CURRENT_LIST_DIR}/external")
|
||||
find_package(Catch2 REQUIRED
|
||||
PATHS
|
||||
${CMAKE_CURRENT_LIST_DIR}/external
|
||||
PATH_SUFFIXES
|
||||
Catch2/cmake/Catch2
|
||||
)
|
||||
include(Catch)
|
||||
include(CTest)
|
||||
|
||||
# path used for generating the *_include.cmake file
|
||||
set(CATCH2_INCLUDE ${CATCH2_PATH}/cmake/Catch2/catch_include.cmake.in)
|
||||
|
||||
include_directories(
|
||||
${CATCH2_PATH}
|
||||
"./include"
|
||||
"./kernels"
|
||||
${HIP_PATH}/include
|
||||
${JSON_PARSER}
|
||||
)
|
||||
|
||||
option(RTC_TESTING "Run tests using HIP RTC to compile the kernels" OFF)
|
||||
if (RTC_TESTING)
|
||||
add_definitions(-DRTC_TESTING=ON)
|
||||
endif()
|
||||
add_definitions(-DKERNELS_PATH="${CMAKE_CURRENT_SOURCE_DIR}/kernels/")
|
||||
|
||||
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)
|
||||
set(ADD_SCRIPT_PATH ${CMAKE_CURRENT_BINARY_DIR}/${CATCH_BUILD_DIR}/script/CatchAddTests.cmake)
|
||||
|
||||
if (WIN32)
|
||||
configure_file(catchProp_in_rc.in ${CMAKE_CURRENT_BINARY_DIR}/catchProp.rc @ONLY)
|
||||
cmake_path(SET LLVM_RC_PATH "${HIP_PATH}/../lc/bin/llvm-rc.exe")
|
||||
cmake_path(SET LLVM_RC_PATH NORMALIZE "${LLVM_RC_PATH}")
|
||||
|
||||
# generates the .res files to be used by executables to populate the properties
|
||||
# expects LC folder with clang, llvm-rc to be present one level up of HIP
|
||||
execute_process(COMMAND ${LLVM_RC_PATH} ${CMAKE_CURRENT_BINARY_DIR}/catchProp.rc
|
||||
OUTPUT_VARIABLE RC_OUTPUT)
|
||||
set(PROP_RC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if(HIP_PLATFORM MATCHES "amd" AND HIP_COMPILER MATCHES "clang")
|
||||
add_compile_options(-Wall -Wextra -pedantic -Werror -Wno-deprecated)
|
||||
endif()
|
||||
|
||||
cmake_policy(PUSH)
|
||||
if(POLICY CMP0037)
|
||||
cmake_policy(SET CMP0037 OLD)
|
||||
endif()
|
||||
|
||||
# Turn off CMAKE_HIP_ARCHITECTURES Feature if cmake version is 3.21+
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21.0)
|
||||
set(CMAKE_HIP_ARCHITECTURES OFF)
|
||||
endif()
|
||||
message(STATUS "CMAKE HIP ARCHITECTURES: ${CMAKE_HIP_ARCHITECTURES}")
|
||||
|
||||
# Note to pass arch use format like -DOFFLOAD_ARCH_STR="--offload-arch=gfx900 --offload-arch=gfx906"
|
||||
# having space at the start/end of OFFLOAD_ARCH_STR can cause build failures
|
||||
# Identify the GPU Targets.
|
||||
# This is done due to limitation of rocm_agent_enumerator
|
||||
# While building test parallelly, rocm_agent_enumerator can fail and give out an empty target
|
||||
# That results in hipcc building the test for gfx803 (the default target)
|
||||
# preference to pass arch -
|
||||
# OFFLOAD_ARCH_STR
|
||||
# ENV{HCC_AMDGPU_TARGET}
|
||||
# rocm_agent_enumerator
|
||||
if(NOT DEFINED OFFLOAD_ARCH_STR
|
||||
AND NOT DEFINED ENV{HCC_AMDGPU_TARGET}
|
||||
AND EXISTS "${ROCM_PATH}/bin/rocm_agent_enumerator"
|
||||
AND HIP_PLATFORM STREQUAL "amd" AND UNIX)
|
||||
execute_process(COMMAND ${ROCM_PATH}/bin/rocm_agent_enumerator
|
||||
OUTPUT_VARIABLE HIP_GPU_ARCH
|
||||
RESULT_VARIABLE ROCM_AGENT_ENUM_RESULT)
|
||||
# Trim out gfx000
|
||||
string(REPLACE "gfx000\n" "" HIP_GPU_ARCH ${HIP_GPU_ARCH})
|
||||
if (NOT HIP_GPU_ARCH STREQUAL "")
|
||||
string(LENGTH ${HIP_GPU_ARCH} HIP_GPU_ARCH_LEN)
|
||||
# If string has more gfx target except gfx000
|
||||
if(${HIP_GPU_ARCH_LEN} GREATER_EQUAL 1)
|
||||
string(REGEX REPLACE "\n" ";" HIP_GPU_ARCH_LIST "${HIP_GPU_ARCH}")
|
||||
set(OFFLOAD_ARCH_STR "")
|
||||
foreach(_hip_gpu_arch ${HIP_GPU_ARCH_LIST})
|
||||
set(OFFLOAD_ARCH_STR "--offload-arch=${_hip_gpu_arch} ${OFFLOAD_ARCH_STR}")
|
||||
endforeach()
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "ROCm Agent Enumurator found no valid architectures")
|
||||
endif()
|
||||
elseif(DEFINED OFFLOAD_ARCH_STR)
|
||||
string(REPLACE "--offload-arch=" "" HIP_GPU_ARCH_LIST ${OFFLOAD_ARCH_STR})
|
||||
endif()
|
||||
|
||||
if(DEFINED OFFLOAD_ARCH_STR)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OFFLOAD_ARCH_STR} ")
|
||||
elseif(DEFINED ENV{HCC_AMDGPU_TARGET})
|
||||
# hipcc pl script appends it to the options
|
||||
set(OFFLOAD_ARCH_STR " --offload-arch=$ENV{HCC_AMDGPU_TARGET}")
|
||||
set(HIP_GPU_ARCH_LIST $ENV{HCC_AMDGPU_TARGET})
|
||||
endif()
|
||||
message(STATUS "Using offload arch string: ${OFFLOAD_ARCH_STR}")
|
||||
|
||||
# prints the catch info to a file
|
||||
string(TIMESTAMP _timestamp UTC)
|
||||
set(_catchInfo "# Auto-generated by cmake on ${_timestamp} UTC\n")
|
||||
set(_catchInfo ${_catchInfo} "HIP_VERSION=${HIP_VERSION}\n")
|
||||
set(_catchInfo ${_catchInfo} "HIP_PLATFORM=${HIP_PLATFORM}\n")
|
||||
set(_catchInfo ${_catchInfo} "ARCHS=${HIP_GPU_ARCH_LIST}\n")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${CATCH_BUILD_DIR}/catchInfo.txt ${_catchInfo})
|
||||
|
||||
# Enable device lambda on nvidia platforms
|
||||
if(HIP_COMPILER MATCHES "nvcc")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --extended-lambda")
|
||||
endif()
|
||||
|
||||
# Disable CXX extensions (gnu++11 etc)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_custom_target(build_tests)
|
||||
|
||||
|
||||
# Tests folder
|
||||
add_subdirectory(unit ${CATCH_BUILD_DIR}/unit)
|
||||
add_subdirectory(ABM ${CATCH_BUILD_DIR}/ABM)
|
||||
add_subdirectory(kernels ${CATCH_BUILD_DIR}/kernels)
|
||||
add_subdirectory(hipTestMain ${CATCH_BUILD_DIR}/hipTestMain)
|
||||
add_subdirectory(stress ${CATCH_BUILD_DIR}/stress)
|
||||
add_subdirectory(TypeQualifiers ${CATCH_BUILD_DIR}/TypeQualifiers)
|
||||
if(UNIX)
|
||||
add_subdirectory(multiproc ${CATCH_BUILD_DIR}/multiproc)
|
||||
endif()
|
||||
|
||||
cmake_policy(POP)
|
||||
|
||||
# packaging the tests
|
||||
# make package_test to generate packages for test
|
||||
set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/packages/)
|
||||
configure_file(packaging/hip-tests.txt ${BUILD_DIR}/CMakeLists.txt @ONLY)
|
||||
if(UNIX)
|
||||
add_custom_target(package_test COMMAND ${CMAKE_COMMAND} .
|
||||
COMMAND rm -rf *.deb *.rpm *.tar.gz
|
||||
COMMAND make package
|
||||
COMMAND cp *.deb ${PROJECT_BINARY_DIR}
|
||||
COMMAND cp *.rpm ${PROJECT_BINARY_DIR}
|
||||
COMMAND cp *.tar.gz ${PROJECT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${BUILD_DIR})
|
||||
else()
|
||||
file(TO_NATIVE_PATH ${PROJECT_BINARY_DIR} CATCH_BINARY_DIR)
|
||||
add_custom_target(package_test COMMAND ${CMAKE_COMMAND} .
|
||||
COMMAND cpack
|
||||
COMMAND copy *.zip ${CATCH_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${BUILD_DIR})
|
||||
endif()
|
||||
@@ -0,0 +1,204 @@
|
||||
# HIP Tests - with Catch2
|
||||
|
||||
## Intro and Motivation
|
||||
HIP Tests were using HIT framework (a custom framework tailored for HIP) to add, build and run tests. As time progressed the frame got big and took substantial amount of effort to maintain and extend. It also took substantial amount of time to configure. We took this oppurtunity to rewrite the HIP's testing framework and porting the test infra to Catch2 format.
|
||||
|
||||
## How to write tests
|
||||
Tests in Catch2 are declared via ```TEST_CASE```.
|
||||
|
||||
[Please read the Catch2 documentation on how to write test cases](https://github.com/catchorg/Catch2/blob/v2.13.6/docs/tutorial.md#top)
|
||||
|
||||
[Catch2 Detailed Reference](https://github.com/catchorg/Catch2/blob/v2.13.6/docs/Readme.md#top)
|
||||
|
||||
## Taking care of existing features
|
||||
- Don’t build on platform: EXCLUDE_(HIP_PLATFORM/HIP_RUNTIME), can be done via CMAKE. Adding source in if(HIP_PLATFORM == amd/nvidia).
|
||||
- HIPCC_OPTIONS/CLANG Options: Can be done via: set_source_files_properties(src.cc PROPERTIES COMPILE_FLAGS “…”).
|
||||
- Additional libraries: Can be done via target_link_libraries()
|
||||
- Multiple runs with different args: This can be done by Catch’s Feature: GENERATE(…)
|
||||
Running Subtest: ctest –R “...” (Regex to match the subtest name)
|
||||
|
||||
## New Features
|
||||
- Skip test without recompiling tests, by addition of a json file. Default name is ```config.json``` , this can be overridden by using the variable ```HIP_CATCH_EXCLUDE_FILE=some_config.json```.
|
||||
- Json file supports regex. Ex: All tests which has the word ‘Memset’ can be skipped using ‘*Memset*’
|
||||
- Support multiple skip test list which can be set via environment variable, so you can have multiple files containing different skip test lists and can pick and choose among them depending on your platform and os.
|
||||
- Better CI integration via xunit compatible output
|
||||
|
||||
## Testing Context
|
||||
HIP testing framework gives you a context for each test. This context will have useful information about the environment your test is running.
|
||||
|
||||
Some useful functions are:
|
||||
- `bool isWindows()` : true if os is windows
|
||||
- `bool isLinux()` : true if os is linux
|
||||
- `bool isAmd()` : true if platform is AMD
|
||||
- `bool isNvidia()` : true if platform is NVIDIA
|
||||
|
||||
This information can be accessed in any test via using: `TestContext::get().isAmd()`.
|
||||
|
||||
## Adding test for a specific platform
|
||||
There might be some functionality which is not present on some platforms. Those tests can be hidden inside following macros.
|
||||
|
||||
- ```HT_AMD``` is 1 when tests are running on AMD platform and 0 on NVIDIA.
|
||||
- ```HT_NVIDIA``` is 1 when tests are running on NVIDIA platform and 0 on AMD
|
||||
|
||||
Usage:
|
||||
|
||||
```cpp
|
||||
#if HT_AMD
|
||||
TEST_CASE("hipExtAPIs") {
|
||||
// ...
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## Config file schema
|
||||
Some tests can be skipped using a config file placed in hipTestMain/config folder. Multiple config files can be defined for different configurations.
|
||||
The naming convention for the file needs to be "config_platform_os_archname.json"
|
||||
Platform and os are mandatory.
|
||||
Arch name is optional and takes precedence while loading the json file.
|
||||
Currently the json files need to be manually chosen by the executor for the architecture of choice.
|
||||
|
||||
example:
|
||||
config_amd_windows.json
|
||||
config_nvidia_windows.json
|
||||
|
||||
The schema of the json file is as follows:
|
||||
```json
|
||||
{
|
||||
"DisabledTests":
|
||||
[
|
||||
"TestName1",
|
||||
"TestName2",
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
- `HIP_CATCH_EXCLUDE_FILE` : This variable can be set to the config file name or full path. Disabled tests will be read from this.
|
||||
- `HT_LOG_ENABLE` : This is for debugging the HIP Test Framework itself. Setting it to 1, all `LogPrintf` will be printed on screen
|
||||
|
||||
## Test Macros
|
||||
### Single Thread Macros
|
||||
These macros are to be used when your test is calling HIP APIs via the main thread.
|
||||
|
||||
- `HIP_CHECK` : This macro takes in a HIP API and tests for its result to be either ```hipSuccess``` or ```hipErrorPeerAccessAlreadyEnabled```.
|
||||
|
||||
- Usage: ```HIP_CHECK(hipMalloc(&dPtr, 10));```
|
||||
|
||||
- ```HIP_CHECK_ERROR``` : This macro takes in a HIP API and tests its result against a provided result. This can be used when the API is expected to fail with a particular result.
|
||||
|
||||
- Usage: ```HIP_CHECK_ERROR(hipMalloc(&dPtr, 0), hipErrorInvalidValue);```
|
||||
|
||||
- ```HIPRTC_CHECK``` : This macro takes in a HIPRTC API and tests its result against HIPRTC_SUCCESS.
|
||||
|
||||
- Usage: ```HIPRTC_CHECK(hiprtcCompileProgram(prog, count, options));```
|
||||
|
||||
- ```HIP_ASSERT``` : This macro takes in a bool condition as input and does a ```REQUIRE``` on the condition.
|
||||
|
||||
- Usage: ```HIP_ASSERT(result == 10);```
|
||||
|
||||
### Multi Thread Macros
|
||||
These macros are to be used when you call HIP APIs in a multi threaded way. They exist because Catch2 ```REQUIRE``` and ```CHECK``` macros can not handle multi threaded calls. To solve this problem, two macros are added```HIP_CHECK_THREAD``` and ```REQUIRE_THREAD``` which can be used to check result of HIP APIs and test assertions respectively. The results can be validate after the threads join via ```HIP_CHECK_THREAD_FINALIZE```.
|
||||
|
||||
Note: These should used in ```std::thread``` only. For multi proc guidelines look at [MultiProc Macros](#multi-process-macros) and [SpawnProc Class](#multiproc-management-class)
|
||||
|
||||
- ```HIP_CHECK_THREAD``` : This macro takes in a HIP API and tests for its result to be either ```hipSuccess``` or ```hipErrorPeerAccessAlreadyEnabled```. It can also tell other threads if an error has occured in one of the HIP API and can prematurely stop the threads.
|
||||
|
||||
- ```REQUIRE_THREAD``` : This macro takes in a bool condition and tests for its result to be true. If this check fails, it can signal other threads to terminate early.
|
||||
|
||||
- ```HIP_CHECK_THREAD_FINALIZE``` : This macro checks for the results logged by ```HIP_CHECK_THREAD```. This needs to be called after the threads have joined.
|
||||
|
||||
Please also note that you can not return values in functions calling ```HIP_CHECK_THREAD``` or ```REQUIRE_THREAD``` macro.
|
||||
|
||||
Usage:
|
||||
|
||||
```cpp
|
||||
auto threadFunc = []() {
|
||||
int *dPtr{nullptr};
|
||||
HIP_CHECK_THREAD(hipMalloc(&dPtr, 10));
|
||||
REQUIRE_THREAD(dPtr != nullptr);
|
||||
// Some other work
|
||||
};
|
||||
|
||||
// Launch threads
|
||||
std::vector<std::thread> threadPool;
|
||||
for(...) {
|
||||
threadPool.emplace_back(std::thread(threadFunc));
|
||||
}
|
||||
|
||||
// Join threads
|
||||
for(auto &i : threadPool) {
|
||||
i.join();
|
||||
}
|
||||
|
||||
// Validate all results
|
||||
HIP_CHECK_THREAD_FINALIZE();
|
||||
```
|
||||
|
||||
### Skipping Tests if certain criteria is not met
|
||||
If there arises a condition where certain flag is disabled and due to which a test can not run at that time, the following macro can be of use. It will highlight the test in ctest report as well.
|
||||
|
||||
- ```HIP_SKIP_TEST``` : The api takes in an input of the reason as well and prints out the line HIP_SKIP_THIS_TEST. This causes ctest to mark the test as skipped and the test shows up in the report as skipped prompting proper response from the team.
|
||||
|
||||
Usage:
|
||||
|
||||
```cpp
|
||||
TEST_CASE("TestOnlyOnXnack") {
|
||||
if(!XNACKEnabled) {
|
||||
HipTest::HIP_SKIP_TEST("Test only runs on system with XNACK enabled");
|
||||
return;
|
||||
}
|
||||
// Rest of test functionality
|
||||
}
|
||||
```
|
||||
|
||||
### Multi Process Macros
|
||||
These macros are to be called in multi process tests, inside a process which gets spawned. The reasoning is the same, Catch2 does not support multi process checks.
|
||||
|
||||
- ```HIPCHECK``` : Same as ```HIP_CHECK``` but will not call Catch2's ```REQUIRE``` on the HIP API. It will print if there is a mismatch and exit the process.
|
||||
|
||||
- ```HIPASSERT``` : Same as ```HIP_ASSERT``` but will not call Catch2's ```REQUIRE``` on the HIP API. It will print if there is a mismatch and exit the process.
|
||||
|
||||
## MultiProc Management Class
|
||||
There is a special interface available for process isolation. ```hip::SpawnProc``` in ```hip_test_process.hh```. Using this interface test can spawn a process and place passing conditions on its return value or its output to stdout. This can be useful for testing printf output.
|
||||
Sample Usage:
|
||||
```cpp
|
||||
hip::SpawnProc proc(<name of exe>, <optional bool value, if output is to be recorded>);
|
||||
REQUIRE(0 == proc.run()); // Test of return value of the proc
|
||||
REQUIRE(exepctedOutput == proc.getOutput()); // Test on expected output of the process
|
||||
```
|
||||
The process must be a standalone exe inside the same folder as other tests.
|
||||
|
||||
## Enabling New Tests
|
||||
Initially, the new tests can be enabled via using ```-DHIP_CATCH_TEST=1```. After porting existing tests, this will be turned on by default.
|
||||
|
||||
## Building a single test
|
||||
```bash
|
||||
hipcc <path_to_test.cpp> -I<HIP_SRC_DIR>/tests/catch/include <HIP_SRC_DIR>/tests/catch/hipTestMain/standalone_main.cc -I<HIP_SRC_DIR>/tests/catch/external/Catch2 -g -o <out_file_name>
|
||||
```
|
||||
|
||||
## Debugging support
|
||||
Catch2 allows multiple ways in which you can debug the test case.
|
||||
- `-b` options breaks into a debugger as soon as there is a failure encountered [Catch2 Options Reference](https://github.com/catchorg/Catch2/blob/devel/docs/command-line.md#breaking-into-the-debugger)
|
||||
- Catch2 provided [logging macro](https://github.com/catchorg/Catch2/blob/v2.13.6/docs/logging.md#top) that print useful information on test case failure
|
||||
- User can also call [CATCH_BREAK_INTO_DEBUGGER](https://github.com/catchorg/Catch2/blob/devel/docs/configuration.md#overriding-catchs-debug-break--b) macro to break at a certain point in a test case.
|
||||
- User can also mention filename.cc:__LineNumber__ to break into a test case via gdb.
|
||||
|
||||
## External Libs being used
|
||||
- [Catch2](https://github.com/catchorg/Catch2) - Testing framework
|
||||
- [picojson](https://github.com/kazuho/picojson) - For config file parsing
|
||||
|
||||
# Testing Guidelines
|
||||
Tests fall in 5 categories and its file name prefix are as follows:
|
||||
- Unit tests (Prefix: Unit_\*API\*_\*Optional Scenario\*, example : Unit_hipMalloc_Negative or Unit_hipMalloc): Unit Tests are simplest test for an API, the target here is to test the API with different types of input and different ways of calling.
|
||||
- Application Behavior Modelling tests (Prefix: ABM_\*Intent\*_\*Optional Scenario\*, example: ABM_ModuleLoadAndRun): ABM tests are used to model a specific use case of HIP APIs, either seen in a customer app or a general purpose app. It mimics the calling behavior seen in aforementioned app.
|
||||
- Stress/Scale tests (Prefix: Stress_\*API\*_\*Intent\*_\*Optional Scenario\*, example: Stress_hipMemset_ExhaustVRAM): These tests are used to see the behavior of HIP APIs in edge scenarios, for example what happens when we have exhausted vram and do a hipMalloc or run many instances of same API in parallel.
|
||||
- Multi Process tests (Prefix: MultiProc_\*API\*_\*Optional Scenario\*, example: MultiProc_hipIPCMemHandle_GetDataFromProc): These tests are multi process tests and will only run on linux. They are used to test HIP APIs in multi process environment
|
||||
- Performance tests(Prefix: Perf_\*Intent\*_\*Optional Scenario\*, example: Perf_DispatchLatenc y): Performance tests are used to get results of HIP APIs.
|
||||
|
||||
# General Guidelines:
|
||||
- Do not use the catch2 tags. Tags wont be used for filtering
|
||||
- Add as many INFO() as you can in tests which prints state of the t est, this will help the debugger when the test fails (INFO macro only prints when the test fails)
|
||||
- Check return of each HIP API and fail whenever there is a misma tch with hipSuccess or hiprtcSuccess.
|
||||
- Each Category of test will hav e its own exe and catch_discover_test macro will be called on it to discover its tests
|
||||
- Optional Scenario in test names are optional. For example you can test all Scenarios of hipMalloc API in one file, you can name the file Unit_hipMalloc, if you are having a file just for negative scenarios you can name it as Unit_hipMalloc_Negative.
|
||||
@@ -0,0 +1,8 @@
|
||||
# Common Tests
|
||||
set(TEST_SRC
|
||||
hipManagedKeyword.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME TypeQualifiers
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME build_tests)
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This testcase verifies the hipManagedKeyword basic scenario
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
|
||||
#define N 1048576
|
||||
__managed__ float A[N]; // Accessible by ALL CPU and GPU functions !!!
|
||||
__managed__ float B[N];
|
||||
__managed__ int x = 0;
|
||||
|
||||
__global__ void add(const float *A, float *B) {
|
||||
int index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int stride = blockDim.x * gridDim.x;
|
||||
for (int i = index; i < N; i += stride)
|
||||
B[i] = A[i] + B[i];
|
||||
}
|
||||
|
||||
__global__ void GPU_func() {
|
||||
x++;
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipManagedKeyword_SingleGpu") {
|
||||
for (int i = 0; i < N; i++) {
|
||||
A[i] = 1.0f;
|
||||
B[i] = 2.0f;
|
||||
}
|
||||
|
||||
int blockSize = 256;
|
||||
int numBlocks = (N + blockSize - 1) / blockSize;
|
||||
dim3 dimGrid(numBlocks, 1, 1);
|
||||
dim3 dimBlock(blockSize, 1, 1);
|
||||
hipLaunchKernelGGL(add, dimGrid, dimBlock, 0, 0, static_cast<const float*>(A),
|
||||
static_cast<float*>(B));
|
||||
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
|
||||
float maxError = 0.0f;
|
||||
for (int i = 0; i < N; i++)
|
||||
maxError = fmax(maxError, fabs(B[i]-3.0f));
|
||||
|
||||
REQUIRE(maxError == 0.0f);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipManagedKeyword_MultiGpu") {
|
||||
int numDevices = 0;
|
||||
HIP_CHECK(hipGetDeviceCount(&numDevices));
|
||||
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
GPU_func<<< 1, 1 >>>();
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
}
|
||||
REQUIRE(x == numDevices);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <windows.h>
|
||||
|
||||
#define HIP_VERSION "@HIP_VERSION@"
|
||||
#define HIP_VERSION_MAJOR @HIP_VERSION_MAJOR@
|
||||
#define HIP_VERSION_MINOR @HIP_VERSION_MINOR@
|
||||
#define HIP_VERSION_PATCH @HIP_VERSION_PATCH@
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION HIP_VERSION_MAJOR, HIP_VERSION_MINOR , HIP_VERSION_PATCH
|
||||
PRODUCTVERSION 10,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Advanced Micro Devices Inc.\0"
|
||||
VALUE "FileDescription", "HIP unit tests"
|
||||
VALUE "FileVersion", "amdhip64.dll" HIP_VERSION
|
||||
VALUE "LegalCopyright", "Copyright (C) 2022 Advanced Micro Devices Inc.\0"
|
||||
VALUE "ProductName", "HIP unit tests"
|
||||
VALUE "ProductVersion", HIP_VERSION
|
||||
VALUE "Comments", "\0"
|
||||
VALUE "InternalName", "HIP unit tests"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0409, 1200
|
||||
END
|
||||
END
|
||||
/* End of Version info */
|
||||
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
+17881
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,249 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
Catch
|
||||
-----
|
||||
|
||||
This module defines a function to help use the Catch test framework.
|
||||
|
||||
The :command:`catch_discover_tests` discovers tests by asking the compiled test
|
||||
executable to enumerate its tests. This does not require CMake to be re-run
|
||||
when tests change. However, it may not work in a cross-compiling environment,
|
||||
and setting test properties is less convenient.
|
||||
|
||||
This command is intended to replace use of :command:`add_test` to register
|
||||
tests, and will create a separate CTest test for each Catch test case. Note
|
||||
that this is in some cases less efficient, as common set-up and tear-down logic
|
||||
cannot be shared by multiple test cases executing in the same instance.
|
||||
However, it provides more fine-grained pass/fail information to CTest, which is
|
||||
usually considered as more beneficial. By default, the CTest test name is the
|
||||
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
|
||||
.. command:: catch_discover_tests
|
||||
|
||||
Automatically add tests with CTest by querying the compiled test executable
|
||||
for available tests::
|
||||
|
||||
catch_discover_tests(target
|
||||
[TEST_SPEC arg1...]
|
||||
[EXTRA_ARGS arg1...]
|
||||
[WORKING_DIRECTORY dir]
|
||||
[TEST_PREFIX prefix]
|
||||
[TEST_SUFFIX suffix]
|
||||
[PROPERTIES name1 value1...]
|
||||
[TEST_LIST var]
|
||||
[REPORTER reporter]
|
||||
[OUTPUT_DIR dir]
|
||||
[OUTPUT_PREFIX prefix}
|
||||
[OUTPUT_SUFFIX suffix]
|
||||
)
|
||||
|
||||
``catch_discover_tests`` sets up a post-build command on the test executable
|
||||
that generates the list of tests by parsing the output from running the test
|
||||
with the ``--list-test-names-only`` argument. This ensures that the full
|
||||
list of tests is obtained. Since test discovery occurs at build time, it is
|
||||
not necessary to re-run CMake when the list of tests changes.
|
||||
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
|
||||
in order to function in a cross-compiling environment.
|
||||
|
||||
Additionally, setting properties on tests is somewhat less convenient, since
|
||||
the tests are not available at CMake time. Additional test properties may be
|
||||
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
|
||||
more fine-grained test control is needed, custom content may be provided
|
||||
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
|
||||
directory property. The set of discovered tests is made accessible to such a
|
||||
script via the ``<target>_TESTS`` variable.
|
||||
|
||||
The options are:
|
||||
|
||||
``target``
|
||||
Specifies the Catch executable, which must be a known CMake executable
|
||||
target. CMake will substitute the location of the built executable when
|
||||
running the test.
|
||||
|
||||
``TEST_SPEC arg1...``
|
||||
Specifies test cases, wildcarded test cases, tags and tag expressions to
|
||||
pass to the Catch executable with the ``--list-test-names-only`` argument.
|
||||
|
||||
``EXTRA_ARGS arg1...``
|
||||
Any extra arguments to pass on the command line to each test case.
|
||||
|
||||
``WORKING_DIRECTORY dir``
|
||||
Specifies the directory in which to run the discovered test cases. If this
|
||||
option is not provided, the current binary directory is used.
|
||||
|
||||
``TEST_PREFIX prefix``
|
||||
Specifies a ``prefix`` to be prepended to the name of each discovered test
|
||||
case. This can be useful when the same test executable is being used in
|
||||
multiple calls to ``catch_discover_tests()`` but with different
|
||||
``TEST_SPEC`` or ``EXTRA_ARGS``.
|
||||
|
||||
``TEST_SUFFIX suffix``
|
||||
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
|
||||
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
|
||||
be specified.
|
||||
|
||||
``PROPERTIES name1 value1...``
|
||||
Specifies additional properties to be set on all tests discovered by this
|
||||
invocation of ``catch_discover_tests``.
|
||||
|
||||
``TEST_LIST var``
|
||||
Make the list of tests available in the variable ``var``, rather than the
|
||||
default ``<target>_TESTS``. This can be useful when the same test
|
||||
executable is being used in multiple calls to ``catch_discover_tests()``.
|
||||
Note that this variable is only available in CTest.
|
||||
|
||||
``REPORTER reporter``
|
||||
Use the specified reporter when running the test case. The reporter will
|
||||
be passed to the Catch executable as ``--reporter reporter``.
|
||||
|
||||
``OUTPUT_DIR dir``
|
||||
If specified, the parameter is passed along as
|
||||
``--out dir/<test_name>`` to Catch executable. The actual file name is the
|
||||
same as the test name. This should be used instead of
|
||||
``EXTRA_ARGS --out foo`` to avoid race conditions writing the result output
|
||||
when using parallel test execution.
|
||||
|
||||
``OUTPUT_PREFIX prefix``
|
||||
May be used in conjunction with ``OUTPUT_DIR``.
|
||||
If specified, ``prefix`` is added to each output file name, like so
|
||||
``--out dir/prefix<test_name>``.
|
||||
|
||||
``OUTPUT_SUFFIX suffix``
|
||||
May be used in conjunction with ``OUTPUT_DIR``.
|
||||
If specified, ``suffix`` is added to each output file name, like so
|
||||
``--out dir/<test_name>suffix``. This can be used to add a file extension to
|
||||
the output e.g. ".xml".
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
function(catch_discover_tests TARGET)
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX"
|
||||
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if(NOT _WORKING_DIRECTORY)
|
||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
|
||||
## 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}/${TARGET}_include-${args_hash}.cmake")
|
||||
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_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 _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
|
||||
)
|
||||
|
||||
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")
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(DIRECTORY
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctestincludepath}"
|
||||
)
|
||||
else()
|
||||
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
|
||||
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
|
||||
if (NOT ${test_include_file_set})
|
||||
set_property(DIRECTORY
|
||||
PROPERTY TEST_INCLUDE_FILE "${ctestincludepath}"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Cannot set more than one TEST_INCLUDE_FILE"
|
||||
)
|
||||
endif()
|
||||
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)
|
||||
set(options)
|
||||
set(args NAME TEST_TARGET_NAME PLATFORM COMPILE_OPTIONS)
|
||||
set(list_args TEST_SRC LINKER_LIBS PROPERTY)
|
||||
cmake_parse_arguments(
|
||||
PARSE_ARGV 0
|
||||
"" # variable prefix
|
||||
"${options}"
|
||||
"${args}"
|
||||
"${list_args}"
|
||||
)
|
||||
# Create shared lib of all tests
|
||||
if(NOT RTC_TESTING)
|
||||
add_executable(${_NAME} EXCLUDE_FROM_ALL ${_TEST_SRC} $<TARGET_OBJECTS:Main_Object> $<TARGET_OBJECTS:KERNELS>)
|
||||
else ()
|
||||
add_executable(${_NAME} EXCLUDE_FROM_ALL ${_TEST_SRC} $<TARGET_OBJECTS:Main_Object>)
|
||||
if(HIP_PLATFORM STREQUAL "amd")
|
||||
target_link_libraries(${_NAME} hiprtc)
|
||||
else()
|
||||
target_link_libraries(${_NAME} nvrtc)
|
||||
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")
|
||||
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 (DEFINED _COMPILE_OPTIONS)
|
||||
target_compile_options(${_NAME} PUBLIC ${_COMPILE_OPTIONS})
|
||||
endif()
|
||||
|
||||
foreach(arg IN LISTS _UNPARSED_ARGUMENTS)
|
||||
message(WARNING "Unparsed arguments: ${arg}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
|
||||
####### Any changes to this file will be overwritten by the next CMake run ####
|
||||
####### The input file was Catch2Config.cmake.in ########
|
||||
|
||||
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
|
||||
|
||||
macro(set_and_check _var _file)
|
||||
set(${_var} "${_file}")
|
||||
if(NOT EXISTS "${_file}")
|
||||
message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(check_required_components _NAME)
|
||||
foreach(comp ${${_NAME}_FIND_COMPONENTS})
|
||||
if(NOT ${_NAME}_${comp}_FOUND)
|
||||
if(${_NAME}_FIND_REQUIRED_${comp})
|
||||
set(${_NAME}_FOUND FALSE)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
####################################################################################
|
||||
|
||||
|
||||
# Avoid repeatedly including the targets
|
||||
if(NOT TARGET Catch2::Catch2)
|
||||
# Provide path for scripts
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/Catch2Targets.cmake)
|
||||
endif()
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
# This is a basic version file for the Config-mode of find_package().
|
||||
# It is used by write_basic_package_version_file() as input file for configure_file()
|
||||
# to create a version-file which can be installed along a config.cmake file.
|
||||
#
|
||||
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
|
||||
# the requested version string are exactly the same and it sets
|
||||
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version,
|
||||
# but only if the requested major version is the same as the current one.
|
||||
# The variable CVF_VERSION must be set before calling configure_file().
|
||||
|
||||
|
||||
set(PACKAGE_VERSION "2.13.6")
|
||||
|
||||
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
else()
|
||||
|
||||
if("2.13.6" MATCHES "^([0-9]+)\\.")
|
||||
set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
set(CVF_VERSION_MAJOR "2.13.6")
|
||||
endif()
|
||||
|
||||
if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR)
|
||||
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
else()
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
endif()
|
||||
|
||||
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
|
||||
set(PACKAGE_VERSION_EXACT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# if the installed project requested no architecture check, don't perform the check
|
||||
if("FALSE")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "" STREQUAL "")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
|
||||
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "")
|
||||
math(EXPR installedBits " * 8")
|
||||
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
|
||||
set(PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
endif()
|
||||
@@ -0,0 +1,99 @@
|
||||
# Generated by CMake
|
||||
|
||||
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5)
|
||||
message(FATAL_ERROR "CMake >= 2.6.0 required")
|
||||
endif()
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(VERSION 2.6...3.17)
|
||||
#----------------------------------------------------------------
|
||||
# Generated CMake target import file.
|
||||
#----------------------------------------------------------------
|
||||
|
||||
# Commands may need to know the format version.
|
||||
set(CMAKE_IMPORT_FILE_VERSION 1)
|
||||
|
||||
# Protect against multiple inclusion, which would fail when already imported targets are added once more.
|
||||
set(_targetsDefined)
|
||||
set(_targetsNotDefined)
|
||||
set(_expectedTargets)
|
||||
foreach(_expectedTarget Catch2::Catch2)
|
||||
list(APPEND _expectedTargets ${_expectedTarget})
|
||||
if(NOT TARGET ${_expectedTarget})
|
||||
list(APPEND _targetsNotDefined ${_expectedTarget})
|
||||
endif()
|
||||
if(TARGET ${_expectedTarget})
|
||||
list(APPEND _targetsDefined ${_expectedTarget})
|
||||
endif()
|
||||
endforeach()
|
||||
if("${_targetsDefined}" STREQUAL "${_expectedTargets}")
|
||||
unset(_targetsDefined)
|
||||
unset(_targetsNotDefined)
|
||||
unset(_expectedTargets)
|
||||
set(CMAKE_IMPORT_FILE_VERSION)
|
||||
cmake_policy(POP)
|
||||
return()
|
||||
endif()
|
||||
if(NOT "${_targetsDefined}" STREQUAL "")
|
||||
message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n")
|
||||
endif()
|
||||
unset(_targetsDefined)
|
||||
unset(_targetsNotDefined)
|
||||
unset(_expectedTargets)
|
||||
|
||||
|
||||
# Compute the installation prefix relative to this file.
|
||||
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
if(_IMPORT_PREFIX STREQUAL "/")
|
||||
set(_IMPORT_PREFIX "")
|
||||
endif()
|
||||
|
||||
# Create imported target Catch2::Catch2
|
||||
add_library(Catch2::Catch2 INTERFACE IMPORTED)
|
||||
|
||||
set_target_properties(Catch2::Catch2 PROPERTIES
|
||||
INTERFACE_COMPILE_FEATURES "cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_defaulted_functions;cxx_deleted_functions;cxx_final;cxx_lambdas;cxx_noexcept;cxx_override;cxx_range_for;cxx_rvalue_references;cxx_static_assert;cxx_strong_enums;cxx_trailing_return_types;cxx_unicode_literals;cxx_user_literals;cxx_variadic_macros"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
|
||||
)
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.0.0)
|
||||
message(FATAL_ERROR "This file relies on consumers using CMake 3.0.0 or greater.")
|
||||
endif()
|
||||
|
||||
# Load information for each installed configuration.
|
||||
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
file(GLOB CONFIG_FILES "${_DIR}/Catch2Targets-*.cmake")
|
||||
foreach(f ${CONFIG_FILES})
|
||||
include(${f})
|
||||
endforeach()
|
||||
|
||||
# Cleanup temporary variables.
|
||||
set(_IMPORT_PREFIX)
|
||||
|
||||
# Loop over all imported files and verify that they actually exist
|
||||
foreach(target ${_IMPORT_CHECK_TARGETS} )
|
||||
foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )
|
||||
if(NOT EXISTS "${file}" )
|
||||
message(FATAL_ERROR "The imported target \"${target}\" references the file
|
||||
\"${file}\"
|
||||
but this file does not exist. Possible reasons include:
|
||||
* The file was deleted, renamed, or moved to another location.
|
||||
* An install or uninstall procedure did not complete successfully.
|
||||
* The installation package was faulty and contained
|
||||
\"${CMAKE_CURRENT_LIST_FILE}\"
|
||||
but not all the files it references.
|
||||
")
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_IMPORT_CHECK_FILES_FOR_${target})
|
||||
endforeach()
|
||||
unset(_IMPORT_CHECK_TARGETS)
|
||||
|
||||
# This file does not depend on other imported targets which have
|
||||
# been exported from the same project but in a separate export set.
|
||||
|
||||
# Commands beyond this point should not need to know the version.
|
||||
set(CMAKE_IMPORT_FILE_VERSION)
|
||||
cmake_policy(POP)
|
||||
@@ -0,0 +1,138 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
set(prefix "${TEST_PREFIX}")
|
||||
set(suffix "${TEST_SUFFIX}")
|
||||
set(spec ${TEST_SPEC})
|
||||
set(extra_args ${TEST_EXTRA_ARGS})
|
||||
set(properties ${TEST_PROPERTIES})
|
||||
set(reporter ${TEST_REPORTER})
|
||||
set(output_dir ${TEST_OUTPUT_DIR})
|
||||
set(output_prefix ${TEST_OUTPUT_PREFIX})
|
||||
set(output_suffix ${TEST_OUTPUT_SUFFIX})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
|
||||
function(add_command NAME)
|
||||
set(_args "")
|
||||
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
|
||||
math(EXPR _last_arg ${ARGC}-1)
|
||||
foreach(_n RANGE 1 ${_last_arg})
|
||||
set(_arg "${ARGV${_n}}")
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
set(_args "${_args} ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
get_filename_component(TEST_EXECUTABLE ${TEST_EXECUTABLE} ABSOLUTE)
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
|
||||
)
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
|
||||
if(${result} EQUAL 0)
|
||||
message(WARNING
|
||||
"Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
|
||||
)
|
||||
elseif(${result} LESS 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${result}\n"
|
||||
" Output: ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Run test executable to get list of available reporters
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-reporters
|
||||
OUTPUT_VARIABLE reporters_output
|
||||
RESULT_VARIABLE reporters_result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
if(${reporters_result} EQUAL 0)
|
||||
message(WARNING
|
||||
"Test executable '${TEST_EXECUTABLE}' contains no reporters!\n"
|
||||
)
|
||||
elseif(${reporters_result} LESS 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${reporters_result}\n"
|
||||
" Output: ${reporters_output}\n"
|
||||
)
|
||||
endif()
|
||||
string(FIND "${reporters_output}" "${reporter}" reporter_is_valid)
|
||||
if(reporter AND ${reporter_is_valid} EQUAL -1)
|
||||
message(FATAL_ERROR
|
||||
"\"${reporter}\" is not a valid reporter!\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Prepare reporter
|
||||
if(reporter)
|
||||
set(reporter_arg "--reporter ${reporter}")
|
||||
endif()
|
||||
|
||||
# Prepare output dir
|
||||
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
|
||||
set(output_dir "${TEST_WORKING_DIR}/${output_dir}")
|
||||
if(NOT EXISTS ${output_dir})
|
||||
file(MAKE_DIRECTORY ${output_dir})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
set(test ${line})
|
||||
# Escape characters in test case names that would be parsed by Catch2
|
||||
set(test_name ${test})
|
||||
foreach(char , [ ])
|
||||
string(REPLACE ${char} "\\${char}" test_name ${test_name})
|
||||
endforeach(char)
|
||||
# ...add output dir
|
||||
if(output_dir)
|
||||
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean ${test_name})
|
||||
set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}")
|
||||
endif()
|
||||
|
||||
file(RELATIVE_PATH exe_path ${CMAKE_CURRENT_BINARY_DIR} ${TEST_EXECUTABLE})
|
||||
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${exe_path}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
"${reporter_arg}"
|
||||
"${output_dir_arg}"
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
add_command(set ${TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${CTEST_FILE}" "${script}")
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
#==================================================================================================#
|
||||
# supported macros #
|
||||
# - TEST_CASE, #
|
||||
# - TEMPLATE_TEST_CASE #
|
||||
# - SCENARIO, #
|
||||
# - TEST_CASE_METHOD, #
|
||||
# - CATCH_TEST_CASE, #
|
||||
# - CATCH_TEMPLATE_TEST_CASE #
|
||||
# - CATCH_SCENARIO, #
|
||||
# - CATCH_TEST_CASE_METHOD. #
|
||||
# #
|
||||
# Usage #
|
||||
# 1. make sure this module is in the path or add this otherwise: #
|
||||
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") #
|
||||
# 2. make sure that you've enabled testing option for the project by the call: #
|
||||
# enable_testing() #
|
||||
# 3. add the lines to the script for testing target (sample CMakeLists.txt): #
|
||||
# project(testing_target) #
|
||||
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") #
|
||||
# enable_testing() #
|
||||
# #
|
||||
# find_path(CATCH_INCLUDE_DIR "catch.hpp") #
|
||||
# include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) #
|
||||
# #
|
||||
# file(GLOB SOURCE_FILES "*.cpp") #
|
||||
# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) #
|
||||
# #
|
||||
# include(ParseAndAddCatchTests) #
|
||||
# ParseAndAddCatchTests(${PROJECT_NAME}) #
|
||||
# #
|
||||
# The following variables affect the behavior of the script: #
|
||||
# #
|
||||
# PARSE_CATCH_TESTS_VERBOSE (Default OFF) #
|
||||
# -- enables debug messages #
|
||||
# PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) #
|
||||
# -- excludes tests marked with [!hide], [.] or [.foo] tags #
|
||||
# PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) #
|
||||
# -- adds fixture class name to the test name #
|
||||
# PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) #
|
||||
# -- adds cmake target name to the test name #
|
||||
# PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) #
|
||||
# -- causes CMake to rerun when file with tests changes so that new tests will be discovered #
|
||||
# #
|
||||
# One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way #
|
||||
# a test should be run. For instance to use test MPI, one can write #
|
||||
# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) #
|
||||
# just before calling this ParseAndAddCatchTests function #
|
||||
# #
|
||||
# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test #
|
||||
# command. For example, to include successful tests in the output, one can write #
|
||||
# set(AdditionalCatchParameters --success) #
|
||||
# #
|
||||
# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source #
|
||||
# file in the target is set, and contains the list of the tests extracted from that target, or #
|
||||
# from that file. This is useful, for example to add further labels or properties to the tests. #
|
||||
# #
|
||||
#==================================================================================================#
|
||||
|
||||
if (CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8)
|
||||
message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer")
|
||||
endif()
|
||||
|
||||
option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF)
|
||||
option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF)
|
||||
option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON)
|
||||
option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON)
|
||||
option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF)
|
||||
|
||||
function(ParseAndAddCatchTests_PrintDebugMessage)
|
||||
if(PARSE_CATCH_TESTS_VERBOSE)
|
||||
message(STATUS "ParseAndAddCatchTests: ${ARGV}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# This removes the contents between
|
||||
# - block comments (i.e. /* ... */)
|
||||
# - full line comments (i.e. // ... )
|
||||
# contents have been read into '${CppCode}'.
|
||||
# !keep partial line comments
|
||||
function(ParseAndAddCatchTests_RemoveComments CppCode)
|
||||
string(ASCII 2 CMakeBeginBlockComment)
|
||||
string(ASCII 3 CMakeEndBlockComment)
|
||||
string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}")
|
||||
string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}")
|
||||
string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}")
|
||||
string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}")
|
||||
|
||||
set(${CppCode} "${${CppCode}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Worker function
|
||||
function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget)
|
||||
# If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file.
|
||||
if(SourceFile MATCHES "\\\$<TARGET_OBJECTS:.+>")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.")
|
||||
return()
|
||||
endif()
|
||||
# According to CMake docs EXISTS behavior is well-defined only for full paths.
|
||||
get_filename_component(SourceFile ${SourceFile} ABSOLUTE)
|
||||
if(NOT EXISTS ${SourceFile})
|
||||
message(WARNING "Cannot find source file: ${SourceFile}")
|
||||
return()
|
||||
endif()
|
||||
ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}")
|
||||
file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
|
||||
|
||||
# Remove block and fullline comments
|
||||
ParseAndAddCatchTests_RemoveComments(Contents)
|
||||
|
||||
# Find definition of test names
|
||||
# https://regex101.com/r/JygOND/1
|
||||
string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([ \t\n]*\"[^\"]*\"[ \t\n]*(,[ \t\n]*\"[^\"]*\")?(,[ \t\n]*[^\,\)]*)*\\)[ \t\n]*\{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}")
|
||||
|
||||
if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests)
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property")
|
||||
set_property(
|
||||
DIRECTORY
|
||||
APPEND
|
||||
PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile}
|
||||
)
|
||||
endif()
|
||||
|
||||
# check CMP0110 policy for new add_test() behavior
|
||||
if(POLICY CMP0110)
|
||||
cmake_policy(GET CMP0110 _cmp0110_value) # new add_test() behavior
|
||||
else()
|
||||
# just to be thorough explicitly set the variable
|
||||
set(_cmp0110_value)
|
||||
endif()
|
||||
|
||||
foreach(TestName ${Tests})
|
||||
# Strip newlines
|
||||
string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}")
|
||||
|
||||
# Get test type and fixture if applicable
|
||||
string(REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}")
|
||||
string(REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}")
|
||||
string(REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}")
|
||||
|
||||
# Get string parts of test definition
|
||||
string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}")
|
||||
|
||||
# Strip wrapping quotation marks
|
||||
string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}")
|
||||
string(REPLACE "\";\"" ";" TestStrings "${TestStrings}")
|
||||
|
||||
# Validate that a test name and tags have been provided
|
||||
list(LENGTH TestStrings TestStringsLength)
|
||||
if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1)
|
||||
message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}")
|
||||
endif()
|
||||
|
||||
# Assign name and tags
|
||||
list(GET TestStrings 0 Name)
|
||||
if("${TestType}" STREQUAL "SCENARIO")
|
||||
set(Name "Scenario: ${Name}")
|
||||
endif()
|
||||
if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND "${TestType}" MATCHES "(CATCH_)?TEST_CASE_METHOD" AND TestFixture )
|
||||
set(CTestName "${TestFixture}:${Name}")
|
||||
else()
|
||||
set(CTestName "${Name}")
|
||||
endif()
|
||||
if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME)
|
||||
set(CTestName "${TestTarget}:${CTestName}")
|
||||
endif()
|
||||
# add target to labels to enable running all tests added from this target
|
||||
set(Labels ${TestTarget})
|
||||
if(TestStringsLength EQUAL 2)
|
||||
list(GET TestStrings 1 Tags)
|
||||
string(TOLOWER "${Tags}" Tags)
|
||||
# remove target from labels if the test is hidden
|
||||
if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*")
|
||||
list(REMOVE_ITEM Labels ${TestTarget})
|
||||
endif()
|
||||
string(REPLACE "]" ";" Tags "${Tags}")
|
||||
string(REPLACE "[" "" Tags "${Tags}")
|
||||
else()
|
||||
# unset tags variable from previous loop
|
||||
unset(Tags)
|
||||
endif()
|
||||
|
||||
list(APPEND Labels ${Tags})
|
||||
|
||||
set(HiddenTagFound OFF)
|
||||
foreach(label ${Labels})
|
||||
string(REGEX MATCH "^!hide|^\\." result ${label})
|
||||
if(result)
|
||||
set(HiddenTagFound ON)
|
||||
break()
|
||||
endif(result)
|
||||
endforeach(label)
|
||||
if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label")
|
||||
else()
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"")
|
||||
if(Labels)
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}")
|
||||
endif()
|
||||
|
||||
# Escape commas in the test spec
|
||||
string(REPLACE "," "\\," Name ${Name})
|
||||
|
||||
# Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were necessary,
|
||||
# only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1
|
||||
# And properly introduced in 3.19 with the CMP0110 policy
|
||||
if(_cmp0110_value STREQUAL "NEW" OR ${CMAKE_VERSION} VERSION_EQUAL "3.18")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("CMP0110 set to NEW, no need for add_test(\"\") workaround")
|
||||
else()
|
||||
ParseAndAddCatchTests_PrintDebugMessage("CMP0110 set to OLD adding \"\" for add_test() workaround")
|
||||
set(CTestName "\"${CTestName}\"")
|
||||
endif()
|
||||
|
||||
# Handle template test cases
|
||||
if("${TestTypeAndFixture}" MATCHES ".*TEMPLATE_.*")
|
||||
set(Name "${Name} - *")
|
||||
endif()
|
||||
|
||||
# Add the test and set its properties
|
||||
add_test(NAME "${CTestName}" COMMAND ${OptionalCatchTestLauncher} $<TARGET_FILE:${TestTarget}> ${Name} ${AdditionalCatchParameters})
|
||||
# Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead
|
||||
if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property")
|
||||
set_tests_properties("${CTestName}" PROPERTIES DISABLED ON)
|
||||
else()
|
||||
set_tests_properties("${CTestName}" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
|
||||
LABELS "${Labels}")
|
||||
endif()
|
||||
set_property(
|
||||
TARGET ${TestTarget}
|
||||
APPEND
|
||||
PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}")
|
||||
set_property(
|
||||
SOURCE ${SourceFile}
|
||||
APPEND
|
||||
PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}")
|
||||
endif()
|
||||
|
||||
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# entry point
|
||||
function(ParseAndAddCatchTests TestTarget)
|
||||
message(DEPRECATION "ParseAndAddCatchTest: function deprecated because of possibility of missed test cases. Consider using 'catch_discover_tests' from 'Catch.cmake'")
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}")
|
||||
get_target_property(SourceFiles ${TestTarget} SOURCES)
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}")
|
||||
foreach(SourceFile ${SourceFiles})
|
||||
ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget})
|
||||
endforeach()
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}")
|
||||
endfunction()
|
||||
@@ -0,0 +1,34 @@
|
||||
# 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()
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright 2009-2010 Cybozu Labs, Inc.
|
||||
Copyright 2011-2014 Kazuho Oku
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
+1200
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
# 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.
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^Debug$")
|
||||
add_definitions(-DHT_LOG_ENABLE)
|
||||
endif()
|
||||
|
||||
add_library(Main_Object EXCLUDE_FROM_ALL OBJECT main.cc hip_test_context.cc)
|
||||
if(HIP_PLATFORM MATCHES "amd")
|
||||
set_property(TARGET Main_Object PROPERTY CXX_STANDARD 17)
|
||||
else()
|
||||
target_compile_options(Main_Object PUBLIC -std=c++17)
|
||||
endif()
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"DisabledTests":
|
||||
[
|
||||
"Unit_hipStreamPerThread_DeviceReset_1",
|
||||
"Unit_hipMallocManaged_OverSubscription",
|
||||
"Unit_hipDeviceGetPCIBusId_Negative_PartialFill",
|
||||
"Unit_hipInit_Negative"
|
||||
]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"DisabledTests":
|
||||
[
|
||||
"Unit_hipStreamPerThread_DeviceReset_1",
|
||||
"Unit_hipMallocManaged_OverSubscription",
|
||||
"Unit_hipDeviceGetSharedMemConfig_Positive_Basic",
|
||||
"Unit_hipDeviceGetSharedMemConfig_Positive_Threaded",
|
||||
"Unit_hipDeviceGetCacheConfig_Positive_Basic",
|
||||
"Unit_hipDeviceGetCacheConfig_Positive_Threaded",
|
||||
"Unit_hipGetDeviceFlags_Positive_Context",
|
||||
"Unit_hipIpcCloseMemHandle_Negative_Close_In_Originating_Process",
|
||||
"Unit_hipIpcOpenMemHandle_Negative_Open_In_Creating_Process",
|
||||
"Unit_hipDeviceGetPCIBusId_Negative_PartialFill",
|
||||
"Unit_hipInit_Negative",
|
||||
"Unit_hipMemset_Negative_OutOfBoundsPtr",
|
||||
"Unit_hipDeviceReset_Positive_Basic",
|
||||
"Unit_hipDeviceReset_Positive_Threaded"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"DisabledTests":
|
||||
[
|
||||
"Unit_hipMalloc_CoherentTst",
|
||||
"Unit_printf_flags",
|
||||
"Unit_printf_specifier",
|
||||
"Unit_hipTextureMipmapObj2D_Check",
|
||||
"Unit_hipGraphAddHostNode_ClonedGraphwithHostNode",
|
||||
"Unit_hipEventIpc",
|
||||
"Unit_hipMalloc3D_Negative",
|
||||
"Unit_hipPointerGetAttribute_MappedMem",
|
||||
"Unit_hipStreamValue_Write",
|
||||
"Unit_hipMemPoolApi_Basic",
|
||||
"Unit_hipMemPoolApi_BasicAlloc",
|
||||
"Unit_hipMemPoolApi_BasicTrim",
|
||||
"Unit_hipMemPoolApi_BasicReuse",
|
||||
"Unit_hipMemPoolApi_Opportunistic",
|
||||
"Unit_hipMemPoolApi_Default",
|
||||
"Unit_hipDeviceGetUuid",
|
||||
"Unit_hipGraphMemcpyNodeSetParams_Functional",
|
||||
"Unit_hipMalloc3D_ValidatePitch",
|
||||
"Unit_hipArrayCreate_happy",
|
||||
"Unit_hipHostRegister_Negative - int",
|
||||
"Unit_hipHostRegister_Negative - float",
|
||||
"Unit_hipHostRegister_Negative - double",
|
||||
"Unit_hipMemAllocPitch_ValidatePitch",
|
||||
"Unit_hipArrayCreate_happy - int",
|
||||
"Unit_hipArrayCreate_happy - int4",
|
||||
"Unit_hipArrayCreate_happy - short2",
|
||||
"Unit_hipArrayCreate_happy - char",
|
||||
"Unit_hipArrayCreate_happy - char4",
|
||||
"Unit_hipArrayCreate_happy - float",
|
||||
"Unit_hipArrayCreate_happy - float2",
|
||||
"Unit_hipArrayCreate_happy - float4",
|
||||
"Unit_hipMemVmm_Basic",
|
||||
"Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Functional",
|
||||
"Unit_hipMallocManaged_MultiChunkMultiDevice",
|
||||
"Unit_hipMallocManaged_TwoPointers - int",
|
||||
"Unit_hipMallocManaged_TwoPointers - float",
|
||||
"Unit_hipMallocManaged_TwoPointers - double",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - unsigned char",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - int",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - float",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - double",
|
||||
"Unit_hipGraphNodeGetDependentNodes_Functional",
|
||||
"Unit_hipGraphNodeGetDependentNodes_ParamValidation",
|
||||
"Unit_hipGraphNodeGetDependencies_Functional",
|
||||
"Unit_hipGraphNodeGetDependencies_ParamValidation",
|
||||
"Unit_hipMemGetInfo_DifferentMallocSmall",
|
||||
"Unit_hipMemGetInfo_MallocArray - int",
|
||||
"Unit_hipMemGetInfo_MallocArray - int4",
|
||||
"Unit_hipMemGetInfo_MallocArray - char",
|
||||
"Unit_hipMemGetInfo_Malloc3D",
|
||||
"Unit_hipMemGetInfo_Malloc3DArray - char",
|
||||
"Unit_hipMemGetInfo_Malloc3DArray - int",
|
||||
"Unit_hipMemGetInfo_Malloc3DArray - int4",
|
||||
"Unit_hipMemGetInfo_ParaSmall",
|
||||
"Unit_hipMemGetInfo_ParaMultiSmall",
|
||||
"Unit_hipFreeMultiTDev - char",
|
||||
"Unit_hipFreeMultiTDev - int",
|
||||
"Unit_hipFreeMultiTDev - float2",
|
||||
"Unit_hipFreeMultiTDev - float4",
|
||||
"Unit_hipFreeMultiTHost - char",
|
||||
"Unit_hipFreeMultiTHost - int",
|
||||
"Unit_hipFreeMultiTHost - float2",
|
||||
"Unit_hipFreeMultiTHost - float4",
|
||||
"Unit_hipFreeMultiTArray - char",
|
||||
"Unit_hipFreeMultiTArray - int",
|
||||
"Unit_hipFreeMultiTArray - float2",
|
||||
"Unit_hipFreeMultiTArray - float4",
|
||||
"Unit_hipStreamSynchronize_FinishWork",
|
||||
"Unit_hipStreamSynchronize_NullStreamAndStreamPerThread",
|
||||
"Unit_hipMultiThreadDevice_NearZero",
|
||||
"Unit_hipStreamPerThread_DeviceReset_1",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_Gte",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_Eq_1",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_Eq_2",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_And",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_Eq",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_Gte",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_And",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_Nor",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Gte_1",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Gte_2",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Eq_1",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Eq_2",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_And",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_Gte",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_Eq",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_And",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_Nor",
|
||||
"Unit_hipDeviceGetPCIBusId_Negative_PartialFill",
|
||||
"Unit_hipInit_Negative",
|
||||
"Unit_hipGraphAddEventRecordNode_Functional_ElapsedTime",
|
||||
"Unit_hipStreamBeginCapture_captureComplexGraph"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"DisabledTests":
|
||||
[
|
||||
"Unit_hipMalloc_CoherentTst",
|
||||
"Unit_hipTextureMipmapObj2D_Check",
|
||||
"Unit_hipGraphAddHostNode_ClonedGraphwithHostNode",
|
||||
"Unit_hipEventIpc",
|
||||
"Unit_hipMalloc3D_Negative",
|
||||
"Unit_hipMemPoolApi_BasicAlloc",
|
||||
"Unit_hipMemPoolApi_BasicTrim",
|
||||
"Unit_hipMemPoolApi_BasicReuse",
|
||||
"Unit_hipMemPoolApi_Opportunistic",
|
||||
"Unit_hipMalloc3D_ValidatePitch",
|
||||
"Unit_hipMemAllocPitch_ValidatePitch",
|
||||
"Unit_hipGraphExecMemcpyNodeSetParamsToSymbol_Functional",
|
||||
"Unit_hipMallocManaged_OverSubscription",
|
||||
"Unit_hipMallocManaged_CoherentTstWthAdvise",
|
||||
"Unit_hipMallocManaged_Advanced",
|
||||
"Unit_hipMemRangeGetAttribute_TstCountParam",
|
||||
"Unit_hipMemRangeGetAttribute_NegativeTests",
|
||||
"Unit_hipMemRangeGetAttribute_AccessedBy1",
|
||||
"Unit_hipMemRangeGetAttribte_3",
|
||||
"Unit_hipMemRangeGetAttribute_4",
|
||||
"Unit_hipMemRangeGetAttribute_PrefetchAndGtAttr",
|
||||
"Unit_hipMemAdvise_TstFlags",
|
||||
"Unit_hipMemAdvise_PrefrdLoc",
|
||||
"Unit_hipMemAdvise_ReadMostly",
|
||||
"Unit_hipMemAdvise_TstFlgOverrideEffect",
|
||||
"Unit_hipMemAdvise_TstAccessedByFlg",
|
||||
"Unit_hipMemAdvise_TstAccessedByFlg4",
|
||||
"Unit_hipMemAdvise_TstMemAdvisePrefrdLoc",
|
||||
"Unit_hipMemAdvise_TstMemAdviseMultiFlag",
|
||||
"Unit_hipMemAdvise_ReadMosltyMgpuTst",
|
||||
"Unit_hipMemAdvise_TstSetUnsetPrfrdLoc",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - unsigned char",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - int",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - float",
|
||||
"Unit_hipMallocManaged_DeviceContextChange - double",
|
||||
"Unit_hipStreamCreateWithPriority_ValidateWithEvents",
|
||||
"Unit_hipStreamPerThread_StrmWaitEvt",
|
||||
"Unit_hipMemGetInfo_DifferentMallocSmall",
|
||||
"Unit_hipMemGetInfo_MallocArray - int",
|
||||
"Unit_hipMemGetInfo_MallocArray - int4",
|
||||
"Unit_hipMemGetInfo_MallocArray - char",
|
||||
"Unit_hipMemGetInfo_Malloc3D",
|
||||
"Unit_hipMemGetInfo_Malloc3DArray - char",
|
||||
"Unit_hipMemGetInfo_Malloc3DArray - int",
|
||||
"Unit_hipMemGetInfo_Malloc3DArray - int4",
|
||||
"Unit_hipMemGetInfo_ParaSmall",
|
||||
"Unit_hipMemGetInfo_ParaMultiSmall",
|
||||
"Unit_hipMultiThreadDevice_NearZero",
|
||||
"Unit_hipStreamPerThread_DeviceReset_1",
|
||||
"Unit_hipStreamCreateWithPriority_ValidateWithEvents",
|
||||
"Unit_hipStreamPerThread_StrmWaitEvt",
|
||||
"Unit_hipGraphMemcpyNodeSetParamsToSymbol_Functional",
|
||||
"Unit_hipStreamWaitEvent_DifferentStreams",
|
||||
"Unit_hipStreamQuery_WithFinishedWork",
|
||||
"Unit_hipDeviceGetCacheConfig_Positive_Basic",
|
||||
"Unit_hipDeviceGetCacheConfig_Positive_Threaded",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_Gte",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_Eq_1",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_Eq_2",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_Mask_And",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_Eq",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_Gte",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_And",
|
||||
"Unit_hipStreamValue_Wait32_Blocking_NoMask_Nor",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Gte_1",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Gte_2",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Eq_1",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_Eq_2",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_Mask_And",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_Gte",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_Eq",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_And",
|
||||
"Unit_hipStreamValue_Wait64_Blocking_NoMask_Nor",
|
||||
"Unit_hipGetDeviceFlags_Positive_Context",
|
||||
"Unit_hipIpcCloseMemHandle_Negative_Close_In_Originating_Process",
|
||||
"Unit_hipIpcOpenMemHandle_Negative_Open_In_Creating_Process",
|
||||
"Unit_hipDeviceGetPCIBusId_Negative_PartialFill",
|
||||
"Unit_hipDeviceGetSharedMemConfig_Positive_Basic",
|
||||
"Unit_hipDeviceGetSharedMemConfig_Positive_Threaded",
|
||||
"Unit_hipDeviceReset_Positive_Basic",
|
||||
"Unit_hipDeviceReset_Positive_Threaded",
|
||||
"Unit_hipInit_Negative",
|
||||
"Unit_hipGraphMemcpyNodeSetParams_Functional",
|
||||
"Unit_hipGraphNodeGetDependentNodes_Functional",
|
||||
"Unit_hipGraphNodeGetDependencies_Functional",
|
||||
"Unit_hipGraphExecChildGraphNodeSetParams_ChildTopology",
|
||||
"Unit_hipGraphAddEventRecordNode_MultipleRun",
|
||||
"Unit_hipGraphAddEventRecordNode_Functional_ElapsedTime",
|
||||
"Unit_hipStreamBeginCapture_captureComplexGraph",
|
||||
"Note: needs to be enabled when streamPerThread issues are fixed",
|
||||
"Unit_hipStreamSynchronize_NullStreamAndStreamPerThread",
|
||||
"Note: intermittent Seg fault failure ",
|
||||
"Unit_hipGraphAddEventRecordNode_Functional_WithoutFlags"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
#include <cstdlib>
|
||||
#include <hip_test_common.hh>
|
||||
#include <picojson.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
#include "hip_test_context.hh"
|
||||
#include "hip_test_filesystem.hh"
|
||||
|
||||
void TestContext::detectOS() {
|
||||
#if (HT_WIN == 1)
|
||||
p_windows = true;
|
||||
#elif (HT_LINUX == 1)
|
||||
p_linux = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestContext::detectPlatform() {
|
||||
#if (HT_AMD == 1)
|
||||
amd = true;
|
||||
#elif (HT_NVIDIA == 1)
|
||||
nvidia = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string TestContext::substringFound(std::vector<std::string> list, std::string filename) {
|
||||
std::string match = "";
|
||||
for (unsigned int i = 0; i < list.size(); i++) {
|
||||
if (filename.find(list.at(i)) != std::string::npos) {
|
||||
match = list.at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
|
||||
std::string TestContext::getMatchingConfigFile(std::string config_dir) {
|
||||
std::string configFileToUse;
|
||||
for (auto& p : fs::recursive_directory_iterator(config_dir)) {
|
||||
fs::path filename = p.path();
|
||||
std::string cur_arch = "TODO";
|
||||
std::string arch = substringFound(amd_arch_list_, filename.filename().string());
|
||||
std::string platform = substringFound(platform_list_, filename.filename().string());
|
||||
std::string os = substringFound(os_list_, filename.filename().string());
|
||||
std::string common_arch = "common";
|
||||
std::vector<std::string> default_arch_vec {common_arch};
|
||||
std::string common = substringFound(default_arch_vec, filename.filename().string());
|
||||
// if arch found then use that exit from loop
|
||||
if (arch == cur_arch) {
|
||||
configFileToUse = filename.string();
|
||||
break;
|
||||
// match the platform/os and continue to look
|
||||
} else if ((platform == config_.platform) && (os == config_.os || os == "all") &&
|
||||
common == common_arch) { // ensures only common file is returned
|
||||
configFileToUse = filename.string();
|
||||
}
|
||||
}
|
||||
return configFileToUse;
|
||||
}
|
||||
|
||||
|
||||
std::string& TestContext::getCommonJsonFile() {
|
||||
fs::path config_dir = exe_path;
|
||||
config_dir = config_dir.parent_path();
|
||||
int levels = 0;
|
||||
bool configFolderFound = false;
|
||||
std::vector<std::string> configList;
|
||||
std::string configFile;
|
||||
// check a max of 5 levels down the executable path
|
||||
while (levels < 5) {
|
||||
fs::path temp_path = config_dir;
|
||||
temp_path /= "hipTestMain";
|
||||
temp_path /= "config";
|
||||
if (fs::exists(temp_path)) {
|
||||
config_dir = fs::absolute(temp_path);
|
||||
configFolderFound = true;
|
||||
break;
|
||||
} else {
|
||||
config_dir = config_dir.parent_path();
|
||||
levels++;
|
||||
}
|
||||
}
|
||||
|
||||
// get config.json files if config folder.
|
||||
if (configFolderFound) {
|
||||
json_file_ = getMatchingConfigFile(config_dir.string());
|
||||
}
|
||||
return json_file_;
|
||||
}
|
||||
|
||||
|
||||
void TestContext::getConfigFiles() {
|
||||
config_.platform = (amd ? "amd" : (nvidia ? "nvidia" : "unknown"));
|
||||
config_.os = (p_windows ? "windows" : (p_linux ? "linux" : "unknown"));
|
||||
|
||||
if (config_.os == "unknown" || config_.platform == "unknown") {
|
||||
LogPrintf("%s", "Either Config or Os is unknown, this wont end well");
|
||||
abort();
|
||||
}
|
||||
|
||||
std::string env_config = TestContext::getEnvVar("HIP_CATCH_EXCLUDE_FILE");
|
||||
LogPrintf("Env Config file: %s",
|
||||
(!env_config.empty()) ? env_config.c_str() : "Not found, using common config");
|
||||
// HIP_CATCH_EXCLUDE_FILE is set for custom file path
|
||||
if (!env_config.empty()) {
|
||||
if(fs::exists(env_config)) {
|
||||
config_.json_files.push_back(env_config);
|
||||
}
|
||||
} else {
|
||||
// get common json file
|
||||
config_.json_files.push_back(getCommonJsonFile());
|
||||
}
|
||||
|
||||
for (const auto& fl : config_.json_files) {
|
||||
LogPrintf("Config file path: %s", fl.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TestContext::TestContext(int argc, char** argv) {
|
||||
detectOS();
|
||||
detectPlatform();
|
||||
setExePath(argc, argv);
|
||||
getConfigFiles();
|
||||
parseJsonFiles();
|
||||
parseOptions(argc, argv);
|
||||
}
|
||||
|
||||
void TestContext::setExePath(int argc, char** argv) {
|
||||
if (argc == 0) return;
|
||||
fs::path p = std::string(argv[0]);
|
||||
if (p.has_filename()) p.remove_filename();
|
||||
exe_path = p.string();
|
||||
}
|
||||
|
||||
bool TestContext::isWindows() const { return p_windows; }
|
||||
bool TestContext::isLinux() const { return p_linux; }
|
||||
|
||||
bool TestContext::isNvidia() const { return nvidia; }
|
||||
bool TestContext::isAmd() const { return amd; }
|
||||
|
||||
void TestContext::parseOptions(int argc, char** argv) {
|
||||
// Test name is at [1] position
|
||||
if (argc != 2) return;
|
||||
current_test = std::string(argv[1]);
|
||||
}
|
||||
|
||||
bool TestContext::skipTest() const {
|
||||
// Direct Match
|
||||
auto flags = std::regex::ECMAScript;
|
||||
for (const auto& i : skip_test) {
|
||||
auto regex = std::regex(i.c_str(), flags);
|
||||
if (std::regex_match(current_test, regex)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO add test case skip as well
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string TestContext::currentPath() const { return fs::current_path().string(); }
|
||||
|
||||
bool TestContext::parseJsonFiles() {
|
||||
// Check if file exists
|
||||
for (const auto& fl : config_.json_files) {
|
||||
if (!fs::exists(fl)) {
|
||||
LogPrintf("Unable to find the file: %s", fl.c_str());
|
||||
return true;
|
||||
}
|
||||
// Open the file
|
||||
std::ifstream js_file(fl);
|
||||
std::string json_str((std::istreambuf_iterator<char>(js_file)), std::istreambuf_iterator<char>());
|
||||
LogPrintf("Json contents:: %s", json_str.data());
|
||||
|
||||
picojson::value v;
|
||||
std::string err = picojson::parse(v, json_str);
|
||||
if (err.size() > 1) {
|
||||
LogPrintf("Error from PicoJson: %s", err.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!v.is<picojson::object>()) {
|
||||
LogPrintf("%s", "Data in json is not in correct format, it should be an object");
|
||||
return false;
|
||||
}
|
||||
|
||||
const picojson::object& o = v.get<picojson::object>();
|
||||
for (picojson::object::const_iterator i = o.begin(); i != o.end(); ++i) {
|
||||
// Processing for DisabledTests
|
||||
if (i->first == "DisabledTests") {
|
||||
// Value should contain list of values
|
||||
if (!i->second.is<picojson::array>()) return false;
|
||||
|
||||
auto& val = i->second.get<picojson::array>();
|
||||
for (auto ai = val.begin(); ai != val.end(); ai++) {
|
||||
std::string tmp = ai->get<std::string>();
|
||||
std::string newRegexName;
|
||||
for (const auto& c : tmp) {
|
||||
if (c == '*')
|
||||
newRegexName += ".*";
|
||||
else
|
||||
newRegexName += c;
|
||||
}
|
||||
skip_test.insert(newRegexName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestContext::cleanContext() {
|
||||
for (auto& pair : compiledKernels) {
|
||||
hipError_t error = hipModuleUnload(pair.second.module);
|
||||
if (error != hipSuccess) {
|
||||
throw std::runtime_error("Unable to unload rtc module");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestContext::trackRtcState(std::string kernelNameExpression, hipModule_t loadedModule,
|
||||
hipFunction_t kernelFunction) {
|
||||
rtcState state{loadedModule, kernelFunction};
|
||||
compiledKernels[kernelNameExpression] = state;
|
||||
}
|
||||
|
||||
hipFunction_t TestContext::getFunction(const std::string kernelNameExpression) {
|
||||
auto it{compiledKernels.find(kernelNameExpression)};
|
||||
|
||||
if (it != compiledKernels.end()) {
|
||||
return it->second.kernelFunction;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TestContext::addResults(HCResult r) {
|
||||
std::unique_lock<std::mutex> lock(resultMutex);
|
||||
results.push_back(r);
|
||||
if ((!r.conditionsResult) ||
|
||||
((r.result != hipSuccess) && (r.result != hipErrorPeerAccessAlreadyEnabled))) {
|
||||
hasErrorOccured_.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TestContext::finalizeResults() {
|
||||
std::unique_lock<std::mutex> lock(resultMutex);
|
||||
// clear the results whatever happens
|
||||
std::shared_ptr<void> emptyVec(nullptr, [this](auto) { results.clear(); });
|
||||
|
||||
for (const auto& i : results) {
|
||||
INFO("HIP API Result check\n File:: "
|
||||
<< i.file << "\n Line:: " << i.line << "\n API:: " << i.call
|
||||
<< "\n Result:: " << i.result << "\n Result Str:: " << hipGetErrorString(i.result));
|
||||
REQUIRE(((i.result == hipSuccess) || (i.result == hipErrorPeerAccessAlreadyEnabled)));
|
||||
REQUIRE(i.conditionsResult);
|
||||
}
|
||||
hasErrorOccured_.store(false); // Clear the flag
|
||||
}
|
||||
|
||||
bool TestContext::hasErrorOccured() { return hasErrorOccured_.load(); }
|
||||
|
||||
TestContext::~TestContext() {
|
||||
// Show this message when there are unchecked results
|
||||
if (results.size() != 0) {
|
||||
std::cerr << "HIP_CHECK_THREAD_FINALIZE() has not been called after HIP_CHECK_THREAD\n"
|
||||
<< "Please call HIP_CHECK_THREAD_FINALIZE after joining threads\n"
|
||||
<< "There is/are " << results.size() << " unchecked results from threads."
|
||||
<< std::endl;
|
||||
std::abort(); // Crash to bring users attention to this message and avoid accidental passing of
|
||||
// tests without checking for errors
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include <hip_test_common.hh>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
auto& context = TestContext::get(argc, argv);
|
||||
if (context.skipTest()) {
|
||||
// CTest uses this regex to figure out if the test has been skipped
|
||||
std::cout << "HIP_SKIP_THIS_TEST" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
int out = Catch::Session().run(argc, argv);
|
||||
TestContext::get().cleanContext();
|
||||
return out;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
template <class T, size_t N, hipArray_Format Format> struct type_and_size_and_format {
|
||||
using type = T;
|
||||
static constexpr size_t size = N;
|
||||
static constexpr hipArray_Format format = Format;
|
||||
};
|
||||
|
||||
// Create a map of type to scalar type, vector size and scalar type format enum.
|
||||
// This is useful for creating simpler function that depend on the vector size.
|
||||
template <typename T> struct vector_info;
|
||||
template <>
|
||||
struct vector_info<int> : type_and_size_and_format<int, 1, HIP_AD_FORMAT_SIGNED_INT32> {};
|
||||
template <> struct vector_info<float> : type_and_size_and_format<float, 1, HIP_AD_FORMAT_FLOAT> {};
|
||||
template <>
|
||||
struct vector_info<short> : type_and_size_and_format<short, 1, HIP_AD_FORMAT_SIGNED_INT16> {};
|
||||
template <>
|
||||
struct vector_info<char> : type_and_size_and_format<char, 1, HIP_AD_FORMAT_SIGNED_INT8> {};
|
||||
template <>
|
||||
struct vector_info<unsigned int>
|
||||
: type_and_size_and_format<unsigned int, 1, HIP_AD_FORMAT_UNSIGNED_INT32> {};
|
||||
template <>
|
||||
struct vector_info<unsigned short>
|
||||
: type_and_size_and_format<unsigned short, 1, HIP_AD_FORMAT_UNSIGNED_INT16> {};
|
||||
template <>
|
||||
struct vector_info<unsigned char>
|
||||
: type_and_size_and_format<unsigned char, 1, HIP_AD_FORMAT_UNSIGNED_INT8> {};
|
||||
|
||||
template <>
|
||||
struct vector_info<int2> : type_and_size_and_format<int, 2, HIP_AD_FORMAT_SIGNED_INT32> {};
|
||||
template <> struct vector_info<float2> : type_and_size_and_format<float, 2, HIP_AD_FORMAT_FLOAT> {};
|
||||
template <>
|
||||
struct vector_info<short2> : type_and_size_and_format<short, 2, HIP_AD_FORMAT_SIGNED_INT16> {};
|
||||
template <>
|
||||
struct vector_info<char2> : type_and_size_and_format<char, 2, HIP_AD_FORMAT_SIGNED_INT8> {};
|
||||
template <>
|
||||
struct vector_info<uint2>
|
||||
: type_and_size_and_format<unsigned int, 2, HIP_AD_FORMAT_UNSIGNED_INT32> {};
|
||||
template <>
|
||||
struct vector_info<ushort2>
|
||||
: type_and_size_and_format<unsigned short, 2, HIP_AD_FORMAT_UNSIGNED_INT16> {};
|
||||
template <>
|
||||
struct vector_info<uchar2>
|
||||
: type_and_size_and_format<unsigned char, 2, HIP_AD_FORMAT_UNSIGNED_INT8> {};
|
||||
|
||||
template <>
|
||||
struct vector_info<int4> : type_and_size_and_format<int, 4, HIP_AD_FORMAT_SIGNED_INT32> {};
|
||||
template <> struct vector_info<float4> : type_and_size_and_format<float, 4, HIP_AD_FORMAT_FLOAT> {};
|
||||
template <>
|
||||
struct vector_info<short4> : type_and_size_and_format<short, 4, HIP_AD_FORMAT_SIGNED_INT16> {};
|
||||
template <>
|
||||
struct vector_info<char4> : type_and_size_and_format<char, 4, HIP_AD_FORMAT_SIGNED_INT8> {};
|
||||
template <>
|
||||
struct vector_info<uint4>
|
||||
: type_and_size_and_format<unsigned int, 4, HIP_AD_FORMAT_UNSIGNED_INT32> {};
|
||||
template <>
|
||||
struct vector_info<ushort4>
|
||||
: type_and_size_and_format<unsigned short, 4, HIP_AD_FORMAT_UNSIGNED_INT16> {};
|
||||
template <>
|
||||
struct vector_info<uchar4>
|
||||
: type_and_size_and_format<unsigned char, 4, HIP_AD_FORMAT_UNSIGNED_INT8> {};
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
Copyright (c) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "hip_test_common.hh"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <type_traits>
|
||||
#define TOL 0.001
|
||||
#define guarantee(cond, str) \
|
||||
{ \
|
||||
if (!(cond)) { \
|
||||
INFO("guarantee failed: " << str); \
|
||||
abort(); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
namespace HipTest {
|
||||
template <typename T>
|
||||
size_t checkVectors(T* A, T* B, T* Out, size_t N, T (*F)(T a, T b), bool expectMatch = true,
|
||||
bool reportMismatch = true) {
|
||||
size_t mismatchCount = 0;
|
||||
size_t firstMismatch = 0;
|
||||
size_t mismatchesToPrint = 10;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
T expected = F(A[i], B[i]);
|
||||
if (std::fabs(Out[i] - expected) > TOL) {
|
||||
if (mismatchCount == 0) {
|
||||
firstMismatch = i;
|
||||
}
|
||||
mismatchCount++;
|
||||
if ((mismatchCount <= mismatchesToPrint) && expectMatch) {
|
||||
INFO("Mismatch at " << i << " Computed: " << Out[i] << " Expeted: " << expected);
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reportMismatch) {
|
||||
if (expectMatch) {
|
||||
if (mismatchCount) {
|
||||
INFO(mismatchCount << " Mismatches First Mismatch at index : " << firstMismatch);
|
||||
REQUIRE(false);
|
||||
}
|
||||
} else {
|
||||
if (mismatchCount == 0) {
|
||||
INFO("Expected Mismatch but not found any");
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mismatchCount;
|
||||
}
|
||||
template <typename T> // pointer type
|
||||
bool checkArray(T* hData, T* hOutputData, size_t width, size_t height, size_t depth = 1) {
|
||||
for (size_t i = 0; i < depth; i++) {
|
||||
for (size_t j = 0; j < height; j++) {
|
||||
for (size_t k = 0; k < width; k++) {
|
||||
int offset = i * width * height + j * width + k;
|
||||
if (hData[offset] != hOutputData[offset]) {
|
||||
INFO("Mismatch at [" << i << "," << j << "," << k << "]:" << hData[offset] << "----"
|
||||
<< hOutputData[offset]);
|
||||
CHECK(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t checkVectorADD(T* A_h, T* B_h, T* result_H, size_t N, bool expectMatch = true,
|
||||
bool reportMismatch = true) {
|
||||
return checkVectors<T>(
|
||||
A_h, B_h, result_H, N, [](T a, T b) { return a + b; }, expectMatch, reportMismatch);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t checkVectorSUB(T* A_h, T* B_h, T* result_H, size_t N, bool expectMatch = true,
|
||||
bool reportMismatch = true) {
|
||||
return checkVectors<T>(
|
||||
A_h, B_h, result_H, N, [](T a, T b) { return a - b; }, expectMatch, reportMismatch);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void checkTest(T* expected_H, T* result_H, size_t N, bool expectMatch = true) {
|
||||
checkVectors<T>(
|
||||
expected_H, expected_H, result_H, N,
|
||||
[](T a, T b) {
|
||||
guarantee(a == b, "Both values should be equal");
|
||||
return a;
|
||||
},
|
||||
expectMatch);
|
||||
}
|
||||
|
||||
|
||||
// Setters and Memory Management
|
||||
|
||||
template <typename T> void setDefaultData(size_t numElements, T* A_h, T* B_h, T* C_h) {
|
||||
// Initialize the host data:
|
||||
|
||||
for (size_t i = 0; i < numElements; i++) {
|
||||
if (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value) {
|
||||
if (A_h) A_h[i] = 3;
|
||||
if (B_h) B_h[i] = 4;
|
||||
if (C_h) C_h[i] = 5;
|
||||
} else if (std::is_same<T, char>::value || std::is_same<T, unsigned char>::value) {
|
||||
if (A_h) A_h[i] = 'a';
|
||||
if (B_h) B_h[i] = 'b';
|
||||
if (C_h) C_h[i] = 'c';
|
||||
} else {
|
||||
if (A_h) A_h[i] = 3.146f + i;
|
||||
if (B_h) B_h[i] = 1.618f + i;
|
||||
if (C_h) C_h[i] = 1.4f + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool initArraysForHost(T** A_h, T** B_h, T** C_h, size_t N, bool usePinnedHost = false) {
|
||||
size_t Nbytes = N * sizeof(T);
|
||||
|
||||
if (usePinnedHost) {
|
||||
if (A_h) {
|
||||
HIP_CHECK(hipHostMalloc((void**)A_h, Nbytes));
|
||||
}
|
||||
if (B_h) {
|
||||
HIP_CHECK(hipHostMalloc((void**)B_h, Nbytes));
|
||||
}
|
||||
if (C_h) {
|
||||
HIP_CHECK(hipHostMalloc((void**)C_h, Nbytes));
|
||||
}
|
||||
} else {
|
||||
if (A_h) {
|
||||
*A_h = (T*)malloc(Nbytes);
|
||||
REQUIRE(*A_h != nullptr);
|
||||
}
|
||||
|
||||
if (B_h) {
|
||||
*B_h = (T*)malloc(Nbytes);
|
||||
REQUIRE(*B_h != nullptr);
|
||||
}
|
||||
|
||||
if (C_h) {
|
||||
*C_h = (T*)malloc(Nbytes);
|
||||
REQUIRE(*C_h != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
setDefaultData(N, A_h ? *A_h : nullptr, B_h ? *B_h : nullptr, C_h ? *C_h : nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool initArrays(T** A_d, T** B_d, T** C_d, T** A_h, T** B_h, T** C_h, size_t N,
|
||||
bool usePinnedHost = false) {
|
||||
size_t Nbytes = N * sizeof(T);
|
||||
|
||||
if (A_d) {
|
||||
HIP_CHECK(hipMalloc(A_d, Nbytes));
|
||||
}
|
||||
if (B_d) {
|
||||
HIP_CHECK(hipMalloc(B_d, Nbytes));
|
||||
}
|
||||
if (C_d) {
|
||||
HIP_CHECK(hipMalloc(C_d, Nbytes));
|
||||
}
|
||||
|
||||
return initArraysForHost(A_h, B_h, C_h, N, usePinnedHost);
|
||||
}
|
||||
|
||||
// Threaded version of setDefaultData to be called from multi thread tests
|
||||
// Call HIP_CHECK_THREAD_FINALIZE after joining
|
||||
template <typename T> void setDefaultDataT(size_t numElements, T* A_h, T* B_h, T* C_h) {
|
||||
// Initialize the host data:
|
||||
|
||||
for (size_t i = 0; i < numElements; i++) {
|
||||
if (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value) {
|
||||
if (A_h) A_h[i] = 3;
|
||||
if (B_h) B_h[i] = 4;
|
||||
if (C_h) C_h[i] = 5;
|
||||
} else if (std::is_same<T, char>::value || std::is_same<T, unsigned char>::value) {
|
||||
if (A_h) A_h[i] = 'a';
|
||||
if (B_h) B_h[i] = 'b';
|
||||
if (C_h) C_h[i] = 'c';
|
||||
} else {
|
||||
if (A_h) A_h[i] = 3.146f + i;
|
||||
if (B_h) B_h[i] = 1.618f + i;
|
||||
if (C_h) C_h[i] = 1.4f + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Threaded version of initArraysForHost to be called from multi thread tests
|
||||
// Call HIP_CHECK_THREAD_FINALIZE after joining
|
||||
template <typename T>
|
||||
void initArraysForHostT(T** A_h, T** B_h, T** C_h, size_t N, bool usePinnedHost = false) {
|
||||
size_t Nbytes = N * sizeof(T);
|
||||
|
||||
if (usePinnedHost) {
|
||||
if (A_h) {
|
||||
HIP_CHECK_THREAD(hipHostMalloc((void**)A_h, Nbytes));
|
||||
}
|
||||
if (B_h) {
|
||||
HIP_CHECK_THREAD(hipHostMalloc((void**)B_h, Nbytes));
|
||||
}
|
||||
if (C_h) {
|
||||
HIP_CHECK_THREAD(hipHostMalloc((void**)C_h, Nbytes));
|
||||
}
|
||||
} else {
|
||||
if (A_h) {
|
||||
*A_h = (T*)malloc(Nbytes);
|
||||
REQUIRE_THREAD(*A_h != nullptr);
|
||||
}
|
||||
|
||||
if (B_h) {
|
||||
*B_h = (T*)malloc(Nbytes);
|
||||
REQUIRE_THREAD(*B_h != nullptr);
|
||||
}
|
||||
|
||||
if (C_h) {
|
||||
*C_h = (T*)malloc(Nbytes);
|
||||
REQUIRE_THREAD(*C_h != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
setDefaultDataT(N, A_h ? *A_h : nullptr, B_h ? *B_h : nullptr, C_h ? *C_h : nullptr);
|
||||
}
|
||||
|
||||
// Threaded version of initArrays to be called from multi thread tests
|
||||
// Call HIP_CHECK_THREAD_FINALIZE after joining
|
||||
template <typename T>
|
||||
void initArraysT(T** A_d, T** B_d, T** C_d, T** A_h, T** B_h, T** C_h, size_t N,
|
||||
bool usePinnedHost = false) {
|
||||
size_t Nbytes = N * sizeof(T);
|
||||
|
||||
if (A_d) {
|
||||
HIP_CHECK_THREAD(hipMalloc(A_d, Nbytes));
|
||||
}
|
||||
if (B_d) {
|
||||
HIP_CHECK_THREAD(hipMalloc(B_d, Nbytes));
|
||||
}
|
||||
if (C_d) {
|
||||
HIP_CHECK_THREAD(hipMalloc(C_d, Nbytes));
|
||||
}
|
||||
|
||||
initArraysForHostT(A_h, B_h, C_h, N, usePinnedHost);
|
||||
}
|
||||
|
||||
// Threaded version of freeArraysForHost to be called from multi thread tests
|
||||
// Call HIP_CHECK_THREAD_FINALIZE after joining
|
||||
template <typename T> void freeArraysForHostT(T* A_h, T* B_h, T* C_h, bool usePinnedHost) {
|
||||
if (usePinnedHost) {
|
||||
if (A_h) {
|
||||
HIP_CHECK_THREAD(hipHostFree(A_h));
|
||||
}
|
||||
if (B_h) {
|
||||
HIP_CHECK_THREAD(hipHostFree(B_h));
|
||||
}
|
||||
if (C_h) {
|
||||
HIP_CHECK_THREAD(hipHostFree(C_h));
|
||||
}
|
||||
} else {
|
||||
if (A_h) {
|
||||
free(A_h);
|
||||
}
|
||||
if (B_h) {
|
||||
free(B_h);
|
||||
}
|
||||
if (C_h) {
|
||||
free(C_h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> bool freeArraysForHost(T* A_h, T* B_h, T* C_h, bool usePinnedHost) {
|
||||
if (usePinnedHost) {
|
||||
if (A_h) {
|
||||
HIP_CHECK(hipHostFree(A_h));
|
||||
}
|
||||
if (B_h) {
|
||||
HIP_CHECK(hipHostFree(B_h));
|
||||
}
|
||||
if (C_h) {
|
||||
HIP_CHECK(hipHostFree(C_h));
|
||||
}
|
||||
} else {
|
||||
if (A_h) {
|
||||
free(A_h);
|
||||
}
|
||||
if (B_h) {
|
||||
free(B_h);
|
||||
}
|
||||
if (C_h) {
|
||||
free(C_h);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void freeArraysT(T* A_d, T* B_d, T* C_d, T* A_h, T* B_h, T* C_h, bool usePinnedHost) {
|
||||
if (A_d) {
|
||||
HIP_CHECK_THREAD(hipFree(A_d));
|
||||
}
|
||||
if (B_d) {
|
||||
HIP_CHECK_THREAD(hipFree(B_d));
|
||||
}
|
||||
if (C_d) {
|
||||
HIP_CHECK_THREAD(hipFree(C_d));
|
||||
}
|
||||
|
||||
freeArraysForHostT(A_h, B_h, C_h, usePinnedHost);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool freeArrays(T* A_d, T* B_d, T* C_d, T* A_h, T* B_h, T* C_h, bool usePinnedHost) {
|
||||
if (A_d) {
|
||||
HIP_CHECK(hipFree(A_d));
|
||||
}
|
||||
if (B_d) {
|
||||
HIP_CHECK(hipFree(B_d));
|
||||
}
|
||||
if (C_d) {
|
||||
HIP_CHECK(hipFree(C_d));
|
||||
}
|
||||
|
||||
return freeArraysForHost(A_h, B_h, C_h, usePinnedHost);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool assemblyFile_Verification(std::string assemfilename, std::string inst) {
|
||||
std::string filePath = "./catch/unit/deviceLib/";
|
||||
bool result = false;
|
||||
std::string filename;
|
||||
filename = filePath + assemfilename;
|
||||
std::ifstream file(filename.c_str(), std::ios::out);
|
||||
if (file) {
|
||||
std::string line;
|
||||
int line_pos = 0, start_pos = 0;
|
||||
int last_pos = 0;
|
||||
int start_match = 0;
|
||||
while (getline(file, line)) {
|
||||
line_pos++;
|
||||
if ((std::is_same<T, float>::value)) {
|
||||
if (!start_pos && std::regex_search(line, std::regex("Begin function (.*)AtomicCheck"))) {
|
||||
start_pos = line_pos;
|
||||
}
|
||||
if (!last_pos && std::regex_search(line, std::regex(".Lfunc_end0-(.*)AtomicCheck"))) {
|
||||
last_pos = line_pos;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((start_match != 2) &&
|
||||
std::regex_search(line, std::regex("Begin function (.*)AtomicCheck"))) {
|
||||
start_match++;
|
||||
if (start_match == 2) start_pos = line_pos;
|
||||
}
|
||||
if (!last_pos && std::regex_search(line, std::regex("func_end1-(.*)AtomicCheck"))) {
|
||||
last_pos = line_pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start_pos) {
|
||||
result = std::regex_search(line, std::regex(inst));
|
||||
if (result) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
SUCCEED("Assembly file does not exist");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace HipTest
|
||||
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
Copyright (c) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "hip_test_context.hh"
|
||||
#include <catch.hpp>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <cstdlib>
|
||||
|
||||
#define HIP_PRINT_STATUS(status) INFO(hipGetErrorName(status) << " at line: " << __LINE__);
|
||||
|
||||
// Not thread-safe
|
||||
#define HIP_CHECK(error) \
|
||||
{ \
|
||||
hipError_t localError = error; \
|
||||
if ((localError != hipSuccess) && (localError != hipErrorPeerAccessAlreadyEnabled)) { \
|
||||
INFO("Error: " << hipGetErrorString(localError) << "\n Code: " << localError \
|
||||
<< "\n Str: " << #error << "\n In File: " << __FILE__ \
|
||||
<< "\n At line: " << __LINE__); \
|
||||
REQUIRE(false); \
|
||||
} \
|
||||
}
|
||||
|
||||
// Threaded HIP_CHECKs
|
||||
#define HIP_CHECK_THREAD(error) \
|
||||
{ \
|
||||
/*To see if error has occured in previous threads, stop execution */ \
|
||||
if (TestContext::get().hasErrorOccured() == true) { \
|
||||
return; /*This will only work with std::thread and not with std::async*/ \
|
||||
} \
|
||||
auto localError = error; \
|
||||
HCResult result(__LINE__, __FILE__, localError, #error); \
|
||||
TestContext::get().addResults(result); \
|
||||
}
|
||||
|
||||
#define REQUIRE_THREAD(condition) \
|
||||
{ \
|
||||
/*To see if error has occured in previous threads, stop execution */ \
|
||||
if (TestContext::get().hasErrorOccured() == true) { \
|
||||
return; /*This will only work with std::thread and not with std::async*/ \
|
||||
} \
|
||||
auto localResult = (condition); \
|
||||
HCResult result(__LINE__, __FILE__, hipSuccess, #condition, localResult); \
|
||||
TestContext::get().addResults(result); \
|
||||
}
|
||||
|
||||
// Do not call before all threads have joined
|
||||
#define HIP_CHECK_THREAD_FINALIZE() \
|
||||
{ TestContext::get().finalizeResults(); }
|
||||
|
||||
|
||||
// Check that an expression, errorExpr, evaluates to the expected error_t, expectedError.
|
||||
#define HIP_CHECK_ERROR(errorExpr, expectedError) \
|
||||
{ \
|
||||
hipError_t localError = errorExpr; \
|
||||
INFO("Matching Errors: " \
|
||||
<< "\n Expected Error: " << hipGetErrorString(expectedError) \
|
||||
<< "\n Expected Code: " << expectedError << '\n' \
|
||||
<< " Actual Error: " << hipGetErrorString(localError) \
|
||||
<< "\n Actual Code: " << localError << "\nStr: " << #errorExpr \
|
||||
<< "\n In File: " << __FILE__ << "\n At line: " << __LINE__); \
|
||||
REQUIRE(localError == expectedError); \
|
||||
}
|
||||
|
||||
// Not thread-safe
|
||||
#define HIPRTC_CHECK(error) \
|
||||
{ \
|
||||
auto localError = error; \
|
||||
if (localError != HIPRTC_SUCCESS) { \
|
||||
INFO("Error: " << hiprtcGetErrorString(localError) << "\n Code: " << localError \
|
||||
<< "\n Str: " << #error << "\n In File: " << __FILE__ \
|
||||
<< "\n At line: " << __LINE__); \
|
||||
REQUIRE(false); \
|
||||
} \
|
||||
}
|
||||
|
||||
// Although its assert, it will be evaluated at runtime
|
||||
#define HIP_ASSERT(x) \
|
||||
{ REQUIRE((x)); }
|
||||
|
||||
#define HIPCHECK(error) \
|
||||
{ \
|
||||
hipError_t localError = error; \
|
||||
if ((localError != hipSuccess) && (localError != hipErrorPeerAccessAlreadyEnabled)) { \
|
||||
printf("error: '%s'(%d) from %s at %s:%d\n", hipGetErrorString(localError), localError, \
|
||||
#error, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define HIPASSERT(condition) \
|
||||
if (!(condition)) { \
|
||||
printf("assertion %s at %s:%d \n", #condition, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
#if HT_NVIDIA
|
||||
#define CTX_CREATE() \
|
||||
hipCtx_t context; \
|
||||
initHipCtx(&context);
|
||||
#define CTX_DESTROY() HIPCHECK(hipCtxDestroy(context));
|
||||
#define ARRAY_DESTROY(array) HIPCHECK(hipArrayDestroy(array));
|
||||
#define HIP_TEX_REFERENCE hipTexRef
|
||||
#define HIP_ARRAY hiparray
|
||||
static void initHipCtx(hipCtx_t* pcontext) {
|
||||
HIPCHECK(hipInit(0));
|
||||
hipDevice_t device;
|
||||
HIPCHECK(hipDeviceGet(&device, 0));
|
||||
HIPCHECK(hipCtxCreate(pcontext, 0, device));
|
||||
}
|
||||
#else
|
||||
#define CTX_CREATE()
|
||||
#define CTX_DESTROY()
|
||||
#define ARRAY_DESTROY(array) HIPCHECK(hipFreeArray(array));
|
||||
#define HIP_TEX_REFERENCE textureReference*
|
||||
#define HIP_ARRAY hipArray*
|
||||
#endif
|
||||
|
||||
|
||||
// Utility Functions
|
||||
namespace HipTest {
|
||||
static inline int getDeviceCount() {
|
||||
int dev = 0;
|
||||
HIP_CHECK(hipGetDeviceCount(&dev));
|
||||
return dev;
|
||||
}
|
||||
|
||||
// Returns the current system time in microseconds
|
||||
static inline long long get_time() {
|
||||
return std::chrono::high_resolution_clock::now().time_since_epoch() /
|
||||
std::chrono::microseconds(1);
|
||||
}
|
||||
|
||||
static inline double elapsed_time(long long startTimeUs, long long stopTimeUs) {
|
||||
return ((double)(stopTimeUs - startTimeUs)) / ((double)(1000));
|
||||
}
|
||||
|
||||
static inline unsigned setNumBlocks(unsigned blocksPerCU, unsigned threadsPerBlock, size_t N) {
|
||||
int device{0};
|
||||
HIP_CHECK(hipGetDevice(&device));
|
||||
hipDeviceProp_t props{};
|
||||
HIP_CHECK(hipGetDeviceProperties(&props, device));
|
||||
|
||||
unsigned blocks = props.multiProcessorCount * blocksPerCU;
|
||||
if (blocks * threadsPerBlock < N) {
|
||||
blocks = (N + threadsPerBlock - 1) / threadsPerBlock;
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
// Threaded version of setNumBlocks - to be used in multi threaded test
|
||||
// Why? because catch2 does not support multithreaded macro calls
|
||||
// Make sure you call HIP_CHECK_THREAD_FINALIZE after your threads join
|
||||
// Also you can not return in threaded functions, due to how HIP_CHECK_THREAD works
|
||||
static inline void setNumBlocksThread(unsigned blocksPerCU, unsigned threadsPerBlock, size_t N,
|
||||
unsigned& blocks) {
|
||||
int device{0};
|
||||
blocks = 0; // incase error has occured in some other thread and the next call might not execute,
|
||||
// we set the blocks size to 0
|
||||
HIP_CHECK_THREAD(hipGetDevice(&device));
|
||||
hipDeviceProp_t props{};
|
||||
HIP_CHECK_THREAD(hipGetDeviceProperties(&props, device));
|
||||
|
||||
blocks = props.multiProcessorCount * blocksPerCU;
|
||||
if (blocks * threadsPerBlock > N) {
|
||||
blocks = (N + threadsPerBlock - 1) / threadsPerBlock;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int RAND_R(unsigned* rand_seed) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
srand(*rand_seed);
|
||||
return rand();
|
||||
#else
|
||||
return rand_r(rand_seed);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool isImageSupported() {
|
||||
int imageSupport = 1;
|
||||
#if HT_AMD
|
||||
int device;
|
||||
HIP_CHECK(hipGetDevice(&device));
|
||||
HIPCHECK(hipDeviceGetAttribute(&imageSupport, hipDeviceAttributeImageSupport, device));
|
||||
#endif
|
||||
return imageSupport != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the test to stop and be skipped at runtime.
|
||||
* reason: Message describing the reason the test has been skipped.
|
||||
*/
|
||||
static inline void HIP_SKIP_TEST(char const* const reason) noexcept {
|
||||
// ctest is setup to parse for "HIP_SKIP_THIS_TEST", at which point it will skip the test.
|
||||
std::cout << "Skipping test. Reason: " << reason << '\n' << "HIP_SKIP_THIS_TEST" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper template that returns the expected arguments of a kernel.
|
||||
*
|
||||
* @return constexpr std::tuple<FArgs...> the expected arguments of the kernel.
|
||||
*/
|
||||
template <typename... FArgs> std::tuple<FArgs...> getExpectedArgs(void(FArgs...)){};
|
||||
|
||||
/**
|
||||
* @brief Asserts that the types of the arguments of a function match exactly with the types in the
|
||||
* function signature.
|
||||
* This is necessary because HIP RTC does not do implicit casting of the kernel
|
||||
* parameters.
|
||||
* In order to get the kernel function signature, this function should only called when
|
||||
* RTC is disabled.
|
||||
*
|
||||
* @tparam F the kernel function
|
||||
* @tparam Args the parameters that will be passed to the kernel.
|
||||
*/
|
||||
template <typename F, typename... Args> void validateArguments(F f, Args...) {
|
||||
using expectedArgsTuple = decltype(getExpectedArgs(f));
|
||||
static_assert(std::is_same<expectedArgsTuple, std::tuple<Args...>>::value,
|
||||
"Kernel arguments types must match exactly!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Launch a kernel using either HIP or HIP RTC.
|
||||
*
|
||||
* @tparam Typenames A list of typenames used by the kernel (unused if the kernel is not a
|
||||
* template).
|
||||
* @tparam K The kernel type. Expects a function or template when RTC is disabled. Expects a
|
||||
* function pointer instead when RTC is enabled.
|
||||
* @tparam Dim Can be either dim3 or int.
|
||||
* @tparam Args A list of kernel arguments to be forwarded.
|
||||
* @param kernel The kernel to be launched (defined in kernels.hh)
|
||||
* @param numBlocks
|
||||
* @param numThreads
|
||||
* @param memPerBlock
|
||||
* @param stream
|
||||
* @param packedArgs A list of kernel arguments to be forwarded.
|
||||
*/
|
||||
template <typename... Typenames, typename K, typename Dim, typename... Args>
|
||||
void launchKernel(K kernel, Dim numBlocks, Dim numThreads, std::uint32_t memPerBlock,
|
||||
hipStream_t stream, Args&&... packedArgs) {
|
||||
#ifndef RTC_TESTING
|
||||
validateArguments(kernel, packedArgs...);
|
||||
kernel<<<numBlocks, numThreads, memPerBlock, stream>>>(std::forward<Args>(packedArgs)...);
|
||||
#else
|
||||
launchRTCKernel<Typenames...>(kernel, numBlocks, numThreads, memPerBlock, stream,
|
||||
std::forward<Args>(packedArgs)...);
|
||||
#endif
|
||||
HIP_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
//---
|
||||
struct Pinned {
|
||||
static const bool isPinned = true;
|
||||
static const char* str() { return "Pinned"; };
|
||||
|
||||
static void* Alloc(size_t sizeBytes) {
|
||||
void* p;
|
||||
HIPCHECK(hipHostMalloc((void**)&p, sizeBytes));
|
||||
return p;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//---
|
||||
struct Unpinned {
|
||||
static const bool isPinned = false;
|
||||
static const char* str() { return "Unpinned"; };
|
||||
|
||||
static void* Alloc(size_t sizeBytes) {
|
||||
void* p = malloc(sizeBytes);
|
||||
HIPASSERT(p);
|
||||
return p;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
struct Memcpy {
|
||||
static const char* str() { return "Memcpy"; };
|
||||
};
|
||||
|
||||
struct MemcpyAsync {
|
||||
static const char* str() { return "MemcpyAsync"; };
|
||||
};
|
||||
|
||||
|
||||
template <typename C> struct MemTraits;
|
||||
|
||||
|
||||
template <> struct MemTraits<Memcpy> {
|
||||
static void Copy(void* dest, const void* src, size_t sizeBytes, hipMemcpyKind kind,
|
||||
hipStream_t stream) {
|
||||
(void)stream;
|
||||
HIPCHECK(hipMemcpy(dest, src, sizeBytes, kind));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <> struct MemTraits<MemcpyAsync> {
|
||||
static void Copy(void* dest, const void* src, size_t sizeBytes, hipMemcpyKind kind,
|
||||
hipStream_t stream) {
|
||||
HIPCHECK(hipMemcpyAsync(dest, src, sizeBytes, kind, stream));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace {
|
||||
static __global__ void waitKernel(clock_t offset) {
|
||||
auto start = clock();
|
||||
while ((clock() - start) < offset) {
|
||||
}
|
||||
}
|
||||
|
||||
// helper function used to set the device frequency variable
|
||||
// estimates the number of clock ticks in 1 second
|
||||
static size_t findTicksPerSecond() {
|
||||
// first read the reported clockRate as a starting point
|
||||
hipDeviceProp_t prop;
|
||||
int device;
|
||||
HIP_CHECK(hipGetDevice(&device));
|
||||
HIP_CHECK(hipGetDeviceProperties(&prop, device));
|
||||
clock_t devFreq = static_cast<clock_t>(prop.clockRate); // in kHz
|
||||
clock_t clockTicksPerSecond = devFreq * 1000;
|
||||
|
||||
// init
|
||||
hipEvent_t start, stop;
|
||||
HIP_CHECK(hipEventCreate(&start));
|
||||
HIP_CHECK(hipEventCreate(&stop));
|
||||
|
||||
// Warmup
|
||||
hipLaunchKernelGGL(waitKernel, dim3(1), dim3(1), 0, 0, clockTicksPerSecond);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
|
||||
// try 10 times to find device frequency
|
||||
// after 10 attempts the result is likely good enough so just accept it
|
||||
for (int attempts = 10; attempts > 0; --attempts) {
|
||||
HIP_CHECK(hipEventRecord(start));
|
||||
hipLaunchKernelGGL(waitKernel, dim3(1), dim3(1), 0, 0, clockTicksPerSecond);
|
||||
HIP_CHECK(hipEventRecord(stop));
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipEventSynchronize(stop));
|
||||
|
||||
float executionTimeMs = 0;
|
||||
HIP_CHECK(hipEventElapsedTime(&executionTimeMs, start, stop));
|
||||
|
||||
constexpr float tolerance = 20;
|
||||
if (fabs(executionTimeMs - 1000) <= tolerance) {
|
||||
// Timing is within accepted tolerance, break here
|
||||
break;
|
||||
} else {
|
||||
clockTicksPerSecond = (clockTicksPerSecond * 1000) / executionTimeMs;
|
||||
--attempts;
|
||||
}
|
||||
}
|
||||
|
||||
// deinit
|
||||
HIP_CHECK(hipEventDestroy(start));
|
||||
HIP_CHECK(hipEventDestroy(stop));
|
||||
return clockTicksPerSecond;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Launches a kernel which runs for specified amount of time
|
||||
// Note: The current implementation uses HIP_CHECK which is not thread safe!
|
||||
// Note: the function assumes execution on a single device and caches the number of clock ticks per
|
||||
// second
|
||||
static inline void runKernelForDuration(std::chrono::milliseconds duration,
|
||||
hipStream_t stream = nullptr) {
|
||||
// number of clocks the device is running at (device frequency)
|
||||
// each translation unit will have a copy of ticksPerSecond but this function isn't designed for
|
||||
// precision so that's acceptable.
|
||||
static size_t ticksPerSecond = findTicksPerSecond();
|
||||
const auto millis = duration.count();
|
||||
hipLaunchKernelGGL(waitKernel, dim3(1), dim3(1), 0, stream, ticksPerSecond * millis / 1000);
|
||||
}
|
||||
|
||||
} // namespace HipTest
|
||||
|
||||
// This must be called in the beginning of image test app's main() to indicate whether image
|
||||
// is supported.
|
||||
#define CHECK_IMAGE_SUPPORT \
|
||||
if (!HipTest::isImageSupported()) { \
|
||||
INFO("Texture is not support on the device. Skipped."); \
|
||||
return; \
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <hip/hip_runtime.h>
|
||||
#include <hip/hiprtc.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
// OS Check
|
||||
#if defined(_WIN32)
|
||||
#define HT_WIN 1
|
||||
#define HT_LINUX 0
|
||||
#elif defined(__linux__)
|
||||
#define HT_WIN 0
|
||||
#define HT_LINUX 1
|
||||
#else
|
||||
#error "OS not recognized"
|
||||
#endif
|
||||
|
||||
// Platform check
|
||||
#if defined(__HIP_PLATFORM_HCC__) || defined(__HIP_PLATFORM_AMD__)
|
||||
#define HT_AMD 1
|
||||
#define HT_NVIDIA 0
|
||||
#elif defined(__HIP_PLATFORM_NVCC__) || defined(__HIP_PLATFORM_NVIDIA__)
|
||||
#define HT_AMD 0
|
||||
#define HT_NVIDIA 1
|
||||
#else
|
||||
#error "Platform not recognized"
|
||||
#endif
|
||||
|
||||
typedef struct Config_ {
|
||||
std::vector<std::string> json_files; // Json files
|
||||
std::string platform; // amd/nvidia
|
||||
std::string os; // windows/linux
|
||||
} Config;
|
||||
|
||||
// Store Multi threaded results
|
||||
struct HCResult {
|
||||
size_t line; // Line of check (HIP_CHECK_THREAD or REQUIRE_THREAD)
|
||||
std::string file; // File name of the check
|
||||
hipError_t result; // hipResult for HIP_CHECK_THREAD, for conditions its hipSuccess
|
||||
std::string call; // Call of HIP API or a bool condition
|
||||
bool conditionsResult; // If bool condition, result of call. For HIP Calls its true
|
||||
HCResult(size_t l, std::string f, hipError_t r, std::string c, bool b = true)
|
||||
: line(l), file(f), result(r), call(c), conditionsResult(b) {}
|
||||
};
|
||||
|
||||
|
||||
class TestContext {
|
||||
bool p_windows = false, p_linux = false; // OS
|
||||
bool amd = false, nvidia = false; // HIP Platform
|
||||
std::string exe_path;
|
||||
std::string current_test;
|
||||
std::set<std::string> skip_test;
|
||||
std::string json_file_;
|
||||
std::vector<std::string> platform_list_ = {"amd", "nvidia"};
|
||||
std::vector<std::string> os_list_ = {"windows", "linux", "all"};
|
||||
std::vector<std::string> amd_arch_list_ = {};
|
||||
|
||||
struct rtcState {
|
||||
hipModule_t module;
|
||||
hipFunction_t kernelFunction;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, rtcState> compiledKernels{};
|
||||
|
||||
Config config_;
|
||||
std::string& getCommonJsonFile();
|
||||
std::string substringFound(std::vector<std::string> list, std::string filename);
|
||||
void detectOS();
|
||||
void detectPlatform();
|
||||
void getConfigFiles();
|
||||
void setExePath(int, char**);
|
||||
void parseOptions(int, char**);
|
||||
bool parseJsonFiles();
|
||||
std::string getMatchingConfigFile(std::string config_dir);
|
||||
const Config& getConfig() const { return config_; }
|
||||
|
||||
|
||||
TestContext(int argc, char** argv);
|
||||
|
||||
// Multi threaded checks helpers
|
||||
std::mutex resultMutex;
|
||||
std::vector<HCResult> results; // Multi threaded test results buffer
|
||||
std::atomic<bool> hasErrorOccured_{false};
|
||||
|
||||
public:
|
||||
static TestContext& get(int argc = 0, char** argv = nullptr) {
|
||||
static TestContext instance(argc, argv);
|
||||
return instance;
|
||||
}
|
||||
|
||||
static std::string getEnvVar(std::string var) {
|
||||
#if defined(_WIN32)
|
||||
rsize_t MAX_LEN = 4096;
|
||||
char dstBuf[MAX_LEN];
|
||||
size_t dstSize;
|
||||
if (!::getenv_s(&dstSize, dstBuf, MAX_LEN, var.c_str())) {
|
||||
return std::string(dstBuf);
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
char* val = std::getenv(var.c_str());
|
||||
if (val != NULL) {
|
||||
return std::string(val);
|
||||
}
|
||||
#else
|
||||
#error "OS not recognized"
|
||||
#endif
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
|
||||
bool isWindows() const;
|
||||
bool isLinux() const;
|
||||
bool isNvidia() const;
|
||||
bool isAmd() const;
|
||||
bool skipTest() const;
|
||||
|
||||
const std::string& getCurrentTest() const { return current_test; }
|
||||
std::string currentPath() const;
|
||||
|
||||
// Multi threaded results helpers
|
||||
void addResults(HCResult r); // Add multi threaded results
|
||||
void finalizeResults(); // Validate on all results
|
||||
bool hasErrorOccured(); // Query if error has occured
|
||||
|
||||
/**
|
||||
* @brief Unload all loaded modules.
|
||||
* Note: This function needs to be called at the end of each test that uses RTC.
|
||||
* It is not possible to unload the loaded modules without adding explicit code to the end
|
||||
* of each test. This function exists only to provide a clean way to exit a test when using RTC.
|
||||
* However, not unloading a module explicitly shouldn't have any effect on the outcome of
|
||||
* the test.
|
||||
*/
|
||||
void cleanContext();
|
||||
|
||||
/**
|
||||
* @brief Keeps track of all the already compiled rtc kernels.
|
||||
*
|
||||
* @param kernelNameExpression The name expression (e.g. hipTest::vectorADD<float>).
|
||||
* @param loadedModule The loaded module.
|
||||
* @param kernelFunction The hipFunction that will be used to run the kernel in the future.
|
||||
*/
|
||||
void trackRtcState(std::string kernelNameExpression, hipModule_t loadedModule,
|
||||
hipFunction_t kernelFunction);
|
||||
|
||||
/**
|
||||
* @brief Get the already compiled hip rtc kernel function if it exists.
|
||||
*
|
||||
* @param kernelNameExpression The name expression (e.g. hipTest::vectorADD<float>).
|
||||
* @return the hipFunction if it exists. nullptr otherwise
|
||||
*/
|
||||
hipFunction_t getFunction(const std::string kernelNameExpression);
|
||||
|
||||
TestContext(const TestContext&) = delete;
|
||||
void operator=(const TestContext&) = delete;
|
||||
|
||||
~TestContext();
|
||||
};
|
||||
|
||||
static bool _log_enable = (!TestContext::getEnvVar("HT_LOG_ENABLE").empty() ? true : false);
|
||||
|
||||
// printing logs
|
||||
#define LogPrintf(format, ...) \
|
||||
{ \
|
||||
if(_log_enable) { \
|
||||
printf(format, __VA_ARGS__); \
|
||||
printf("%c", '\n'); \
|
||||
} \
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
// We haven't checked which filesystem to include yet
|
||||
#ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
|
||||
// Check for feature test macro for <filesystem>
|
||||
#if defined(__cpp_lib_filesystem)
|
||||
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
|
||||
// Check for feature test macro for <experimental/filesystem>
|
||||
#elif defined(__cpp_lib_experimental_filesystem)
|
||||
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
|
||||
// We can't check if headers exist...
|
||||
// Let's assume experimental to be safe
|
||||
#elif !defined(__has_include)
|
||||
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
|
||||
// Check if the header "<filesystem>" exists
|
||||
#elif __has_include(<filesystem>)
|
||||
// If we're compiling on Visual Studio and are not compiling with C++17,
|
||||
// we need to use experimental
|
||||
#ifdef _MSC_VER
|
||||
// Check and include header that defines "_HAS_CXX17"
|
||||
#if __has_include(<yvals_core.h>)
|
||||
#include <yvals_core.h>
|
||||
|
||||
// Check for enabled C++17 support
|
||||
#if defined(_HAS_CXX17) && _HAS_CXX17
|
||||
// We're using C++17, so let's use the normal version
|
||||
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// If the marco isn't defined yet, that means any of the other
|
||||
// VS specific checks failed, so we need to use experimental
|
||||
#ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
|
||||
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
|
||||
#endif
|
||||
|
||||
// Not on Visual Studio. Let's use the normal version
|
||||
#else // #ifdef _MSC_VER
|
||||
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0
|
||||
#endif
|
||||
|
||||
// Check if the header "<filesystem>" exists
|
||||
#elif __has_include(<experimental/filesystem>)
|
||||
#define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1
|
||||
|
||||
// Fail if neither header is available with a nice error message
|
||||
#else
|
||||
#error Could not find system header "<filesystem>" ||
|
||||
"<experimental/filesystem>"
|
||||
#endif
|
||||
|
||||
// We priously determined that we need the exprimental version
|
||||
#if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
|
||||
// Include it
|
||||
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING 1;
|
||||
#include <experimental/filesystem>
|
||||
// We need the alias from std::experimental::filesystem to std::filesystem
|
||||
namespace fs = std::experimental::filesystem;
|
||||
// We have a decent compiler and can use the normal version
|
||||
#else
|
||||
// Include it
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#endif
|
||||
|
||||
#endif // #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "hip_test_common.hh"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/sysinfo.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <sysinfoapi.h>
|
||||
#endif
|
||||
|
||||
namespace HipTest {
|
||||
static inline int getGeviceCount() {
|
||||
int dev = 0;
|
||||
HIP_CHECK(hipGetDeviceCount(&dev));
|
||||
return dev;
|
||||
}
|
||||
|
||||
// Get Free Memory from the system
|
||||
static inline size_t getMemoryAmount() {
|
||||
#ifdef __linux__
|
||||
struct sysinfo info{};
|
||||
sysinfo(&info);
|
||||
return info.freeram / (1024 * 1024); // MB
|
||||
#elif defined(_WIN32)
|
||||
MEMORYSTATUSEX statex;
|
||||
statex.dwLength = sizeof(statex);
|
||||
GlobalMemoryStatusEx(&statex);
|
||||
return (statex.ullAvailPhys / (1024 * 1024)); // MB
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t getHostThreadCount(const size_t memPerThread, const size_t maxThreads) {
|
||||
if (memPerThread == 0) return 0;
|
||||
auto memAmount = getMemoryAmount();
|
||||
const auto processor_count = std::thread::hardware_concurrency();
|
||||
if (processor_count == 0 || memAmount == 0) return 0;
|
||||
size_t thread_count = 0;
|
||||
if ((processor_count * memPerThread) < memAmount)
|
||||
thread_count = processor_count;
|
||||
else
|
||||
thread_count = reinterpret_cast<size_t>(memAmount / memPerThread);
|
||||
if (maxThreads > 0) {
|
||||
return (thread_count > maxThreads) ? maxThreads : thread_count;
|
||||
}
|
||||
return thread_count;
|
||||
}
|
||||
|
||||
} // namespace HipTest
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hip/hip_runtime.h>
|
||||
|
||||
namespace HipTest {
|
||||
template <typename T> __global__ void vectorADD(const T* A_d, const T* B_d, T* C_d, size_t NELEM) {
|
||||
size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
for (size_t i = offset; i < NELEM; i += stride) {
|
||||
C_d[i] = A_d[i] + B_d[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> __global__ void vectorSUB(const T* A_d, const T* B_d, T* C_d, size_t NELEM) {
|
||||
size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
for (size_t i = offset; i < NELEM; i += stride) {
|
||||
C_d[i] = A_d[i] - B_d[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void vectorADDReverse(const T* A_d, const T* B_d, T* C_d, size_t NELEM) {
|
||||
size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
for (int64_t i = NELEM - stride + offset; i >= 0; i -= stride) {
|
||||
C_d[i] = A_d[i] + B_d[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T> __global__ void addCount(const T* A_d, T* C_d, size_t NELEM, int count) {
|
||||
size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
// Deliberately do this in an inefficient way to increase kernel runtime
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (size_t i = offset; i < NELEM; i += stride) {
|
||||
C_d[i] = A_d[i] + (T)count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
__global__ void addCountReverse(const T* A_d, T* C_d, int64_t NELEM, int count) {
|
||||
size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
// Deliberately do this in an inefficient way to increase kernel runtime
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (int64_t i = NELEM - stride + offset; i >= 0; i -= stride) {
|
||||
C_d[i] = A_d[i] + (T)count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> __global__ void memsetReverse(T* C_d, T val, int64_t NELEM) {
|
||||
size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
for (int64_t i = NELEM - stride + offset; i >= 0; i -= stride) {
|
||||
C_d[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> __global__ void vector_square(const T* A_d, T* C_d, size_t N_ELMTS) {
|
||||
size_t gputhread = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
for (size_t i = gputhread; i < N_ELMTS; i += stride) {
|
||||
C_d[i] = A_d[i] * A_d[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> __global__ void vector_cubic(const T* A_d, T* C_d, size_t N_ELMTS) {
|
||||
size_t gputhread = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
for (size_t i = gputhread; i < N_ELMTS; i += stride) {
|
||||
C_d[i] = A_d[i] * A_d[i] * A_d[i];
|
||||
}
|
||||
}
|
||||
} // namespace HipTest
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hip_test_common.hh"
|
||||
#include "hip_test_filesystem.hh"
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <random>
|
||||
#include <fstream>
|
||||
#include <streambuf>
|
||||
|
||||
namespace hip {
|
||||
/*
|
||||
Class to spawn a process in isolation and test its standard output and return status
|
||||
Good for printf tests and environment variable tests
|
||||
|
||||
How to use:
|
||||
Have the stand alone exe in the same folder
|
||||
Init a class using hip::SpawnProc proc("ExeName", yes_or_no_to_capture_output);
|
||||
proc.run("Optional command line args");
|
||||
*/
|
||||
class SpawnProc {
|
||||
std::string exeName;
|
||||
std::string resultStr;
|
||||
std::string tmpFileName;
|
||||
bool captureOutput;
|
||||
|
||||
std::string getRandomString(size_t len = 6) {
|
||||
std::random_device dev;
|
||||
std::mt19937 rng(dev());
|
||||
std::uniform_int_distribution<std::mt19937::result_type> dist(0, 25);
|
||||
|
||||
std::string res;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
res += 'a' + dist(rng);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
SpawnProc(std::string exeName_, bool captureOutput_ = false)
|
||||
: exeName(exeName_), captureOutput(captureOutput_) {
|
||||
auto dir = fs::path(TestContext::get().currentPath());
|
||||
dir /= exeName;
|
||||
exeName = dir.string();
|
||||
// On Windows, fs::exists returns false without extension.
|
||||
if (TestContext::get().isWindows()) {
|
||||
if(fs::path(exeName).extension().empty()) {
|
||||
exeName += ".exe";
|
||||
}
|
||||
}
|
||||
INFO("Testing that exe exists: " << exeName);
|
||||
REQUIRE(fs::exists(exeName));
|
||||
|
||||
if (captureOutput) {
|
||||
auto path = fs::temp_directory_path();
|
||||
path /= getRandomString();
|
||||
tmpFileName = path.string();
|
||||
INFO("Testing that capture file does not exist already: " << tmpFileName);
|
||||
REQUIRE(!fs::exists(tmpFileName));
|
||||
}
|
||||
}
|
||||
|
||||
int run(std::string commandLineArgs = "") {
|
||||
std::string execCmd = exeName;
|
||||
|
||||
// Append command line args
|
||||
if (commandLineArgs.size() > 0) {
|
||||
execCmd += " "; // Add space for command line args
|
||||
execCmd += commandLineArgs;
|
||||
}
|
||||
|
||||
if (captureOutput) {
|
||||
execCmd += " > ";
|
||||
execCmd += tmpFileName;
|
||||
}
|
||||
auto res = std::system(execCmd.c_str());
|
||||
|
||||
if (captureOutput) {
|
||||
std::ifstream t(tmpFileName.c_str());
|
||||
resultStr =
|
||||
std::string((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
|
||||
t.close();
|
||||
}
|
||||
#if HT_LINUX
|
||||
return WEXITSTATUS(res);
|
||||
#else
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getOutput() { return resultStr; }
|
||||
};
|
||||
} // namespace hip
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <hip/hip_runtime.h>
|
||||
#include <hip/hiprtc.h>
|
||||
#include <kernel_mapping.hh>
|
||||
#include <catch.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include "hip/hip_runtime_api.h"
|
||||
#include "hip_test_context.hh"
|
||||
|
||||
namespace HipTest {
|
||||
|
||||
struct KernelArgument {
|
||||
const void* ptr;
|
||||
size_t sizeRequirement;
|
||||
size_t alignmentRequirement;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reconstructs the name expression for the kernel.
|
||||
*
|
||||
* @param kernelName the name of the kernel (e.g. "HipTest::VectorADD")
|
||||
* @param typenames the typenames used by this kernel (e.g. "float").
|
||||
* @return std::string the reconstructed expression (e.g. "VectorADD<float>""). Returns kernelName
|
||||
* instead if the kernel is not a template.
|
||||
*/
|
||||
inline std::string reconstructExpression(std::string& kernelName,
|
||||
std::vector<std::string>& typenames) {
|
||||
std::string kernelExpression = kernelName;
|
||||
if (typenames.size() > 0) {
|
||||
kernelExpression += "<" + typenames[0];
|
||||
for (size_t i = 1; i < typenames.size(); ++i) {
|
||||
kernelExpression += "," + typenames[i];
|
||||
}
|
||||
kernelExpression += ">";
|
||||
}
|
||||
|
||||
return kernelExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Packs the kernel arguments into the format expected by hipModuleLaunchKernel
|
||||
*
|
||||
* @param args list of arguments for the kernel and their alignemnt requirements.
|
||||
* @return std::vector<char> the packed arguments ready to be passed on to hipModuleLaunchKernel
|
||||
*/
|
||||
inline std::vector<char> alignArguments(std::vector<KernelArgument>& args) {
|
||||
std::vector<char> alignedArguments{};
|
||||
int count = 0;
|
||||
for (auto& arg : args) {
|
||||
const char* argPtr{reinterpret_cast<const char*>(arg.ptr)};
|
||||
|
||||
int paddingNeeded = (arg.alignmentRequirement - 1) & (~count + 1);
|
||||
alignedArguments.insert(std::end(alignedArguments), paddingNeeded, 0);
|
||||
count += paddingNeeded;
|
||||
|
||||
alignedArguments.insert(std::end(alignedArguments), argPtr, argPtr + arg.sizeRequirement);
|
||||
count += arg.sizeRequirement;
|
||||
}
|
||||
return alignedArguments;
|
||||
}
|
||||
|
||||
inline std::vector<char> getKernelCode(hiprtcProgram& rtcProgram) {
|
||||
size_t codeSize;
|
||||
REQUIRE(HIPRTC_SUCCESS == hiprtcGetCodeSize(rtcProgram, &codeSize));
|
||||
|
||||
std::vector<char> code(codeSize);
|
||||
REQUIRE(HIPRTC_SUCCESS == hiprtcGetCode(rtcProgram, code.data()));
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compiles a kernel using HIP RTC
|
||||
*
|
||||
* @param rtcKernel the name of the kernel to compile.
|
||||
* @param kernelNameExpression the name expression to be added to the RTC program (e.g.
|
||||
* HipTest::VectorADD<float>)
|
||||
* @return hiprtcProgram the compiled rtc program.
|
||||
*/
|
||||
inline hiprtcProgram compileRTC(std::string& rtcKernel, std::string& kernelNameExpression) {
|
||||
std::string fileName = mapKernelToFileName.at(rtcKernel);
|
||||
std::string filePath{KERNELS_PATH + fileName};
|
||||
|
||||
INFO("Opening Kernel File: " << filePath);
|
||||
std::ifstream kernelFile{filePath};
|
||||
REQUIRE(kernelFile.is_open());
|
||||
|
||||
std::stringstream stringStream;
|
||||
std::string line;
|
||||
while (getline(kernelFile, line)) {
|
||||
/* Skip the include directive since it is not part of the kernel */
|
||||
if (line.find("#include") != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
stringStream << line << '\n';
|
||||
}
|
||||
kernelFile.close();
|
||||
|
||||
std::string kernelCode{stringStream.str()};
|
||||
INFO("RTC Kernel Code:\n" << kernelCode)
|
||||
|
||||
hiprtcProgram rtcProgram;
|
||||
hiprtcCreateProgram(&rtcProgram, kernelCode.c_str(), (fileName + ".cu").c_str(), 0, nullptr,
|
||||
nullptr);
|
||||
|
||||
std::vector<const char*> options{};
|
||||
#ifdef __HIP_PLATFORM_AMD__
|
||||
|
||||
int deviceCount;
|
||||
REQUIRE(hipSuccess == hipGetDeviceCount(&deviceCount));
|
||||
|
||||
std::set<std::string> architectures{};
|
||||
for (int i = 0; i < deviceCount; ++i) {
|
||||
hipDeviceProp_t props;
|
||||
REQUIRE(hipSuccess == hipGetDeviceProperties(&props, i));
|
||||
architectures.insert(std::string{"--gpu-architecture="} + props.gcnArchName);
|
||||
}
|
||||
|
||||
for (auto& architecture : architectures) {
|
||||
options.push_back(architecture.c_str());
|
||||
}
|
||||
#else
|
||||
options.push_back("--fmad=false");
|
||||
#endif
|
||||
|
||||
REQUIRE(HIPRTC_SUCCESS == hiprtcAddNameExpression(rtcProgram, kernelNameExpression.c_str()));
|
||||
REQUIRE(HIPRTC_SUCCESS == hiprtcCompileProgram(rtcProgram, 1, options.data()));
|
||||
|
||||
return rtcProgram;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a typename as a string
|
||||
*
|
||||
* @tparam T The typename
|
||||
* @return std::string the string representation of T
|
||||
*/
|
||||
template <typename T> std::string getTypeName() {
|
||||
std::string name, prefix, suffix;
|
||||
|
||||
|
||||
#ifdef __clang__
|
||||
name = __PRETTY_FUNCTION__;
|
||||
prefix = "std::string HipTest::getTypeName() [T = ";
|
||||
suffix = "]";
|
||||
#elif defined(__GNUC__)
|
||||
name = __PRETTY_FUNCTION__;
|
||||
prefix = "std::string HipTest::getTypeName() [with T = ";
|
||||
suffix = "; std::string = std::__cxx11::basic_string<char>]";
|
||||
#elif defined(_MSC_VER)
|
||||
name = __FUNCSIG__;
|
||||
prefix = "std::string __cdecl HipTest::getTypeName<";
|
||||
suffix = ">(void)";
|
||||
#endif
|
||||
|
||||
return name.substr(prefix.size(), name.rfind(suffix) - prefix.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tells the user that the kernels are using HIP RTC. Prints only once per test.
|
||||
*
|
||||
*/
|
||||
static inline void printInfo() {
|
||||
static bool alreadyPrinted{false};
|
||||
|
||||
if (!alreadyPrinted) {
|
||||
std::cout << "INFO: This test is running using HIP RTC to compile and run the kernels."
|
||||
<< std::endl;
|
||||
alreadyPrinted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compiles and launches a kernel using HIP RTC
|
||||
*
|
||||
* @tparam Typenames A list of typenames used by the kernel (unused if the kernel is not a
|
||||
* template).
|
||||
* @tparam Args A list of kernel arguments to be forwarded.
|
||||
* @param getKernelName A function wrapper that returns the name of the kernel to launch (check
|
||||
* kernels.hh for more info)
|
||||
* @param numBlocks
|
||||
* @param numThreads
|
||||
* @param memPerBlock
|
||||
* @param stream
|
||||
* @param packedArgs A list of kernel arguments to be forwarded.
|
||||
*/
|
||||
template <typename... Typenames, typename... Args>
|
||||
void launchRTCKernel(std::string (*getKernelName)(), dim3 numBlocks, dim3 numThreads,
|
||||
std::uint32_t memPerBlock, hipStream_t stream, Args&&... packedArgs) {
|
||||
printInfo();
|
||||
TestContext& testContext = TestContext::get();
|
||||
std::string kernelName = (*getKernelName)();
|
||||
|
||||
std::vector<std::string> kernelTypenames{std::string(HipTest::getTypeName<Typenames>())...};
|
||||
std::string kernelExpression = reconstructExpression(kernelName, kernelTypenames);
|
||||
|
||||
static std::mutex mutex{};
|
||||
{
|
||||
std::lock_guard<std::mutex> lockGuard(mutex);
|
||||
if (testContext.getFunction(kernelExpression) == nullptr) {
|
||||
hiprtcProgram rtcProgram{compileRTC(kernelName, kernelExpression)};
|
||||
std::vector<char> compiledCode{getKernelCode(rtcProgram)};
|
||||
|
||||
hipModule_t module;
|
||||
|
||||
REQUIRE(hipSuccess == hipModuleLoadData(&module, compiledCode.data()));
|
||||
|
||||
hipFunction_t kernelFunction;
|
||||
|
||||
const char* loweredName;
|
||||
REQUIRE(HIPRTC_SUCCESS ==
|
||||
hiprtcGetLoweredName(rtcProgram, kernelExpression.c_str(), &loweredName));
|
||||
REQUIRE(hipSuccess == hipModuleGetFunction(&kernelFunction, module, loweredName));
|
||||
|
||||
/* After obtaining the kernelFunction, the program is no longer needed. So it can be destroyed */
|
||||
REQUIRE(HIPRTC_SUCCESS == hiprtcDestroyProgram(&rtcProgram));
|
||||
|
||||
testContext.trackRtcState(kernelExpression, module, kernelFunction);
|
||||
}
|
||||
}
|
||||
|
||||
hipFunction_t kernelFunction = testContext.getFunction(kernelExpression);
|
||||
|
||||
std::vector<KernelArgument> args = {
|
||||
{reinterpret_cast<const void*>(&packedArgs), sizeof(Args), alignof(Args)}...};
|
||||
|
||||
std::vector<char> alignedArguments{alignArguments(args)};
|
||||
size_t argumentsSize{alignedArguments.size()};
|
||||
|
||||
void* config_array[] = {HIP_LAUNCH_PARAM_BUFFER_POINTER, alignedArguments.data(),
|
||||
HIP_LAUNCH_PARAM_BUFFER_SIZE, reinterpret_cast<void*>(&argumentsSize),
|
||||
HIP_LAUNCH_PARAM_END};
|
||||
|
||||
REQUIRE(hipSuccess ==
|
||||
hipModuleLaunchKernel(kernelFunction, numBlocks.x, numBlocks.y, numBlocks.z, numThreads.x,
|
||||
numThreads.y, numThreads.z, memPerBlock, stream, nullptr,
|
||||
config_array));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template overload for when numBlocks and numThreads is an integer.
|
||||
*
|
||||
*/
|
||||
template <typename... Typenames, typename... Args>
|
||||
void launchRTCKernel(std::string kernelName, int numBlocks, int numThreads,
|
||||
std::uint32_t memPerBlock, hipStream_t stream, Args&&... packedArgs) {
|
||||
launchRTCKernel<Typenames...>(kernelName, dim3(numBlocks), dim3(numThreads), memPerBlock, stream,
|
||||
std::forward<Args>(packedArgs)...);
|
||||
}
|
||||
|
||||
} // namespace HipTest
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Error codes retured by rocm_smi_lib functions
|
||||
*/
|
||||
typedef enum {
|
||||
RSMI_STATUS_SUCCESS = 0x0, //!< Operation was successful
|
||||
RSMI_STATUS_INVALID_ARGS, //!< Passed in arguments are not valid
|
||||
RSMI_STATUS_NOT_SUPPORTED, //!< The requested information or
|
||||
//!< action is not available for the
|
||||
//!< given input, on the given system
|
||||
RSMI_STATUS_FILE_ERROR, //!< Problem accessing a file. This
|
||||
//!< may because the operation is not
|
||||
//!< supported by the Linux kernel
|
||||
//!< version running on the executing
|
||||
//!< machine
|
||||
RSMI_STATUS_PERMISSION, //!< Permission denied/EACCESS file
|
||||
//!< error. Many functions require
|
||||
//!< root access to run.
|
||||
RSMI_STATUS_OUT_OF_RESOURCES, //!< Unable to acquire memory or other
|
||||
//!< resource
|
||||
RSMI_STATUS_INTERNAL_EXCEPTION, //!< An internal exception was caught
|
||||
RSMI_STATUS_INPUT_OUT_OF_BOUNDS, //!< The provided input is out of
|
||||
//!< allowable or safe range
|
||||
RSMI_STATUS_INIT_ERROR, //!< An error occurred when rsmi
|
||||
//!< initializing internal data
|
||||
//!< structures
|
||||
RSMI_INITIALIZATION_ERROR = RSMI_STATUS_INIT_ERROR,
|
||||
RSMI_STATUS_NOT_YET_IMPLEMENTED, //!< The requested function has not
|
||||
//!< yet been implemented in the
|
||||
//!< current system for the current
|
||||
//!< devices
|
||||
RSMI_STATUS_NOT_FOUND, //!< An item was searched for but not
|
||||
//!< found
|
||||
RSMI_STATUS_INSUFFICIENT_SIZE, //!< Not enough resources were
|
||||
//!< available for the operation
|
||||
RSMI_STATUS_INTERRUPT, //!< An interrupt occurred during
|
||||
//!< execution of function
|
||||
RSMI_STATUS_UNEXPECTED_SIZE, //!< An unexpected amount of data
|
||||
//!< was read
|
||||
RSMI_STATUS_NO_DATA, //!< No data was found for a given
|
||||
//!< input
|
||||
RSMI_STATUS_UNEXPECTED_DATA, //!< The data read or provided to
|
||||
//!< function is not what was expected
|
||||
RSMI_STATUS_BUSY, //!< A resource or mutex could not be
|
||||
//!< acquired because it is already
|
||||
//!< being used
|
||||
RSMI_STATUS_REFCOUNT_OVERFLOW, //!< An internal reference counter
|
||||
//!< exceeded INT32_MAX
|
||||
|
||||
RSMI_STATUS_UNKNOWN_ERROR = 0xFFFFFFFF, //!< An unknown error occurred
|
||||
} rsmi_status_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Types of memory
|
||||
*/
|
||||
typedef enum {
|
||||
RSMI_MEM_TYPE_FIRST = 0,
|
||||
|
||||
RSMI_MEM_TYPE_VRAM = RSMI_MEM_TYPE_FIRST, //!< VRAM memory
|
||||
RSMI_MEM_TYPE_VIS_VRAM, //!< VRAM memory that is visible
|
||||
RSMI_MEM_TYPE_GTT, //!< GTT memory
|
||||
|
||||
RSMI_MEM_TYPE_LAST = RSMI_MEM_TYPE_GTT
|
||||
} rsmi_memory_type_t;
|
||||
@@ -0,0 +1,370 @@
|
||||
#pragma once
|
||||
#include <math.h>
|
||||
|
||||
#define HIP_SAMPLING_VERIFY_EPSILON 0.00001
|
||||
// The internal precision varies by the GPU family and sometimes within the family.
|
||||
// Thus the following threshold is subject to change.
|
||||
#define HIP_SAMPLING_VERIFY_RELATIVE_THRESHOLD 0.05 // 5% for filter mode
|
||||
#define HIP_SAMPLING_VERIFY_ABSOLUTE_THRESHOLD 0.1
|
||||
|
||||
#if HT_NVIDIA
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 4, T>::type
|
||||
inline __host__ __device__ operator+(const T &a, const T &b)
|
||||
{
|
||||
return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 4, T>::type
|
||||
inline __host__ __device__ operator-(const T &a, const T &b)
|
||||
{
|
||||
return {a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 4, bool>::type
|
||||
inline __host__ __device__ operator==(const T &a, const T &b)
|
||||
{
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 4, T>::type
|
||||
inline __host__ __device__ operator*(const decltype(T::x) &a, const T &b)
|
||||
{
|
||||
return {a * b.x, a * b.y, a * b.z, a * b.w};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 4, void>::type
|
||||
inline __host__ __device__ operator*=(T &a, const decltype(T::x) &b)
|
||||
{
|
||||
a.x *= b;
|
||||
a.y *= b;
|
||||
a.z *= b;
|
||||
a.w *= b;
|
||||
}
|
||||
#endif // HT_NVIDIA
|
||||
|
||||
// See https://en.wikipedia.org/wiki/SRGB#Transformation
|
||||
// From CIE 1931 color space to sRGB
|
||||
inline float hipSRGBMap(float fc) {
|
||||
double c = static_cast<double>(fc);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
if (std::isnan(c))
|
||||
c = 0.0;
|
||||
#else
|
||||
if (_isnan(c)) c = 0.0;
|
||||
#endif
|
||||
|
||||
if (c > 1.0)
|
||||
c = 1.0;
|
||||
else if (c < 0.0)
|
||||
c = 0.0;
|
||||
else if (c < 0.0031308)
|
||||
c = 12.92 * c;
|
||||
else
|
||||
c = 1.055 * pow(c, 5.0 / 12.0) - 0.055;
|
||||
|
||||
return static_cast<float>(c);
|
||||
}
|
||||
|
||||
// From sRGB to CIE 1931 color space
|
||||
inline float hipSRGBUnmap(float fc) {
|
||||
double c = static_cast<double>(fc);
|
||||
|
||||
if (c <= 0.04045)
|
||||
c = c / 12.92;
|
||||
else
|
||||
c = pow((c + 0.055) / 1.055, 2.4);
|
||||
|
||||
return static_cast<float>(c);
|
||||
}
|
||||
|
||||
inline float4 hipSRGBMap(float4 fc) {
|
||||
fc.x = hipSRGBMap(fc.x);
|
||||
fc.y = hipSRGBMap(fc.y);
|
||||
fc.z = hipSRGBMap(fc.z);
|
||||
// Alpha channel will keep unchanged
|
||||
return fc;
|
||||
}
|
||||
|
||||
inline float4 hipSRGBUnmap(float4 fc) {
|
||||
fc.x = hipSRGBUnmap(fc.x);
|
||||
fc.y = hipSRGBUnmap(fc.y);
|
||||
fc.z = hipSRGBUnmap(fc.z);
|
||||
// Alpha channel will keep unchanged
|
||||
return fc;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_scalar<T>::value == true, double>::type
|
||||
hipFabs(const T &t) {
|
||||
return fabs(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 1, double>::type
|
||||
hipFabs(const T &t) {
|
||||
return fabs(t.x);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 2, double>::type
|
||||
hipFabs(const T &t) {
|
||||
double x = static_cast<double>(t.x);
|
||||
double y = static_cast<double>(t.y);
|
||||
double s = x * x + y * y;
|
||||
return sqrt(s);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 3, double>::type
|
||||
hipFabs(const T &t) {
|
||||
double x = static_cast<double>(t.x);
|
||||
double y = static_cast<double>(t.y);
|
||||
double z = static_cast<double>(t.z);
|
||||
double s = x * x + y * y + z * z;
|
||||
return sqrt(s);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<sizeof(T) / sizeof(decltype(T::x)) == 4, double>::type
|
||||
hipFabs(const T &t) {
|
||||
double x = static_cast<double>(t.x);
|
||||
double y = static_cast<double>(t.y);
|
||||
double z = static_cast<double>(t.z);
|
||||
double w = static_cast<double>(t.w);
|
||||
double s = x * x + y * y + z * z + w * w;
|
||||
return sqrt(s);
|
||||
}
|
||||
|
||||
template<typename T, hipTextureFilterMode fMode = hipFilterModePoint, bool sRGB = false>
|
||||
bool hipTextureSamplingVerify(T outputData, T expected) {
|
||||
bool testResult = false;
|
||||
if (fMode == hipFilterModePoint && !sRGB) {
|
||||
testResult = outputData == expected;
|
||||
} else {
|
||||
double mean = (hipFabs(outputData) + hipFabs(expected)) / 2;
|
||||
double diff = hipFabs(outputData - expected);
|
||||
double ratio = diff / (mean + HIP_SAMPLING_VERIFY_EPSILON);
|
||||
if (ratio <= HIP_SAMPLING_VERIFY_RELATIVE_THRESHOLD) {
|
||||
testResult = true;
|
||||
} else if (diff <= HIP_SAMPLING_VERIFY_ABSOLUTE_THRESHOLD) {
|
||||
// Some small outputs have big ratio due to float operation difference of ALU and GPU
|
||||
testResult = true;
|
||||
}
|
||||
}
|
||||
return testResult;
|
||||
}
|
||||
|
||||
// Simulate CTS static AddressingTable sAddressingTable
|
||||
template<hipTextureAddressMode addressMode>
|
||||
void hipTextureGetAddress(int &value, const int maxValue)
|
||||
{
|
||||
switch(addressMode)
|
||||
{
|
||||
case hipAddressModeClamp:
|
||||
value = value < 0 ? 0
|
||||
: (value > maxValue - 1 ? maxValue - 1 : value);
|
||||
break;
|
||||
case hipAddressModeBorder:
|
||||
value = value < -1 ? -1
|
||||
: (value > maxValue ? maxValue : value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate logics in CTS read_image_pixel_float().
|
||||
// x, y and z must be returned by hipTextureGetAddress()
|
||||
template<typename T, hipTextureAddressMode addressMode, bool sRGB = false>
|
||||
T hipTextureGetValue(const T *data, const int x, const int width,
|
||||
const int y = 0, const int height = 0, const int z = 0, const int depth = 0) {
|
||||
T result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
switch (addressMode) {
|
||||
case hipAddressModeClamp:
|
||||
if (width > 0) {
|
||||
if (height == 0 && depth == 0) {
|
||||
result = data[x]; // 1D
|
||||
} else if (depth == 0) {
|
||||
result = data[y * width + x]; // 2D
|
||||
} else {
|
||||
result = data[z * width * height + y * width + x]; // 3D
|
||||
}
|
||||
}
|
||||
break;
|
||||
case hipAddressModeBorder:
|
||||
if (width > 0) {
|
||||
if (height == 0 && depth == 0) {
|
||||
if (x >= 0 && x < width)
|
||||
result = data[x]; // 1D
|
||||
} else if (depth == 0) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height)
|
||||
result = data[y * width + x]; // 2D
|
||||
} else {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height && z >= 0 && z < depth)
|
||||
result = data[z * width * height + y * width + x]; // 3D
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (sRGB && std::is_same<T, float4>::value) {
|
||||
result = hipSRGBUnmap(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, hipTextureAddressMode addressMode, hipTextureFilterMode filterMode, bool sRGB = false>
|
||||
T getExpectedValue(const int width, float x, const T *data) {
|
||||
T result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
switch (filterMode) {
|
||||
case hipFilterModePoint: {
|
||||
int i1 = static_cast<int>(floor(x));
|
||||
hipTextureGetAddress < addressMode > (i1, width);
|
||||
result = hipTextureGetValue < T, addressMode, sRGB > (data, i1, width);
|
||||
}
|
||||
break;
|
||||
case hipFilterModeLinear: {
|
||||
x -= 0.5;
|
||||
int i1 = static_cast<int>(floor(x));
|
||||
int i2 = i1 + 1;
|
||||
float a = x - i1;
|
||||
hipTextureGetAddress < addressMode > (i1, width);
|
||||
hipTextureGetAddress < addressMode > (i2, width);
|
||||
|
||||
T t1 = hipTextureGetValue < T, addressMode, sRGB> (data, i1, width);
|
||||
T t2 = hipTextureGetValue < T, addressMode, sRGB > (data, i2, width);
|
||||
|
||||
return (1 - a) * t1 + a * t2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, hipTextureAddressMode addressMode, hipTextureFilterMode filterMode, bool sRGB = false>
|
||||
T getExpectedValue(const int width, const int height, float x, float y, const T *data) {
|
||||
T result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
switch (filterMode) {
|
||||
case hipFilterModePoint: {
|
||||
int i1 = static_cast<int>(floor(x));
|
||||
int j1 = static_cast<int>(floor(y));
|
||||
hipTextureGetAddress < addressMode > (i1, width);
|
||||
hipTextureGetAddress < addressMode > (j1, height);
|
||||
result = hipTextureGetValue < T, addressMode, sRGB > (data, i1, width, j1, height);
|
||||
}
|
||||
break;
|
||||
case hipFilterModeLinear: {
|
||||
x -= 0.5;
|
||||
y -= 0.5;
|
||||
|
||||
int i1 = static_cast<int>(floor(x));
|
||||
int j1 = static_cast<int>(floor(y));
|
||||
|
||||
int i2 = i1 + 1;
|
||||
int j2 = j1 + 1;
|
||||
|
||||
float a = x - i1;
|
||||
float b = y - j1;
|
||||
|
||||
hipTextureGetAddress < addressMode > (i1, width);
|
||||
hipTextureGetAddress < addressMode > (i2, width);
|
||||
hipTextureGetAddress < addressMode > (j1, height);
|
||||
hipTextureGetAddress < addressMode > (j2, height);
|
||||
|
||||
T t11 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i1, width, j1, height);
|
||||
T t21 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i2, width, j1, height);
|
||||
T t12 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i1, width, j2, height);
|
||||
T t22 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i2, width, j2, height);
|
||||
|
||||
result = (1 - a) * (1 - b) * t11 + a * (1 - b) * t21 + (1 - a) * b * t12
|
||||
+ a * b * t22;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, hipTextureAddressMode addressMode, hipTextureFilterMode filterMode, bool sRGB = false>
|
||||
T getExpectedValue(const int width, const int height, const int depth,
|
||||
float x, float y, float z, const T *data) {
|
||||
T result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
switch (filterMode) {
|
||||
case hipFilterModePoint: {
|
||||
int i1 = static_cast<int>(floor(x));
|
||||
int j1 = static_cast<int>(floor(y));
|
||||
int k1 = static_cast<int>(floor(z));
|
||||
|
||||
hipTextureGetAddress < addressMode > (i1, width);
|
||||
hipTextureGetAddress < addressMode > (j1, height);
|
||||
hipTextureGetAddress < addressMode > (k1, depth);
|
||||
|
||||
result = hipTextureGetValue < T, addressMode, sRGB > (data, i1, width, j1, height, k1, depth);
|
||||
}
|
||||
break;
|
||||
case hipFilterModeLinear: {
|
||||
x -= 0.5;
|
||||
y -= 0.5;
|
||||
z -= 0.5;
|
||||
|
||||
int i1 = static_cast<int>(floor(x));
|
||||
int j1 = static_cast<int>(floor(y));
|
||||
int k1 = static_cast<int>(floor(z));
|
||||
|
||||
int i2 = i1 + 1;
|
||||
int j2 = j1 + 1;
|
||||
int k2 = k1 + 1;
|
||||
|
||||
float a = x - i1;
|
||||
float b = y - j1;
|
||||
float c = z - k1;
|
||||
|
||||
hipTextureGetAddress < addressMode > (i1, width);
|
||||
hipTextureGetAddress < addressMode > (i2, width);
|
||||
hipTextureGetAddress < addressMode > (j1, height);
|
||||
hipTextureGetAddress < addressMode > (j2, height);
|
||||
hipTextureGetAddress < addressMode > (k1, depth);
|
||||
hipTextureGetAddress < addressMode > (k2, depth);
|
||||
|
||||
T t111 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i1, width, j1, height, k1, depth);
|
||||
T t211 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i2, width, j1, height, k1, depth);
|
||||
T t121 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i1, width, j2, height, k1, depth);
|
||||
T t112 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i1, width, j1, height, k2, depth);
|
||||
T t122 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i1, width, j2, height, k2, depth);
|
||||
T t212 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i2, width, j1, height, k2, depth);
|
||||
T t221 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i2, width, j2, height, k1, depth);
|
||||
T t222 = hipTextureGetValue < T, addressMode, sRGB
|
||||
> (data, i2, width, j2, height, k2, depth);
|
||||
|
||||
result =
|
||||
(1 - a) * (1 - b) * (1 - c) * t111 + a * (1 - b) * (1 - c) * t211 +
|
||||
(1 - a) * b * (1 - c) * t121 + a * b * (1 - c) * t221 +
|
||||
(1 - a) * (1 - b) * c * t112 + a * (1 - b) * c * t212 +
|
||||
(1 - a) * b * c * t122 + a * b * c * t222;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
const std::map<std::string, std::string> mapKernelToFileName{
|
||||
{"Set", "Set.cpp"},
|
||||
{"HipTest::vectorADD", "vectorADD.inl"},
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <map>
|
||||
|
||||
#ifndef RTC_TESTING
|
||||
|
||||
__global__ void Set(int* Ad, int val);
|
||||
|
||||
/* Kernel Templates */
|
||||
#include "vectorADD.inl"
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Wrapper Macros that create a string representation of the kernel name.
|
||||
* In the case of kernel templates, a variadic template is used to ensure compatibility with
|
||||
* the launchKernel template when RTC is not enabled. If the kernel is inside a namespace, use the
|
||||
* "_NS" version of the Macro.
|
||||
*/
|
||||
#define FUNCTION_WRAPPER(param) \
|
||||
std::string param() { return #param; }
|
||||
#define TEMPLATE_WRAPPER(param) \
|
||||
template <typename...> std::string param() { return #param; }
|
||||
#define FUNCTION_WRAPPER_NS(param, namespace) \
|
||||
std::string param() { return #namespace "::" #param; }
|
||||
#define TEMPLATE_WRAPPER_NS(param, namespace) \
|
||||
template <typename...> std::string param() { return #namespace "::" #param; }
|
||||
|
||||
FUNCTION_WRAPPER(Set);
|
||||
|
||||
namespace HipTest {
|
||||
TEMPLATE_WRAPPER_NS(vectorADD, HipTest);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hip_array_common.hh>
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_runtime_api.h>
|
||||
|
||||
enum class LinearAllocs {
|
||||
malloc,
|
||||
mallocAndRegister,
|
||||
hipHostMalloc,
|
||||
hipMalloc,
|
||||
hipMallocManaged,
|
||||
};
|
||||
|
||||
template <typename T> class LinearAllocGuard {
|
||||
public:
|
||||
LinearAllocGuard(const LinearAllocs allocation_type, const size_t size,
|
||||
const unsigned int flags = 0u)
|
||||
: allocation_type_{allocation_type} {
|
||||
switch (allocation_type_) {
|
||||
case LinearAllocs::malloc:
|
||||
ptr_ = host_ptr_ = reinterpret_cast<T*>(malloc(size));
|
||||
break;
|
||||
case LinearAllocs::mallocAndRegister:
|
||||
host_ptr_ = reinterpret_cast<T*>(malloc(size));
|
||||
HIP_CHECK(hipHostRegister(host_ptr_, size, flags));
|
||||
HIP_CHECK(hipHostGetDevicePointer(reinterpret_cast<void**>(&ptr_), host_ptr_, 0u));
|
||||
break;
|
||||
case LinearAllocs::hipHostMalloc:
|
||||
HIP_CHECK(hipHostMalloc(reinterpret_cast<void**>(&ptr_), size, flags));
|
||||
host_ptr_ = ptr_;
|
||||
break;
|
||||
case LinearAllocs::hipMalloc:
|
||||
HIP_CHECK(hipMalloc(reinterpret_cast<void**>(&ptr_), size));
|
||||
break;
|
||||
case LinearAllocs::hipMallocManaged:
|
||||
HIP_CHECK(hipMallocManaged(reinterpret_cast<void**>(&ptr_), size, flags ? flags : 1u));
|
||||
host_ptr_ = ptr_;
|
||||
}
|
||||
}
|
||||
|
||||
LinearAllocGuard(const LinearAllocGuard&) = delete;
|
||||
LinearAllocGuard(LinearAllocGuard&&) = delete;
|
||||
|
||||
~LinearAllocGuard() {
|
||||
// No Catch macros, don't want to possibly throw in the destructor
|
||||
switch (allocation_type_) {
|
||||
case LinearAllocs::malloc:
|
||||
free(ptr_);
|
||||
break;
|
||||
case LinearAllocs::mallocAndRegister:
|
||||
// Cast to void to suppress nodiscard warnings
|
||||
static_cast<void>(hipHostUnregister(host_ptr_));
|
||||
free(host_ptr_);
|
||||
break;
|
||||
case LinearAllocs::hipHostMalloc:
|
||||
static_cast<void>(hipHostFree(ptr_));
|
||||
break;
|
||||
case LinearAllocs::hipMalloc:
|
||||
case LinearAllocs::hipMallocManaged:
|
||||
static_cast<void>(hipFree(ptr_));
|
||||
}
|
||||
}
|
||||
|
||||
T* ptr() const { return ptr_; };
|
||||
T* host_ptr() const { return host_ptr_; }
|
||||
|
||||
private:
|
||||
const LinearAllocs allocation_type_;
|
||||
T* ptr_ = nullptr;
|
||||
T* host_ptr_ = nullptr;
|
||||
};
|
||||
|
||||
template <typename T> class LinearAllocGuardMultiDim {
|
||||
protected:
|
||||
LinearAllocGuardMultiDim(hipExtent extent) : extent_{extent} {}
|
||||
|
||||
~LinearAllocGuardMultiDim() { static_cast<void>(hipFree(pitched_ptr_.ptr)); }
|
||||
|
||||
public:
|
||||
T* ptr() const { return reinterpret_cast<T*>(pitched_ptr_.ptr); };
|
||||
|
||||
size_t pitch() const { return pitched_ptr_.pitch; }
|
||||
|
||||
hipExtent extent() const { return extent_; }
|
||||
|
||||
hipPitchedPtr pitched_ptr() const { return pitched_ptr_; }
|
||||
|
||||
size_t width() const { return extent_.width; }
|
||||
|
||||
size_t width_logical() const { return extent_.width / sizeof(T); }
|
||||
|
||||
size_t height() const { return extent_.height; }
|
||||
|
||||
public:
|
||||
hipPitchedPtr pitched_ptr_;
|
||||
const hipExtent extent_;
|
||||
};
|
||||
|
||||
template <typename T> class LinearAllocGuard2D : public LinearAllocGuardMultiDim<T> {
|
||||
public:
|
||||
LinearAllocGuard2D(const size_t width_logical, const size_t height)
|
||||
: LinearAllocGuardMultiDim<T>{make_hipExtent(width_logical * sizeof(T), height, 1)} {
|
||||
HIP_CHECK(hipMallocPitch(&this->pitched_ptr_.ptr, &this->pitched_ptr_.pitch,
|
||||
this->extent_.width, this->extent_.height));
|
||||
}
|
||||
|
||||
LinearAllocGuard2D(const LinearAllocGuard2D&) = delete;
|
||||
LinearAllocGuard2D(LinearAllocGuard2D&&) = delete;
|
||||
};
|
||||
|
||||
template <typename T> class LinearAllocGuard3D : public LinearAllocGuardMultiDim<T> {
|
||||
public:
|
||||
LinearAllocGuard3D(const size_t width_logical, const size_t height, const size_t depth)
|
||||
: LinearAllocGuardMultiDim<T>{make_hipExtent(width_logical * sizeof(T), height, depth)} {
|
||||
HIP_CHECK(hipMalloc3D(&this->pitched_ptr_, this->extent_));
|
||||
}
|
||||
|
||||
LinearAllocGuard3D(const hipExtent extent) : LinearAllocGuardMultiDim<T>(extent) {
|
||||
HIP_CHECK(hipMalloc3D(&this->pitched_ptr_, this->extent_));
|
||||
}
|
||||
|
||||
LinearAllocGuard3D(const LinearAllocGuard3D&) = delete;
|
||||
LinearAllocGuard3D(LinearAllocGuard3D&&) = delete;
|
||||
|
||||
size_t depth() const { return this->extent_.depth; }
|
||||
};
|
||||
|
||||
template <typename T> class ArrayAllocGuard {
|
||||
public:
|
||||
// extent should contain logical width
|
||||
ArrayAllocGuard(const hipExtent extent, const unsigned int flags = 0u) : extent_{extent} {
|
||||
hipChannelFormatDesc desc = hipCreateChannelDesc<T>();
|
||||
HIP_CHECK(hipMalloc3DArray(&ptr_, &desc, extent_, flags));
|
||||
}
|
||||
|
||||
~ArrayAllocGuard() { static_cast<void>(hipFreeArray(ptr_)); }
|
||||
|
||||
ArrayAllocGuard(const ArrayAllocGuard&) = delete;
|
||||
ArrayAllocGuard(ArrayAllocGuard&&) = delete;
|
||||
|
||||
hipArray_t ptr() const { return ptr_; }
|
||||
|
||||
hipExtent extent() const { return extent_; }
|
||||
|
||||
private:
|
||||
hipArray_t ptr_ = nullptr;
|
||||
const hipExtent extent_;
|
||||
};
|
||||
|
||||
template <typename T> class DrvArrayAllocGuard {
|
||||
public:
|
||||
// extent should contain width in bytes
|
||||
DrvArrayAllocGuard(const hipExtent extent, const unsigned int flags = 0u) : extent_{extent} {
|
||||
HIP_ARRAY3D_DESCRIPTOR desc{};
|
||||
using vec_info = vector_info<T>;
|
||||
desc.Format = vec_info::format;
|
||||
desc.NumChannels = vec_info::size;
|
||||
desc.Width = extent_.width / sizeof(T);
|
||||
desc.Height = extent_.height;
|
||||
desc.Depth = extent_.depth;
|
||||
desc.Flags = flags;
|
||||
HIP_CHECK(hipArray3DCreate(&ptr_, &desc));
|
||||
}
|
||||
|
||||
~DrvArrayAllocGuard() { static_cast<void>(hipArrayDestroy(ptr_)); }
|
||||
|
||||
DrvArrayAllocGuard(const DrvArrayAllocGuard&) = delete;
|
||||
DrvArrayAllocGuard(DrvArrayAllocGuard&&) = delete;
|
||||
|
||||
hiparray ptr() const { return ptr_; }
|
||||
|
||||
hipExtent extent() const { return extent_; }
|
||||
|
||||
private:
|
||||
hiparray ptr_ = nullptr;
|
||||
const hipExtent extent_;
|
||||
};
|
||||
|
||||
enum class Streams { nullstream, perThread, created };
|
||||
|
||||
class StreamGuard {
|
||||
public:
|
||||
StreamGuard(const Streams stream_type) : stream_type_{stream_type} {
|
||||
switch (stream_type_) {
|
||||
case Streams::nullstream:
|
||||
stream_ = nullptr;
|
||||
break;
|
||||
case Streams::perThread:
|
||||
stream_ = hipStreamPerThread;
|
||||
break;
|
||||
case Streams::created:
|
||||
HIP_CHECK(hipStreamCreate(&stream_));
|
||||
}
|
||||
}
|
||||
|
||||
StreamGuard(const StreamGuard&) = delete;
|
||||
StreamGuard(StreamGuard&&) = delete;
|
||||
|
||||
~StreamGuard() {
|
||||
if (stream_type_ == Streams::created) {
|
||||
static_cast<void>(hipStreamDestroy(stream_));
|
||||
}
|
||||
}
|
||||
|
||||
hipStream_t stream() const { return stream_; }
|
||||
|
||||
private:
|
||||
const Streams stream_type_;
|
||||
hipStream_t stream_;
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
/*
|
||||
Guarantees total ordering between parent and child thread
|
||||
PARENT CHILD
|
||||
THREAD THREAD
|
||||
TestPart1
|
||||
\
|
||||
\
|
||||
\
|
||||
TestPart2
|
||||
/
|
||||
/
|
||||
/
|
||||
TestPart3
|
||||
\
|
||||
\
|
||||
\
|
||||
TestPart4
|
||||
Usage:
|
||||
Define a derived class which inherits from ThreadedZigZagTest instantiated with that selfsame class,
|
||||
which implements the appropriate test methods
|
||||
class DerivedTestClass : public ThreadedZigZagTest<DerivedTestClass> {
|
||||
void TestPart1() {...}
|
||||
void TestPart2() {...}
|
||||
void TestPart3() {...}
|
||||
void TestPart4() {...}
|
||||
};
|
||||
The derived class can contain state that the test requires.
|
||||
*/
|
||||
|
||||
template <typename T> class ThreadedZigZagTest {
|
||||
public:
|
||||
void run() {
|
||||
// 1.
|
||||
static_cast<T*>(this)->TestPart1();
|
||||
|
||||
auto t = std::thread([this] {
|
||||
// 2.
|
||||
static_cast<T*>(this)->TestPart2();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
ready_ = true;
|
||||
}
|
||||
cv_.notify_one();
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx_);
|
||||
cv_.wait(lock, [this] { return !ready_; });
|
||||
}
|
||||
|
||||
// 4.
|
||||
static_cast<T*>(this)->TestPart4();
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx_);
|
||||
cv_.wait(lock, [this] { return ready_; });
|
||||
}
|
||||
|
||||
// 3.
|
||||
static_cast<T*>(this)->TestPart3();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
ready_ = false;
|
||||
}
|
||||
cv_.notify_one();
|
||||
|
||||
// Finalize
|
||||
t.join();
|
||||
HIP_CHECK_THREAD_FINALIZE();
|
||||
}
|
||||
|
||||
void TestPart1() const {}
|
||||
void TestPart2() const {}
|
||||
void TestPart3() const {}
|
||||
void TestPart4() const {}
|
||||
|
||||
private:
|
||||
std::mutex mtx_;
|
||||
std::condition_variable cv_;
|
||||
bool ready_ = false;
|
||||
};
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
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 <chrono>
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_runtime_api.h>
|
||||
|
||||
namespace {
|
||||
inline constexpr size_t kPageSize = 4096;
|
||||
} // anonymous namespace
|
||||
|
||||
template <typename T>
|
||||
void ArrayMismatch(T* const expected, T* const actual, const size_t num_elements) {
|
||||
const auto ret = std::mismatch(expected, expected + num_elements, actual);
|
||||
if (ret.first != expected + num_elements) {
|
||||
const auto idx = std::distance(expected, ret.first);
|
||||
INFO("Value mismatch at index: " << idx);
|
||||
REQUIRE(expected[idx] == actual[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename It, typename T> void ArrayFindIfNot(It begin, It end, const T expected_value) {
|
||||
const auto it = std::find_if_not(
|
||||
begin, end, [expected_value](const int elem) { return expected_value == elem; });
|
||||
|
||||
if (it != end) {
|
||||
const auto idx = std::distance(begin, it);
|
||||
INFO("Value mismatch at index " << idx);
|
||||
REQUIRE(expected_value == *it);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ArrayFindIfNot(T* const array, const T expected_value, const size_t num_elements) {
|
||||
ArrayFindIfNot(array, array + num_elements, expected_value);
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
void PitchedMemoryVerify(T* const ptr, const size_t pitch, const size_t width, const size_t height,
|
||||
const size_t depth, F expected_value_generator) {
|
||||
for (size_t z = 0; z < depth; ++z) {
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
for (size_t x = 0; x < width; ++x) {
|
||||
const auto slice = reinterpret_cast<uint8_t*>(ptr) + pitch * height * z;
|
||||
const auto row = slice + pitch * y;
|
||||
if (reinterpret_cast<T*>(row)[x] != expected_value_generator(x, y, z)) {
|
||||
INFO("Mismatch at indices: " << x << ", " << y << ", " << z);
|
||||
REQUIRE(reinterpret_cast<T*>(row)[x] == expected_value_generator(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
void PitchedMemorySet(T* const ptr, const size_t pitch, const size_t width, const size_t height,
|
||||
const size_t depth, F expected_value_generator) {
|
||||
for (size_t z = 0; z < depth; ++z) {
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
for (size_t x = 0; x < width; ++x) {
|
||||
const auto slice = reinterpret_cast<uint8_t*>(ptr) + pitch * height * z;
|
||||
const auto row = slice + pitch * y;
|
||||
reinterpret_cast<T*>(row)[x] = expected_value_generator(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void VectorIncrement(T* const vec, const T increment_value, 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) {
|
||||
vec[i] += increment_value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> __global__ void VectorSet(T* const vec, const T value, 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) {
|
||||
vec[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Will execute for atleast interval milliseconds
|
||||
static __global__ void Delay(uint32_t interval, const uint32_t ticks_per_ms) {
|
||||
while (interval--) {
|
||||
uint64_t start = clock();
|
||||
while (clock() - start < ticks_per_ms) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__global__ void Iota(T* const out, size_t pitch, size_t w, size_t h, size_t d) {
|
||||
const auto x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const auto y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
const auto z = blockIdx.z * blockDim.z + threadIdx.z;
|
||||
if (x < w && y < h && z < d) {
|
||||
char* const slice = reinterpret_cast<char*>(out) + pitch * h * z;
|
||||
char* const row = slice + pitch * y;
|
||||
reinterpret_cast<T*>(row)[x] = z * w * h + y * w + x;
|
||||
}
|
||||
}
|
||||
|
||||
inline void LaunchDelayKernel(const std::chrono::milliseconds interval, const hipStream_t stream) {
|
||||
int ticks_per_ms = 0;
|
||||
// Clock rate is in kHz => number of clock ticks in a millisecond
|
||||
HIP_CHECK(hipDeviceGetAttribute(&ticks_per_ms, hipDeviceAttributeClockRate, 0));
|
||||
Delay<<<1, 1, 0, stream>>>(interval.count(), ticks_per_ms);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <typename... Attributes>
|
||||
inline bool DeviceAttributesSupport(const int device, Attributes... attributes) {
|
||||
constexpr auto DeviceAttributeSupport = [](const int device,
|
||||
const hipDeviceAttribute_t attribute) {
|
||||
int value = 0;
|
||||
HIP_CHECK(hipDeviceGetAttribute(&value, attribute, device));
|
||||
return value;
|
||||
};
|
||||
return (... && DeviceAttributeSupport(device, attributes));
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
if(NOT RTC_TESTING)
|
||||
set(TEST_SRC
|
||||
Set.cpp
|
||||
)
|
||||
|
||||
add_library(KERNELS EXCLUDE_FROM_ALL OBJECT ${TEST_SRC})
|
||||
target_compile_options(KERNELS PUBLIC -std=c++17)
|
||||
endif()
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <kernels.hh>
|
||||
|
||||
__global__ void Set(int* Ad, int val) {
|
||||
int tx = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
Ad[tx] = val;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace HipTest {
|
||||
template <typename T> __global__ void vectorADD(const T* A_d, const T* B_d, T* C_d, size_t NELEM) {
|
||||
size_t offset = (blockIdx.x * blockDim.x + threadIdx.x);
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
|
||||
for (size_t i = offset; i < NELEM; i += stride) {
|
||||
C_d[i] = A_d[i] + B_d[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# Common Tests
|
||||
set(LINUX_TEST_SRC
|
||||
childMalloc.cc
|
||||
hipDeviceComputeCapabilityMproc.cc
|
||||
hipDeviceGetPCIBusIdMproc.cc
|
||||
hipDeviceTotalMemMproc.cc
|
||||
hipGetDeviceAttributeMproc.cc
|
||||
hipGetDeviceCountMproc.cc
|
||||
hipGetDevicePropertiesMproc.cc
|
||||
hipSetGetDeviceMproc.cc
|
||||
hipIpcMemAccessTest.cc
|
||||
hipMallocConcurrencyMproc.cc
|
||||
hipMemCoherencyTstMProc.cc
|
||||
hipIpcEventHandle.cc
|
||||
hipIpcMemAccessTest.cc
|
||||
deviceAllocationMproc.cc
|
||||
hipNoGpuTsts.cc
|
||||
hipMemGetInfo.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)
|
||||
|
||||
# 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_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})
|
||||
endif()
|
||||
add_dependencies(build_tests dummy_kernel.code)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
#include <hip_test_kernels.hh>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
|
||||
bool testMallocFromChild() {
|
||||
int fd[2];
|
||||
pid_t childpid;
|
||||
bool testResult = false;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
childpid = fork();
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
|
||||
return testResult;
|
||||
|
||||
} else if (!childpid) { // Child
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
char* A_d = nullptr;
|
||||
hipError_t ret = hipMalloc(&A_d, 1024);
|
||||
|
||||
printf("hipMalloc returned : %s\n", hipGetErrorString(ret));
|
||||
if (ret == hipSuccess)
|
||||
testResult = true;
|
||||
else
|
||||
testResult = false;
|
||||
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("ChildMalloc") {
|
||||
auto res = testMallocFromChild();
|
||||
REQUIRE(res == true);
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
#include <hip_test_kernels.hh>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#define SIZE 2097152
|
||||
// GPU threads
|
||||
#define BLOCKSIZE 512
|
||||
#define GRIDSIZE 256
|
||||
|
||||
__device__ static char* dev_common_ptr = nullptr;
|
||||
|
||||
/**
|
||||
* This kernel allocates a memory chunk using malloc().
|
||||
*/
|
||||
static __global__ void kerTestDeviceMalloc(size_t size) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate
|
||||
if (myId == 0) {
|
||||
dev_common_ptr = reinterpret_cast<char*> (malloc(size));
|
||||
if (dev_common_ptr == nullptr) {
|
||||
printf("Device Allocation Failed! \n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel writes to the memory location allocated in kernel
|
||||
* kerTestDeviceMalloc or kerTestDeviceNew.
|
||||
*/
|
||||
static __global__ void kerTestDeviceWrite() {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate
|
||||
if (dev_common_ptr == nullptr) {
|
||||
printf("Device Allocation Failed! \n");
|
||||
return;
|
||||
}
|
||||
*(dev_common_ptr + myId) = SCHAR_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel frees the memory chunk allocated in kernel
|
||||
* kerTestDeviceMalloc using free().
|
||||
*/
|
||||
static __global__ void kerTestDeviceFree(int *result) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate
|
||||
if (myId == 0) {
|
||||
if (dev_common_ptr != nullptr) {
|
||||
*result = 1;
|
||||
for (int idx = 0; idx < (BLOCKSIZE*GRIDSIZE); idx++) {
|
||||
if (*(dev_common_ptr + myId) != SCHAR_MAX) {
|
||||
*result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(dev_common_ptr);
|
||||
} else {
|
||||
*result = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel allocates a memory chunk using new operator.
|
||||
*/
|
||||
static __global__ void kerTestDeviceNew(size_t size) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate
|
||||
if (myId == 0) {
|
||||
dev_common_ptr = new char[size];
|
||||
if (dev_common_ptr == nullptr) {
|
||||
printf("Device Allocation Failed! \n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel frees the memory chunk allocated in kernel
|
||||
* kerTestDeviceNew using delete operator.
|
||||
*/
|
||||
static __global__ void kerTestDeviceDelete(int *result) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate
|
||||
if (myId == 0) {
|
||||
if (dev_common_ptr != nullptr) {
|
||||
*result = 1;
|
||||
for (int idx = 0; idx < (BLOCKSIZE*GRIDSIZE); idx++) {
|
||||
if (*(dev_common_ptr + myId) != SCHAR_MAX) {
|
||||
*result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete[] dev_common_ptr;
|
||||
} else {
|
||||
*result = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test device malloc()/new in both Parent and Child Process.
|
||||
* Allocate SIZE bytes in both parent and child process. Verify
|
||||
* the allocated size in both parent and child process.
|
||||
*/
|
||||
static bool testDeviceAllocMulProc(bool testmalloc) {
|
||||
int fd[2];
|
||||
pid_t childpid;
|
||||
bool testResult = false;
|
||||
size_t avail = 0, tot = 0;
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
// fork process
|
||||
childpid = fork();
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// Allocate in parent
|
||||
if (testmalloc) {
|
||||
kerTestDeviceMalloc<<<1, 1>>>(SIZE);
|
||||
} else {
|
||||
kerTestDeviceNew<<<1, 1>>>(SIZE);
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Check allocated memory size
|
||||
HIP_CHECK(hipMemGetInfo(&avail, &tot));
|
||||
if ((tot - avail) < SIZE) {
|
||||
return false;
|
||||
}
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
// At this point the child process exits.
|
||||
// Ensure that device memory allocated from child is freed.
|
||||
HIP_CHECK(hipMemGetInfo(&avail, &tot));
|
||||
if ((tot - avail) < SIZE) {
|
||||
testResult = false;
|
||||
}
|
||||
} else if (!childpid) { // Child
|
||||
// Wait for hipDeviceSetLimit() completion in parent.
|
||||
close(fd[0]);
|
||||
// Allocate in child
|
||||
if (testmalloc) {
|
||||
kerTestDeviceMalloc<<<1, 1>>>(SIZE);
|
||||
} else {
|
||||
kerTestDeviceNew<<<1, 1>>>(SIZE);
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Check allocated memory size
|
||||
HIP_CHECK(hipMemGetInfo(&avail, &tot));
|
||||
if ((tot - avail) < SIZE) {
|
||||
testResult = false;
|
||||
} else {
|
||||
testResult = true;
|
||||
}
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
}
|
||||
return testResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test device malloc()/new, write and free()/delete[]
|
||||
* from both Parent and Child Process. From both Parent and
|
||||
* Child Process invoke the kernel to allocate memory, the
|
||||
* kernel to write to the allocated memory and a third kernel
|
||||
* to verify the memory contents and free it.
|
||||
*/
|
||||
static bool testDeviceMemMulProc(bool testmalloc) {
|
||||
int fd[2];
|
||||
bool testResult = false;
|
||||
pid_t childpid;
|
||||
int testResultChild = 0;
|
||||
size_t size = BLOCKSIZE*GRIDSIZE;
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
// fork process
|
||||
childpid = fork();
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
int *result_d{nullptr}, *result_h{nullptr};
|
||||
HIP_CHECK(hipMalloc(&result_d, sizeof(int)));
|
||||
result_h = reinterpret_cast<int*> (malloc(sizeof(int)));
|
||||
REQUIRE(result_h != nullptr);
|
||||
// Allocate in parent
|
||||
if (testmalloc) {
|
||||
kerTestDeviceMalloc<<<1, 1>>>(size);
|
||||
} else {
|
||||
kerTestDeviceNew<<<1, 1>>>(size);
|
||||
}
|
||||
// Write
|
||||
kerTestDeviceWrite<<<GRIDSIZE, BLOCKSIZE>>>();
|
||||
// Free
|
||||
if (testmalloc) {
|
||||
kerTestDeviceFree<<<1, 1>>>(result_d);
|
||||
} else {
|
||||
kerTestDeviceDelete<<<1, 1>>>(result_d);
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
*result_h = 0;
|
||||
HIP_CHECK(hipMemcpy(result_h, result_d, sizeof(int),
|
||||
hipMemcpyDefault));
|
||||
if (*result_h == 0) {
|
||||
testResult = false;
|
||||
} else {
|
||||
testResult = true;
|
||||
}
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &testResultChild, sizeof(int));
|
||||
if (testResultChild == 0) {
|
||||
testResult &= false;
|
||||
} else {
|
||||
testResult &= true;
|
||||
}
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
HIP_CHECK(hipFree(result_d));
|
||||
free(result_h);
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
} else if (!childpid) { // Child
|
||||
// Wait for hipDeviceSetLimit() completion in parent.
|
||||
close(fd[0]);
|
||||
int *result_d{nullptr}, *result_h{nullptr};
|
||||
HIP_CHECK(hipMalloc(&result_d, sizeof(int)));
|
||||
result_h = reinterpret_cast<int*> (malloc(sizeof(int)));
|
||||
REQUIRE(result_h != nullptr);
|
||||
// Allocate in child
|
||||
if (testmalloc) {
|
||||
kerTestDeviceMalloc<<<1, 1>>>(size);
|
||||
} else {
|
||||
kerTestDeviceNew<<<1, 1>>>(size);
|
||||
}
|
||||
// Write
|
||||
kerTestDeviceWrite<<<GRIDSIZE, BLOCKSIZE>>>();
|
||||
// Free
|
||||
if (testmalloc) {
|
||||
kerTestDeviceFree<<<1, 1>>>(result_d);
|
||||
} else {
|
||||
kerTestDeviceDelete<<<1, 1>>>(result_d);
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
*result_h = 0;
|
||||
HIP_CHECK(hipMemcpy(result_h, result_d, sizeof(int),
|
||||
hipMemcpyDefault));
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], result_h, sizeof(int));
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
HIP_CHECK(hipFree(result_d));
|
||||
free(result_h);
|
||||
exit(0);
|
||||
}
|
||||
return testResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiprocess device side malloc test.
|
||||
*/
|
||||
TEST_CASE("Unit_deviceAllocation_Malloc_MultProcess") {
|
||||
auto res = testDeviceAllocMulProc(true);
|
||||
REQUIRE(res == true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiprocess device side new test.
|
||||
*/
|
||||
TEST_CASE("Unit_deviceAllocation_New_MultProcess") {
|
||||
auto res = testDeviceAllocMulProc(false);
|
||||
REQUIRE(res == true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiprocess device side malloc, write and free test.
|
||||
*/
|
||||
TEST_CASE("Unit_deviceAllocation_MallocFree_MultProcess") {
|
||||
auto res = testDeviceMemMulProc(true);
|
||||
REQUIRE(res == true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiprocess device side new, write and delete test.
|
||||
*/
|
||||
TEST_CASE("Unit_deviceAllocation_NewDelete_MultProcess") {
|
||||
auto res = testDeviceMemMulProc(false);
|
||||
REQUIRE(res == true);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright (c) 2015 - 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 "hip/hip_runtime.h"
|
||||
|
||||
extern "C" __global__ void dummy_ker() {
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
Copyright (c) 2021-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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* hipDeviceComputeCapability tests
|
||||
* Scenario: Validate behavior of hipDeviceComputeCapability for masked devices
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
||||
#define MAX_SIZE 30
|
||||
#define VISIBLE_DEVICE 0
|
||||
|
||||
/**
|
||||
* Fetches Gpu device count
|
||||
*/
|
||||
static void getDeviceCount(int *pdevCnt) {
|
||||
int fd[2], val = 0;
|
||||
pid_t childpid;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
#endif
|
||||
|
||||
childpid = fork();
|
||||
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &val, sizeof(val));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
|
||||
*pdevCnt = val;
|
||||
} else if (!childpid) { // Child
|
||||
int devCnt = 1;
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &devCnt, sizeof(devCnt));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else { // failure
|
||||
*pdevCnt = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs test on masked devices
|
||||
*/
|
||||
bool runMaskedDeviceTest(int actualNumGPUs) {
|
||||
bool testResult = true;
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
hipError_t err;
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
snprintf(visibleDeviceString, MAX_SIZE, "%d", VISIBLE_DEVICE);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
HIP_CHECK(hipInit(0));
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#endif
|
||||
|
||||
for (int count = 1;
|
||||
count < actualNumGPUs; count++) {
|
||||
int major, minor;
|
||||
err = hipDeviceComputeCapability(&major, &minor, count);
|
||||
if (err == hipSuccess) {
|
||||
testResult = false;
|
||||
} else {
|
||||
printf("hipDeviceComputeCapability: Error Code Returned: '%s'(%d)\n",
|
||||
hipGetErrorString(err), err);
|
||||
}
|
||||
}
|
||||
close(fd[0]);
|
||||
printf("testResult = %d \n", testResult);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
|
||||
} else if (cPid > 0) { // parent
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
return testResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate behavior of hipDeviceComputeCapability for masked devices.
|
||||
*/
|
||||
TEST_CASE("Unit_hipDeviceGet_MaskedDevices") {
|
||||
int count = -1;
|
||||
constexpr int ReqGPUs = 2;
|
||||
bool ret;
|
||||
|
||||
getDeviceCount(&count);
|
||||
|
||||
if (count >= ReqGPUs) {
|
||||
ret = runMaskedDeviceTest(count);
|
||||
REQUIRE(ret == true);
|
||||
} else {
|
||||
SUCCEED("Not enough GPUs to run the masked GPU tests");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __linux__
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) 2020-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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tests to
|
||||
* 1. Compare {pciDomainID, pciBusID, pciDeviceID} values
|
||||
* hipDeviceGetPCIBusId vs lspci
|
||||
* 2. Validate behavior of hipDeviceGetPCIBusId for masked devices
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define MAX_DEVICE_LENGTH 20
|
||||
#define MAX_SIZE 30
|
||||
#define VISIBLE_DEVICE 0
|
||||
|
||||
namespace hipDeviceGetPCIBusIdTests {
|
||||
|
||||
/**
|
||||
* Fetches Gpu device count
|
||||
*/
|
||||
void getDeviceCount(int *pdevCnt) {
|
||||
int fd[2], val = 0;
|
||||
pid_t childpid;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
#endif
|
||||
|
||||
childpid = fork();
|
||||
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &val, sizeof(val));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
|
||||
*pdevCnt = val;
|
||||
} else if (!childpid) { // Child
|
||||
int devCnt = 1;
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &devCnt, sizeof(devCnt));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else { // failure
|
||||
*pdevCnt = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs test on masked devices
|
||||
*/
|
||||
bool testWithMaskedDevices(int actualNumGPUs) {
|
||||
bool testResult = true;
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
hipError_t err;
|
||||
char pciBusId[MAX_DEVICE_LENGTH];
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
snprintf(visibleDeviceString, MAX_SIZE, "%d", VISIBLE_DEVICE);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
HIP_CHECK(hipInit(0));
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#endif
|
||||
|
||||
for (int count = 1;
|
||||
count < actualNumGPUs; count++) {
|
||||
err = hipDeviceGetPCIBusId(pciBusId, MAX_DEVICE_LENGTH, count);
|
||||
if (err == hipSuccess) {
|
||||
testResult &= false;
|
||||
} else {
|
||||
printf("hipGetDeviceProperties: Error Code Returned: '%s'(%d)\n",
|
||||
hipGetErrorString(err), err);
|
||||
}
|
||||
}
|
||||
close(fd[0]);
|
||||
printf("testResult = %d \n", testResult);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
|
||||
} else if (cPid > 0) { // parent
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
return testResult;
|
||||
}
|
||||
|
||||
|
||||
bool getPciBusId(int deviceCount,
|
||||
char **hipDeviceList) {
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
HIP_CHECK(hipDeviceGetPCIBusId(hipDeviceList[i], MAX_DEVICE_LENGTH, i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace hipDeviceGetPCIBusIdTests
|
||||
|
||||
|
||||
/**
|
||||
* Scenario: Validate behavior of hipDeviceGetPCIBusId for masked devices.
|
||||
*/
|
||||
TEST_CASE("Unit_hipDeviceGetPCIBusId_MaskedDevices") {
|
||||
int count = -1;
|
||||
constexpr int ReqGPUs = 2;
|
||||
bool ret;
|
||||
|
||||
hipDeviceGetPCIBusIdTests::getDeviceCount(&count);
|
||||
|
||||
if (count >= ReqGPUs) {
|
||||
ret = hipDeviceGetPCIBusIdTests::testWithMaskedDevices(count);
|
||||
REQUIRE(ret == true);
|
||||
} else {
|
||||
SUCCEED("Not enough GPUs to run the masked GPU tests");
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare {pciDomainID, pciBusID, pciDeviceID} values
|
||||
* hipDeviceGetPCIBusId vs lspci
|
||||
*/
|
||||
TEST_CASE("Unit_hipDeviceGetPCIBusId_CheckPciBusIDWithLspci") {
|
||||
FILE *fpipe;
|
||||
{
|
||||
// Check if lspci is installed, if not, don't proceed
|
||||
char const *cmd = "lspci --version";
|
||||
char *lspciCheck{nullptr};
|
||||
constexpr auto MaxLen = 50;
|
||||
char temp[MaxLen]{};
|
||||
|
||||
fpipe = popen(cmd, "r");
|
||||
REQUIRE_FALSE(fpipe == nullptr);
|
||||
|
||||
lspciCheck = fgets(temp, MaxLen, fpipe);
|
||||
pclose(fpipe);
|
||||
|
||||
if (lspciCheck == nullptr) {
|
||||
WARN("Skipping test as lspci is not found in system");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int deviceCount = 0;
|
||||
HIP_CHECK(hipGetDeviceCount(&deviceCount));
|
||||
REQUIRE_FALSE(deviceCount == 0);
|
||||
// Allocate an array of pointer to characters
|
||||
char **hipDeviceList = new char*[deviceCount];
|
||||
REQUIRE_FALSE(hipDeviceList == nullptr);
|
||||
char **pciDeviceList = new char*[deviceCount];
|
||||
REQUIRE_FALSE(pciDeviceList == nullptr);
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
hipDeviceList[i] = new char[MAX_DEVICE_LENGTH];
|
||||
REQUIRE_FALSE(hipDeviceList[i] == nullptr);
|
||||
pciDeviceList[i] = new char[MAX_DEVICE_LENGTH];
|
||||
REQUIRE_FALSE(pciDeviceList[i] == nullptr);
|
||||
}
|
||||
|
||||
hipDeviceGetPCIBusIdTests::getPciBusId(deviceCount, hipDeviceList);
|
||||
char const *command = nullptr;
|
||||
// Get lspci device list and compare with hip device list
|
||||
if ((TestContext::get()).isNvidia()) {
|
||||
command = "lspci -D | grep controller | grep NVIDIA | "
|
||||
"cut -d ' ' -f 1";
|
||||
} else {
|
||||
command = "lspci -D | grep controller | grep AMD/ATI | "
|
||||
"cut -d ' ' -f 1";
|
||||
}
|
||||
fpipe = popen(command, "r");
|
||||
REQUIRE_FALSE(fpipe == nullptr);
|
||||
|
||||
int index = 0;
|
||||
int deviceMatchCount = 0;
|
||||
constexpr auto cmpLen = 10;
|
||||
while (fgets(pciDeviceList[index], MAX_DEVICE_LENGTH, fpipe)) {
|
||||
bool bMatchFound = false;
|
||||
for (int deviceNo = 0; deviceNo < deviceCount; deviceNo++) {
|
||||
if (!strncasecmp(pciDeviceList[index], hipDeviceList[deviceNo],
|
||||
cmpLen)) {
|
||||
deviceMatchCount++;
|
||||
bMatchFound = true;
|
||||
}
|
||||
}
|
||||
if (bMatchFound == false) {
|
||||
printf("PCI device: %s is not reported by HIP\n",
|
||||
pciDeviceList[index]);
|
||||
}
|
||||
index++;
|
||||
if (index >= deviceCount) break;
|
||||
}
|
||||
// Deallocate
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
delete hipDeviceList[i];
|
||||
}
|
||||
delete[] hipDeviceList;
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
delete pciDeviceList[i];
|
||||
}
|
||||
delete[] pciDeviceList;
|
||||
pclose(fpipe);
|
||||
|
||||
REQUIRE(deviceMatchCount == deviceCount);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright (c) 2020-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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* hipDeviceTotalMem tests
|
||||
* Scenario: Validate behavior of hipDeviceTotalMem for masked devices.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define MAX_SIZE 30
|
||||
#define VISIBLE_DEVICE 0
|
||||
|
||||
/**
|
||||
* Fetches Gpu device count
|
||||
*/
|
||||
static void getDeviceCount(int *pdevCnt) {
|
||||
int fd[2], val = 0;
|
||||
pid_t childpid;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
#endif
|
||||
|
||||
childpid = fork();
|
||||
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &val, sizeof(val));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
|
||||
*pdevCnt = val;
|
||||
} else if (!childpid) { // Child
|
||||
int devCnt = 1;
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &devCnt, sizeof(devCnt));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else { // failure
|
||||
*pdevCnt = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Func tries to fetch total memory of masked devices and returns pass/fail.
|
||||
*/
|
||||
static bool getTotalMemoryOfMaskedDevices(int actualNumGPUs) {
|
||||
bool testResult = true;
|
||||
int fd[2];
|
||||
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
hipError_t err;
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
snprintf(visibleDeviceString, MAX_SIZE, "%d", VISIBLE_DEVICE);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
HIP_CHECK(hipInit(0));
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#endif
|
||||
|
||||
for (int count = 1;
|
||||
count < actualNumGPUs; count++) {
|
||||
size_t totMem;
|
||||
err = hipDeviceTotalMem(&totMem, count);
|
||||
if (err == hipSuccess) {
|
||||
testResult &= false;
|
||||
} else {
|
||||
printf("hipDeviceTotalMem: Error Code Returned: '%s'(%d)\n",
|
||||
hipGetErrorString(err), err);
|
||||
}
|
||||
}
|
||||
close(fd[0]);
|
||||
printf("testResult = %d \n", testResult);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
|
||||
} else if (cPid > 0) { // parent
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
return testResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scenario: Validate behavior of hipDeviceTotalMem for masked devices.
|
||||
*/
|
||||
TEST_CASE("Unit_hipDeviceTotalMem_MaskedDevices") {
|
||||
int count = -1;
|
||||
constexpr int ReqGPUs = 2;
|
||||
bool ret;
|
||||
|
||||
getDeviceCount(&count);
|
||||
|
||||
if (count >= ReqGPUs) {
|
||||
ret = getTotalMemoryOfMaskedDevices(count);
|
||||
REQUIRE(ret == true);
|
||||
} else {
|
||||
SUCCEED("Not enough GPUs to run the masked GPU tests");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright (c) 2020-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* hipGetDeviceAttribute tests
|
||||
* Scenario: Validate behavior of hipGetDeviceAttribute for masked devices.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <iostream>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define MAX_SIZE 30
|
||||
#define VISIBLE_DEVICE 0
|
||||
|
||||
/**
|
||||
* Fetches Gpu device count
|
||||
*/
|
||||
static void getDeviceCount(int *pdevCnt) {
|
||||
int fd[2], val = 0;
|
||||
pid_t childpid;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
#endif
|
||||
|
||||
childpid = fork();
|
||||
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &val, sizeof(val));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
|
||||
*pdevCnt = val;
|
||||
} else if (!childpid) { // Child
|
||||
int devCnt = 1;
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &devCnt, sizeof(devCnt));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else { // failure
|
||||
*pdevCnt = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to fetch device attribute of masked devices and returns pass/fail.
|
||||
*/
|
||||
static bool validateGetAttributeOfMaskedDevices(int actualNumGPUs) {
|
||||
bool testResult = true;
|
||||
int fd[2];
|
||||
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
hipError_t err;
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
snprintf(visibleDeviceString, MAX_SIZE, "%d", VISIBLE_DEVICE);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
HIP_CHECK(hipInit(0));
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#endif
|
||||
|
||||
for (int count = 1;
|
||||
count < actualNumGPUs; count++) {
|
||||
int pi = -1;
|
||||
err = hipDeviceGetAttribute(&pi, hipDeviceAttributePciBusId, count);
|
||||
if (err == hipSuccess) {
|
||||
testResult &= false;
|
||||
} else {
|
||||
printf("hipDeviceGetAttribute: Error Code Returned: '%s'(%d)\n",
|
||||
hipGetErrorString(err), err);
|
||||
}
|
||||
}
|
||||
close(fd[0]);
|
||||
printf("testResult = %d \n", testResult);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
|
||||
} else if (cPid > 0) { // parent
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
return testResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scenario: Validate behavior of hipDeviceGetAttribute for masked devices.
|
||||
*/
|
||||
TEST_CASE("Unit_hipDeviceGetAttribute_MaskedDevices") {
|
||||
int count = -1;
|
||||
constexpr int ReqGPUs = 2;
|
||||
bool ret;
|
||||
|
||||
getDeviceCount(&count);
|
||||
|
||||
if (count >= ReqGPUs) {
|
||||
ret = validateGetAttributeOfMaskedDevices(count);
|
||||
REQUIRE(ret == true);
|
||||
} else {
|
||||
SUCCEED("Not enough GPUs to run the masked GPU tests");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright (c) 2020-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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* hipGetDeviceCount tests
|
||||
* Scenario: Validates the value of numDevices when devices are hidden.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define MAX_SIZE 30
|
||||
#define VISIBLE_DEVICE 0
|
||||
|
||||
/**
|
||||
* Validate behavior of hipGetDeviceCount for masked devices.
|
||||
*/
|
||||
TEST_CASE("Unit_hipGetDeviceCount_MaskedDevices") {
|
||||
int numDevices = 0;
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
snprintf(visibleDeviceString, MAX_SIZE, "%d", VISIBLE_DEVICE);
|
||||
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#endif
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&numDevices));
|
||||
REQUIRE(numDevices == 1);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Scenario: Validate behavior of hipGetDeviceProperties for masked devices.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
||||
#define MAX_SIZE 30
|
||||
#define VISIBLE_DEVICE 0
|
||||
|
||||
/**
|
||||
* Fetches Gpu device count
|
||||
*/
|
||||
static void getDeviceCount(int *pdevCnt) {
|
||||
int fd[2], val = 0;
|
||||
pid_t childpid;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
#endif
|
||||
|
||||
childpid = fork();
|
||||
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &val, sizeof(val));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
|
||||
*pdevCnt = val;
|
||||
} else if (!childpid) { // Child
|
||||
int devCnt = 1;
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &devCnt, sizeof(devCnt));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else { // failure
|
||||
*pdevCnt = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tries to fetch device properties of masked devices and returns pass/fail.
|
||||
*/
|
||||
static bool validateGetPropsOfMaskedDevices(int actualNumGPUs) {
|
||||
bool testResult = true;
|
||||
int fd[2];
|
||||
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
hipError_t err;
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
snprintf(visibleDeviceString, MAX_SIZE, "%d", VISIBLE_DEVICE);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
HIP_CHECK(hipInit(0));
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#endif
|
||||
|
||||
for (int count = 1;
|
||||
count < actualNumGPUs; count++) {
|
||||
hipDeviceProp_t prop;
|
||||
err = hipGetDeviceProperties(&prop, count);
|
||||
if (err == hipSuccess) {
|
||||
testResult &= false;
|
||||
} else {
|
||||
printf("hipGetDeviceProperties: Error Code Returned: '%s'(%d)\n",
|
||||
hipGetErrorString(err), err);
|
||||
}
|
||||
}
|
||||
close(fd[0]);
|
||||
printf("testResult = %d \n", testResult);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
|
||||
} else if (cPid > 0) { // parent
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
return testResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Scenario: Validate behavior of hipGetDeviceProperties for masked devices.
|
||||
*/
|
||||
TEST_CASE("Unit_hipGetDeviceProperties_MaskedDevices") {
|
||||
int count = -1;
|
||||
constexpr int ReqGPUs = 2;
|
||||
bool ret;
|
||||
|
||||
getDeviceCount(&count);
|
||||
|
||||
if (count >= ReqGPUs) {
|
||||
ret = validateGetPropsOfMaskedDevices(count);
|
||||
REQUIRE(ret == true);
|
||||
} else {
|
||||
SUCCEED("Not enough GPUs to run the masked GPU tests");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
Testcase Scenarios
|
||||
------------------
|
||||
Functional:
|
||||
1) Validate usecase of Event handle along with memory handle across multiple
|
||||
processes with complex scenario.
|
||||
|
||||
Negative/Argument Validation:
|
||||
1) Get event handle with eventHandle(nullptr).
|
||||
2) Get event handle with event(nullptr).
|
||||
3) Get event handle with invalid event object.
|
||||
4) Get event handle for event allocated without Interprocess flag.
|
||||
5) Open event handle with event(nullptr).
|
||||
6) Open event handle with eventHandle as invalid.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define BUF_SIZE 4096
|
||||
#define MAX_DEVICES 16
|
||||
|
||||
|
||||
typedef struct ipcEventInfo {
|
||||
int device;
|
||||
pid_t pid;
|
||||
hipIpcEventHandle_t eventHandle;
|
||||
hipIpcMemHandle_t memHandle;
|
||||
} ipcEventInfo_t;
|
||||
|
||||
typedef struct ipcDevices {
|
||||
int count;
|
||||
int ordinals[MAX_DEVICES];
|
||||
} ipcDevices_t;
|
||||
|
||||
typedef struct ipcBarrier {
|
||||
int count;
|
||||
bool sense;
|
||||
bool allExit;
|
||||
} ipcBarrier_t;
|
||||
|
||||
/**
|
||||
Get device count and list down devices with
|
||||
P2P access with Device 0.
|
||||
*/
|
||||
void getDevices(ipcDevices_t *devices) {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (!pid) {
|
||||
// HIP APIs are called in child process,
|
||||
// to avoid HIP Initialization in main process.
|
||||
int i, devCnt{};
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
|
||||
if (devCnt < 2) {
|
||||
devices->count = 0;
|
||||
WARN("Count less than expected number of devices");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// Device 0
|
||||
devices->ordinals[0] = 0;
|
||||
devices->count = 1;
|
||||
|
||||
// Check possibility for peer accesses, relevant to our tests
|
||||
INFO("Checking GPU(s) for support of p2p memory access ");
|
||||
INFO("Between GPU0 and other GPU(s)");
|
||||
|
||||
int canPeerAccess_0i, canPeerAccess_i0;
|
||||
for (i = 1; i < devCnt; i++) {
|
||||
HIP_CHECK(hipDeviceCanAccessPeer(&canPeerAccess_0i, 0, i));
|
||||
HIP_CHECK(hipDeviceCanAccessPeer(&canPeerAccess_i0, i, 0));
|
||||
|
||||
if (canPeerAccess_0i * canPeerAccess_i0) {
|
||||
devices->ordinals[i] = i;
|
||||
INFO("Two-way peer access is available between GPU"
|
||||
<< devices->ordinals[0] <<" and GPU"
|
||||
<< devices->ordinals[devices->count]);
|
||||
devices->count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
HIP_ASSERT(!status);
|
||||
}
|
||||
}
|
||||
|
||||
static ipcBarrier_t *g_Barrier{};
|
||||
static bool g_procSense;
|
||||
static int g_processCnt;
|
||||
|
||||
/**
|
||||
Calling process waits for other processes to signal/complete.
|
||||
*/
|
||||
void processBarrier() {
|
||||
int newCount = __sync_add_and_fetch(&g_Barrier->count, 1);
|
||||
|
||||
if (newCount == g_processCnt) {
|
||||
g_Barrier->count = 0;
|
||||
g_Barrier->sense = !g_procSense;
|
||||
|
||||
} else {
|
||||
while (g_Barrier->sense == g_procSense) {
|
||||
if (!g_Barrier->allExit) {
|
||||
sched_yield();
|
||||
} else {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_procSense = !g_procSense;
|
||||
}
|
||||
|
||||
|
||||
__global__ void computeKernel(int *dst, int *src, int num) {
|
||||
int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
dst[idx] = src[idx] / num;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1) Process 0 allocates buffer in GPU0 memory and exports the memory handle.
|
||||
* 2) Other processes opens memory handle of GPU0 memory, performs computation
|
||||
* and records event.
|
||||
* 3) Process 0 synchronizes event and validates the resulting buffer.
|
||||
*/
|
||||
void runMultiProcKernel(ipcEventInfo_t *shmEventInfo, int index) {
|
||||
int *d_ptr;
|
||||
int hData[BUF_SIZE]{};
|
||||
unsigned int seed = time(nullptr);
|
||||
|
||||
// Randomize data before computation
|
||||
for (int i = 0; i < BUF_SIZE; i++) {
|
||||
hData[i] = rand_r(&seed);
|
||||
}
|
||||
|
||||
HIP_CHECK(hipSetDevice(shmEventInfo[index].device));
|
||||
|
||||
if (index == 0) {
|
||||
int h_results[BUF_SIZE * MAX_DEVICES];
|
||||
hipEvent_t event[MAX_DEVICES];
|
||||
|
||||
HIP_CHECK(hipMalloc(&d_ptr, BUF_SIZE * g_processCnt * sizeof(int)));
|
||||
HIP_CHECK(hipIpcGetMemHandle(&shmEventInfo[0].memHandle, d_ptr));
|
||||
HIP_CHECK(hipMemcpy(d_ptr, hData,
|
||||
BUF_SIZE * sizeof(int), hipMemcpyHostToDevice));
|
||||
|
||||
// Barrier 1: Process0 will wait for all processes to create event handles,
|
||||
// signals device memory creation.
|
||||
processBarrier();
|
||||
|
||||
for (int i = 1; i < g_processCnt; i++) {
|
||||
HIP_CHECK(hipIpcOpenEventHandle(&event[i], shmEventInfo[i].eventHandle));
|
||||
}
|
||||
|
||||
// Barrier 2: Process0 waits for kernels to be launched
|
||||
// and the events to be recorded.
|
||||
processBarrier();
|
||||
|
||||
for (int i = 1; i < g_processCnt; i++) {
|
||||
HIP_CHECK(hipEventSynchronize(event[i]));
|
||||
}
|
||||
|
||||
HIP_CHECK(hipMemcpy(h_results, d_ptr + BUF_SIZE,
|
||||
BUF_SIZE * (g_processCnt - 1) * sizeof(int), hipMemcpyDeviceToHost));
|
||||
|
||||
// Barrier 3: Process0 signals event usage is done.
|
||||
processBarrier();
|
||||
HIP_CHECK(hipFree(d_ptr));
|
||||
for (int n = 1; n < g_processCnt; n++) {
|
||||
for (int i = 0; i < BUF_SIZE; i++) {
|
||||
if (hData[i]/(n + 1) != h_results[(n-1) * BUF_SIZE + i]) {
|
||||
WARN("Data validation error at index " << i << " n" << n);
|
||||
g_Barrier->allExit = true;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hipEvent_t event;
|
||||
HIP_CHECK(hipEventCreateWithFlags(&event,
|
||||
hipEventDisableTiming | hipEventInterprocess));
|
||||
HIP_CHECK(hipIpcGetEventHandle(&shmEventInfo[index].eventHandle, event));
|
||||
|
||||
// Barrier 1 : wait until proc 0 initializes device memory,
|
||||
// signals event creation.
|
||||
processBarrier();
|
||||
HIP_CHECK(hipIpcOpenMemHandle(reinterpret_cast<void **>(&d_ptr),
|
||||
shmEventInfo[0].memHandle,
|
||||
hipIpcMemLazyEnablePeerAccess));
|
||||
const dim3 threads(512, 1);
|
||||
const dim3 blocks(BUF_SIZE / threads.x, 1);
|
||||
hipLaunchKernelGGL(computeKernel, dim3(blocks), dim3(threads), 0, 0,
|
||||
d_ptr + index *BUF_SIZE, d_ptr, index + 1);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipEventRecord(event));
|
||||
|
||||
// Barrier 2 : Signals that event is recorded
|
||||
processBarrier();
|
||||
HIP_CHECK(hipIpcCloseMemHandle(d_ptr));
|
||||
|
||||
// Barrier 3 : wait for all the events to be used up by processes
|
||||
processBarrier();
|
||||
HIP_CHECK(hipEventDestroy(event));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Functional test demonstrating IPC event usage along with IPC memory handle
|
||||
*/
|
||||
TEST_CASE("Unit_hipIpcEventHandle_Functional") {
|
||||
ipcDevices_t *shmDevices;
|
||||
ipcEventInfo_t *shmEventInfo;
|
||||
shmDevices = reinterpret_cast<ipcDevices_t *> (mmap(NULL, sizeof(*shmDevices),
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0));
|
||||
REQUIRE(MAP_FAILED != shmDevices);
|
||||
|
||||
getDevices(shmDevices);
|
||||
|
||||
if (shmDevices->count < 2) {
|
||||
WARN("Test requires atleast two GPUs with P2P access. Skipping test.");
|
||||
return;
|
||||
}
|
||||
|
||||
g_processCnt = (shmDevices->count > MAX_DEVICES) ? MAX_DEVICES : shmDevices->count;
|
||||
|
||||
// Barrier is used to synchronize processes created.
|
||||
g_Barrier = reinterpret_cast<ipcBarrier_t *> (mmap(NULL, sizeof(*g_Barrier),
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0));
|
||||
REQUIRE(MAP_FAILED != g_Barrier);
|
||||
memset(g_Barrier, 0, sizeof(*g_Barrier));
|
||||
|
||||
// set local barrier sense flag
|
||||
g_procSense = 0;
|
||||
|
||||
// shared memory for Event and memHandle Info
|
||||
shmEventInfo = reinterpret_cast<ipcEventInfo_t *>(mmap(NULL,
|
||||
g_processCnt * sizeof(*shmEventInfo),
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0));
|
||||
REQUIRE(MAP_FAILED != shmEventInfo);
|
||||
|
||||
// initialize shared memory
|
||||
memset(shmEventInfo, 0, g_processCnt * sizeof(*shmEventInfo));
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (int i = 1; i < g_processCnt; i++) {
|
||||
int pid = fork();
|
||||
|
||||
if (!pid) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
shmEventInfo[i].pid = pid;
|
||||
}
|
||||
}
|
||||
|
||||
shmEventInfo[index].device = shmDevices->ordinals[index];
|
||||
|
||||
// Run the test
|
||||
runMultiProcKernel(shmEventInfo, index);
|
||||
|
||||
// Cleanup
|
||||
if (index == 0) {
|
||||
for (int i = 1; i < g_processCnt; i++) {
|
||||
int status;
|
||||
waitpid(shmEventInfo[i].pid, &status, 0);
|
||||
HIP_ASSERT(WIFEXITED(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Performs API Parameter validation.
|
||||
*/
|
||||
TEST_CASE("Unit_hipIpcEventHandle_ParameterValidation") {
|
||||
hipEvent_t event;
|
||||
hipIpcEventHandle_t eventHandle;
|
||||
hipError_t ret;
|
||||
HIP_CHECK(hipEventCreateWithFlags(&event,
|
||||
hipEventDisableTiming | hipEventInterprocess));
|
||||
#if HT_AMD
|
||||
// Test disabled for nvidia due to segfault with cuda api
|
||||
SECTION("Get event handle with eventHandle(nullptr)") {
|
||||
ret = hipIpcGetEventHandle(nullptr, event);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("Get event handle with event(nullptr)") {
|
||||
ret = hipIpcGetEventHandle(&eventHandle, nullptr);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
SECTION("Get event handle with handle == nullptr and event == nullptr") {
|
||||
HIP_CHECK_ERROR(hipIpcGetEventHandle(nullptr, nullptr), hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
SECTION("Get event handle with invalid event object") {
|
||||
hipEvent_t eventUninit{};
|
||||
ret = hipIpcGetEventHandle(&eventHandle, eventUninit);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
SECTION("Get event handle for event allocated without Interprocess flag") {
|
||||
hipEvent_t eventNoIpc;
|
||||
HIP_CHECK(hipEventCreateWithFlags(&eventNoIpc, hipEventDisableTiming));
|
||||
|
||||
ret = hipIpcGetEventHandle(&eventHandle, eventNoIpc);
|
||||
if ((ret != hipErrorInvalidResourceHandle) &&
|
||||
(ret != hipErrorInvalidConfiguration)) {
|
||||
INFO("Error returned : " << ret);
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Open event handle with event(nullptr)") {
|
||||
hipIpcEventHandle_t ipc_handle{};
|
||||
ret = hipIpcOpenEventHandle(nullptr, ipc_handle);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
SECTION("Open event handle with eventHandle as invalid") {
|
||||
hipIpcEventHandle_t ipc_handle{};
|
||||
hipEvent_t eventOut;
|
||||
ret = hipIpcOpenEventHandle(&eventOut, ipc_handle);
|
||||
if ((ret != hipErrorInvalidValue) && (ret != hipErrorMapFailed)) {
|
||||
INFO("Error returned : " << ret);
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Open handle in process that created it") {
|
||||
hipIpcEventHandle_t event_handle;
|
||||
hipEvent_t event1, event2;
|
||||
HIP_CHECK(hipEventCreateWithFlags(&event1, hipEventDisableTiming | hipEventInterprocess));
|
||||
HIP_CHECK(hipIpcGetEventHandle(&event_handle, event1));
|
||||
HIP_CHECK_ERROR(hipIpcOpenEventHandle(&event2, event_handle), hipErrorInvalidContext);
|
||||
HIP_CHECK(hipEventDestroy(event1));
|
||||
}
|
||||
|
||||
// Disabled on AMD because of return value mismatch - EXSWHTEC-41
|
||||
#if HT_NVIDIA
|
||||
SECTION("Event created with no flags") {
|
||||
hipEvent_t event;
|
||||
hipIpcEventHandle_t event_handle;
|
||||
|
||||
HIP_CHECK(hipEventCreate(&event));
|
||||
HIP_CHECK_ERROR(hipIpcGetEventHandle(&event_handle, event), hipErrorInvalidResourceHandle);
|
||||
HIP_CHECK(hipEventDestroy(event));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
1)Testcase verifies the hipIpcMemAccess APIs by creating memory handle
|
||||
in parent process and access it in child process.
|
||||
2)Test case performs Parameter validation of hipIpcMemAccess APIs.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define NUM_ELMTS 1024
|
||||
#define NUM_THREADS 10
|
||||
|
||||
|
||||
typedef struct mem_handle {
|
||||
int device;
|
||||
hipIpcMemHandle_t memHandle;
|
||||
bool IfTestPassed;
|
||||
} hip_ipc_t;
|
||||
|
||||
|
||||
|
||||
// This testcase verifies the hipIpcMemAccess APIs as follows
|
||||
// The following program spawns a child process and does the following
|
||||
// Parent iterate through each device, create memory -- create hipIpcMemhandle
|
||||
// stores the mem handle in mmaped memory, release the child using sem_post()
|
||||
// and wait for child to release itself(parent process)
|
||||
// child process:
|
||||
// Child process get the ipc mem handle using hipIpcOpenMemHandle
|
||||
// Iterate through all the available gpus and do Device to Device copies
|
||||
// and check for data consistencies and close the hipIpcCloseMemHandle
|
||||
// release the parent and wait for parent to release itself(child)
|
||||
|
||||
TEST_CASE("Unit_hipIpcMemAccess_Semaphores") {
|
||||
hip_ipc_t *shrd_mem = NULL;
|
||||
pid_t pid;
|
||||
size_t N = 1024;
|
||||
size_t Nbytes = N * sizeof(int);
|
||||
int *A_d{nullptr}, *B_d{nullptr}, *C_d{nullptr};
|
||||
int *A_h{nullptr}, *C_h{nullptr};
|
||||
sem_t *sem_ob1{nullptr}, *sem_ob2{nullptr};
|
||||
int Num_devices = 0, CanAccessPeer = 0;
|
||||
|
||||
std::string cmd_line = "rm -rf /dev/shm/sem.my-sem-object*";
|
||||
int res = system(cmd_line.c_str());
|
||||
REQUIRE(res != -1);
|
||||
sem_ob1 = sem_open("/my-sem-object1", O_CREAT|O_EXCL, 0660, 0);
|
||||
sem_ob2 = sem_open("/my-sem-object2", O_CREAT|O_EXCL, 0660, 0);
|
||||
REQUIRE(sem_ob1 != SEM_FAILED);
|
||||
REQUIRE(sem_ob2 != SEM_FAILED);
|
||||
|
||||
shrd_mem = reinterpret_cast<hip_ipc_t *>(mmap(NULL, sizeof(hip_ipc_t),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANONYMOUS,
|
||||
0, 0));
|
||||
REQUIRE(shrd_mem != NULL);
|
||||
shrd_mem->IfTestPassed = true;
|
||||
HipTest::initArrays<int>(nullptr, nullptr, nullptr,
|
||||
&A_h, nullptr, &C_h, N, false);
|
||||
pid = fork();
|
||||
if (pid != 0) {
|
||||
// Parent process
|
||||
HIP_CHECK(hipGetDeviceCount(&Num_devices));
|
||||
for (int i = 0; i < Num_devices; ++i) {
|
||||
if (shrd_mem->IfTestPassed == true) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipMalloc(&A_d, Nbytes));
|
||||
HIP_CHECK(hipIpcGetMemHandle(reinterpret_cast<hipIpcMemHandle_t *>
|
||||
(&shrd_mem->memHandle),
|
||||
A_d));
|
||||
HIP_CHECK(hipMemcpy(A_d, A_h, Nbytes, hipMemcpyHostToDevice));
|
||||
shrd_mem->device = i;
|
||||
if ((sem_post(sem_ob1)) == -1) {
|
||||
// Need to use inline function to release resources.
|
||||
shrd_mem->IfTestPassed = false;
|
||||
WARN("sem_post() call failed in parent process.");
|
||||
}
|
||||
if ((sem_wait(sem_ob2)) == -1) {
|
||||
shrd_mem->IfTestPassed = false;
|
||||
WARN("sem_wait() call failed in parent process.");
|
||||
}
|
||||
HIP_CHECK(hipFree(A_d));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Child process
|
||||
HIP_CHECK(hipGetDeviceCount(&Num_devices));
|
||||
for (int j = 0; j < Num_devices; ++j) {
|
||||
HIP_CHECK(hipSetDevice(j));
|
||||
if ((sem_wait(sem_ob1)) == -1) {
|
||||
shrd_mem->IfTestPassed = false;
|
||||
WARN("sem_wait() call failed in child process.");
|
||||
if ((sem_post(sem_ob2)) == -1) {
|
||||
shrd_mem->IfTestPassed = false;
|
||||
WARN("sem_post() call on sem_ob2 failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < Num_devices; ++i) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipDeviceCanAccessPeer(&CanAccessPeer, i, shrd_mem->device));
|
||||
if (CanAccessPeer == 1) {
|
||||
HIP_CHECK(hipMalloc(&C_d, Nbytes));
|
||||
HIP_CHECK(hipIpcOpenMemHandle(reinterpret_cast<void **>(&B_d),
|
||||
shrd_mem->memHandle,
|
||||
hipIpcMemLazyEnablePeerAccess));
|
||||
HIP_CHECK(hipMemcpy(C_d, B_d, Nbytes, hipMemcpyDeviceToDevice));
|
||||
HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost));
|
||||
HipTest::checkTest<int>(A_h, C_h, N);
|
||||
memset(reinterpret_cast<void*>(C_h), 0, Nbytes);
|
||||
// Checking if the data obtained from Ipc shared memory is consistent
|
||||
HIP_CHECK(hipMemcpy(C_h, B_d, Nbytes, hipMemcpyDeviceToHost));
|
||||
HipTest::checkTest<int>(A_h, C_h, N);
|
||||
HIP_CHECK(hipIpcCloseMemHandle(reinterpret_cast<void*>(B_d)));
|
||||
HIP_CHECK(hipFree(C_d));
|
||||
}
|
||||
}
|
||||
if ((sem_post(sem_ob2)) == -1) {
|
||||
shrd_mem->IfTestPassed = false;
|
||||
WARN("sem_post() call on sem_ob2 failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
if ((sem_unlink("/my-sem-object1")) == -1) {
|
||||
WARN("sem_unlink() call on /my-sem-object1 failed");
|
||||
}
|
||||
if ((sem_unlink("/my-sem-object2")) == -1) {
|
||||
WARN("sem_unlink() call on /my-sem-object2 failed");
|
||||
}
|
||||
int rFlag = 0;
|
||||
waitpid(pid, &rFlag, 0);
|
||||
REQUIRE(shrd_mem->IfTestPassed == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipIpcMemAccess_ParameterValidation") {
|
||||
hipIpcMemHandle_t MemHandle;
|
||||
hipIpcMemHandle_t MemHandleUninit;
|
||||
void *Ad{}, *Ad2{};
|
||||
hipError_t ret;
|
||||
|
||||
HIP_CHECK(hipMalloc(&Ad, 1024));
|
||||
|
||||
#if HT_AMD
|
||||
// Test is disabled for nvidia as api resulting in seg fault.
|
||||
SECTION("Get mem handle with handle as nullptr") {
|
||||
ret = hipIpcGetMemHandle(nullptr, Ad);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
#endif
|
||||
SECTION("Get mem handle with devptr as nullptr") {
|
||||
ret = hipIpcGetMemHandle(&MemHandle, nullptr);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
SECTION("Get mem handle with handle/devptr as nullptr") {
|
||||
ret = hipIpcGetMemHandle(nullptr, nullptr);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
SECTION("Get mem handle with valid devptr") {
|
||||
ret = hipIpcGetMemHandle(&MemHandle, Ad);
|
||||
REQUIRE(ret == hipSuccess);
|
||||
}
|
||||
|
||||
SECTION("Open mem handle with devptr as nullptr") {
|
||||
ret = hipIpcOpenMemHandle(nullptr, MemHandle,
|
||||
hipIpcMemLazyEnablePeerAccess);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
SECTION("Open mem handle with handle as un-initialized") {
|
||||
ret = hipIpcOpenMemHandle(&Ad2, MemHandleUninit,
|
||||
hipIpcMemLazyEnablePeerAccess);
|
||||
REQUIRE((ret == hipErrorInvalidValue || ret == hipErrorInvalidDevicePointer));
|
||||
}
|
||||
#if HT_AMD
|
||||
// Test is disabled for nvidia as api not returning expected value.
|
||||
SECTION("Open mem handle with flags as random value") {
|
||||
constexpr unsigned int flags = 123;
|
||||
HIP_CHECK(hipIpcGetMemHandle(&MemHandle, Ad));
|
||||
ret = hipIpcOpenMemHandle(&Ad2, MemHandle, flags);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
#endif
|
||||
SECTION("Close mem handle with devptr(nullptr)") {
|
||||
ret = hipIpcCloseMemHandle(nullptr);
|
||||
REQUIRE(ret == hipErrorInvalidValue);
|
||||
}
|
||||
|
||||
HIP_CHECK(hipFree(Ad));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
Testcase Scenarios :
|
||||
|
||||
1) Run hipMalloc() api/kernel code on same gpu parallely from parent and child
|
||||
processes, validate the results.
|
||||
|
||||
2) Execute hipMalloc() api simultaneously on all the gpus by spawning multiple
|
||||
child processes. Validate buffers allocated after running kernel code.
|
||||
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
#include <hip_test_kernels.hh>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Fetches Gpu device count
|
||||
*/
|
||||
static void getDeviceCount(int* pdevCnt) {
|
||||
int fd[2], val = 0;
|
||||
pid_t childpid;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef HT_NVIDIA
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
#endif
|
||||
|
||||
childpid = fork();
|
||||
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &val, sizeof(val));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(nullptr);
|
||||
|
||||
*pdevCnt = val;
|
||||
} else if (!childpid) { // Child
|
||||
int devCnt = 1;
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &devCnt, sizeof(devCnt));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else { // failure
|
||||
*pdevCnt = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates data consistency on supplied gpu
|
||||
*/
|
||||
static bool validateMemoryOnGPU(int gpu, bool concurOnOneGPU = false) {
|
||||
int *A_d, *B_d, *C_d;
|
||||
int *A_h, *B_h, *C_h;
|
||||
size_t prevAvl, prevTot, curAvl, curTot;
|
||||
bool TestPassed = true;
|
||||
constexpr auto N = 4 * 1024 * 1024;
|
||||
constexpr auto blocksPerCU = 6; // to hide latency
|
||||
constexpr auto threadsPerBlock = 256;
|
||||
size_t Nbytes = N * sizeof(int);
|
||||
|
||||
HIP_CHECK(hipSetDevice(gpu));
|
||||
HIP_CHECK(hipMemGetInfo(&prevAvl, &prevTot));
|
||||
HipTest::initArrays(&A_d, &B_d, &C_d, &A_h, &B_h, &C_h, N, false);
|
||||
HIP_CHECK(hipMemGetInfo(&curAvl, &curTot));
|
||||
|
||||
if (!concurOnOneGPU && (prevAvl < curAvl || prevTot != curTot)) {
|
||||
//In concurrent calls on one GPU, we cannot verify leaking in this way
|
||||
printf("%s : Memory allocation mismatch observed."
|
||||
"Possible memory leak.\n", __func__);
|
||||
TestPassed &= false;
|
||||
}
|
||||
|
||||
unsigned blocks = HipTest::setNumBlocks(blocksPerCU, threadsPerBlock, N);
|
||||
|
||||
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<const int*>(A_d),
|
||||
static_cast<const int*>(B_d), C_d, N);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipMemcpy(C_h, C_d, Nbytes, hipMemcpyDeviceToHost));
|
||||
|
||||
if (!HipTest::checkVectorADD(A_h, B_h, C_h, N)) {
|
||||
printf("Validation PASSED for gpu %d from pid %d\n", gpu, getpid());
|
||||
} else {
|
||||
printf("Validation FAILED for gpu %d from pid %d\n", gpu, getpid());
|
||||
TestPassed = false;
|
||||
}
|
||||
|
||||
HIP_CHECK(hipMemGetInfo(&prevAvl, &prevTot));
|
||||
HipTest::freeArrays(A_d, B_d, C_d, A_h, B_h, C_h, false);
|
||||
HIP_CHECK(hipMemGetInfo(&curAvl, &curTot));
|
||||
|
||||
if (!concurOnOneGPU && (curAvl < prevAvl || prevTot != curTot)) {
|
||||
// In concurrent calls on one GPU, we cannot verify leaking in this way
|
||||
UNSCOPED_INFO("validateMemoryOnGPU : Memory allocation mismatch observed."
|
||||
<< "Possible memory leak.");
|
||||
TestPassed = false;
|
||||
}
|
||||
|
||||
if (!concurOnOneGPU && (prevAvl != curAvl || prevTot != curTot)) {
|
||||
// In concurrent calls on one GPU, we cannot verify leaking in this way
|
||||
printf(
|
||||
"%s : Memory allocation mismatch observed."
|
||||
"Possible memory leak.\n",
|
||||
__func__);
|
||||
TestPassed = false;
|
||||
}
|
||||
|
||||
return TestPassed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parallel execution of parent and child on gpu0
|
||||
*/
|
||||
TEST_CASE("Unit_hipMalloc_ChildConcurrencyDefaultGpu") {
|
||||
int devCnt = 0, pid = 0;
|
||||
constexpr auto resSuccess = 1, resFailure = 2;
|
||||
bool TestPassed = true;
|
||||
|
||||
// Get GPU count
|
||||
getDeviceCount(&devCnt);
|
||||
REQUIRE(devCnt > 0);
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
INFO("Child_Concurrency_DefaultGpu : fork() returned error : " << pid);
|
||||
HIP_ASSERT(false);
|
||||
|
||||
} else if (!pid) { // Child process
|
||||
bool TestPassedChild = false;
|
||||
|
||||
// Allocates and validates memory on Gpu0 simultaneously with parent
|
||||
TestPassedChild = validateMemoryOnGPU(0, true);
|
||||
|
||||
if (TestPassedChild) {
|
||||
exit(resSuccess); // child exit with success status
|
||||
} else {
|
||||
exit(resFailure); // child exit with failure status
|
||||
}
|
||||
|
||||
} else { // Parent process
|
||||
int exitStatus;
|
||||
|
||||
// Allocates and validates memory on Gpu0 simultaneously with child
|
||||
TestPassed = validateMemoryOnGPU(0, true);
|
||||
|
||||
// Wait and get result from child
|
||||
pid = wait(&exitStatus);
|
||||
if ((WEXITSTATUS(exitStatus) == resFailure) || (pid < 0))
|
||||
TestPassed = false;
|
||||
}
|
||||
|
||||
REQUIRE(TestPassed == true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parallel execution of api on multiple gpus from
|
||||
* different child processes.
|
||||
*/
|
||||
TEST_CASE("Unit_hipMalloc_ChildConcurrencyMultiGpu") {
|
||||
int devCnt = 0, pid = 0;
|
||||
constexpr auto resSuccess = 1, resFailure = 2;
|
||||
|
||||
// Get GPU count
|
||||
getDeviceCount(&devCnt);
|
||||
REQUIRE(devCnt > 0);
|
||||
|
||||
// Spawn child for each GPU
|
||||
for (int gpu = 0; gpu < devCnt; gpu++) {
|
||||
if ((pid = fork()) < 0) {
|
||||
INFO("Child_Concurrency_MultiGpu : fork() returned error : " << pid);
|
||||
REQUIRE(false);
|
||||
|
||||
} else if (!pid) { // Child process
|
||||
bool TestPassedChild = false;
|
||||
TestPassedChild = validateMemoryOnGPU(gpu, true);
|
||||
|
||||
if (TestPassedChild) {
|
||||
exit(resSuccess); // child exit with success status
|
||||
} else {
|
||||
exit(resFailure); // child exit with failure status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parent shall wait for child to complete
|
||||
int passCnt = 0;
|
||||
for (int i = 0; i < devCnt; i++) {
|
||||
int pidwait = 0, exitStatus;
|
||||
pidwait = wait(&exitStatus);
|
||||
|
||||
printf("exitStatus for dev:%d is %d\n", i, WEXITSTATUS(exitStatus));
|
||||
if (pidwait < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (WEXITSTATUS(exitStatus) == resSuccess) passCnt++;
|
||||
}
|
||||
REQUIRE(passCnt == devCnt);
|
||||
}
|
||||
#endif // __linux__
|
||||
@@ -0,0 +1,781 @@
|
||||
/*
|
||||
Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
/* Test Case Description:
|
||||
Scenario 3: The test validates if fine grain
|
||||
behavior is observed or not with memory allocated using malloc()
|
||||
Scenario 4: The test validates if coarse grain memory
|
||||
behavior is observed or not with memory allocated using malloc()
|
||||
Scenario 5: The test validates if fine memory
|
||||
behavior is observed or not with memory allocated using mmap()
|
||||
Scenario 6: The test validates if coarse grain memory
|
||||
behavior is observed or not with memory allocated using mmap()
|
||||
Scenario:7 Test Case Description: The following test checks if the memory is
|
||||
accessible when HIP_HOST_COHERENT is set to 0
|
||||
Scenario:8 Test Case Description: The following test checks if the memory
|
||||
exhibits fine grain behavior when HIP_HOST_COHERENT is set to 1
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <chrono>
|
||||
|
||||
__global__ void CoherentTst(int *ptr, int PeakClk) {
|
||||
// Incrementing the value by 1
|
||||
int64_t GpuFrq = int64_t(PeakClk) * 1000;
|
||||
int64_t StrtTck = clock64();
|
||||
atomicAdd(ptr, 1);
|
||||
// The following while loop checks the value in ptr for around 3-4 seconds
|
||||
while ((clock64() - StrtTck) <= (3 * GpuFrq)) {
|
||||
if (atomicCAS(ptr, 3, 4) == 3) break;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void SquareKrnl(int *ptr) {
|
||||
// ptr value squared here
|
||||
*ptr = (*ptr) * (*ptr);
|
||||
}
|
||||
|
||||
// The variable below will work as signal to decide pass/fail
|
||||
static bool YES_COHERENT = false;
|
||||
|
||||
// The function tests the coherency of allocated memory
|
||||
static void TstCoherency(int *Ptr, bool HmmMem) {
|
||||
int *Dptr = nullptr, peak_clk;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
// storing value 1 in the memory created above
|
||||
*Ptr = 1;
|
||||
// Getting gpu frequency
|
||||
HIP_CHECK(hipDeviceGetAttribute(&peak_clk, hipDeviceAttributeClockRate, 0));
|
||||
if (!HmmMem) {
|
||||
HIP_CHECK(hipHostGetDevicePointer(reinterpret_cast<void **>(&Dptr), Ptr,
|
||||
0));
|
||||
CoherentTst<<<1, 1, 0, strm>>>(Dptr, peak_clk);
|
||||
} else {
|
||||
CoherentTst<<<1, 1, 0, strm>>>(Ptr, peak_clk);
|
||||
}
|
||||
// looping until the value is 2 for 3 seconds
|
||||
std::chrono::steady_clock::time_point start =
|
||||
std::chrono::steady_clock::now();
|
||||
while (std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::steady_clock::now() - start).count() < 3) {
|
||||
if (*Ptr == 2) {
|
||||
*Ptr += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
if (*Ptr == 4) {
|
||||
YES_COHERENT = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test case description: The following test validates if fine grain
|
||||
behavior is observed or not with memory allocated using malloc()*/
|
||||
// The following test is failing on Nvidia platform hence disabled it for now
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_malloc_CoherentTst") {
|
||||
if ((setenv("HSA_XNACK", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HSA_XNACK, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
// The following code block is used to check for gfx906/8 so as to skip if
|
||||
// any of the gpus available
|
||||
int fd1[2]; // Used to store two ends of first pipe
|
||||
pid_t p;
|
||||
if (pipe(fd1) == -1) {
|
||||
fprintf(stderr, "Pipe Failed");
|
||||
REQUIRE(false);
|
||||
}
|
||||
|
||||
/* GpuId[0] for gfx90a exists--> 1 for yes and 0 for no*/
|
||||
int GpuId[1] = {0};
|
||||
p = fork();
|
||||
|
||||
if (p < 0) {
|
||||
fprintf(stderr, "fork Failed");
|
||||
REQUIRE(false);
|
||||
} else if (p > 0) { // parent process
|
||||
close(fd1[1]); // Close writing end of first pipe
|
||||
// Wait for child to send a string
|
||||
wait(NULL);
|
||||
// Read string from child and close reading end.
|
||||
read(fd1[0], GpuId, 2 * sizeof(int));
|
||||
close(fd1[0]);
|
||||
if (GpuId[0] == 0) {
|
||||
WARN("This test is applicable for MI200."
|
||||
"Skipping the test!!");
|
||||
exit(0);
|
||||
}
|
||||
} else { // child process
|
||||
close(fd1[0]); // Close read end of first pipe
|
||||
hipDeviceProp_t prop;
|
||||
HIPCHECK(hipGetDeviceProperties(&prop, 0));
|
||||
char *p = NULL;
|
||||
p = strstr(prop.gcnArchName, "gfx90a");
|
||||
if (p) {
|
||||
WARN("gfx90a gpu found on this system!!");
|
||||
GpuId[0] = 1;
|
||||
}
|
||||
// Write concatenated string and close writing end
|
||||
write(fd1[1], GpuId, 2 * sizeof(int));
|
||||
close(fd1[1]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Test Case execution begins from here
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int managed = 0;
|
||||
HIPCHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory,
|
||||
0));
|
||||
if (managed == 1) {
|
||||
int *Ptr = nullptr, SIZE = sizeof(int);
|
||||
bool HmmMem = true;
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipMallocManaged() memory
|
||||
Ptr = reinterpret_cast<int*>(malloc(SIZE));
|
||||
TstCoherency(Ptr, HmmMem);
|
||||
free(Ptr);
|
||||
if (YES_COHERENT) {
|
||||
// exit() with code 10 which indicates pass
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Test case description: The following test validates if coarse grain memory
|
||||
behavior is observed or not with memory allocated using malloc()*/
|
||||
// The following test is failing on Nvidia platform hence disabling it for now
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_malloc_CoherentTstWthAdvise") {
|
||||
if ((setenv("HSA_XNACK", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HSA_XNACK, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
// The following code block is used to check for gfx906/8 so as to skip if
|
||||
// any of the gpus available
|
||||
int fd1[2]; // Used to store two ends of first pipe
|
||||
pid_t p;
|
||||
if (pipe(fd1) == -1) {
|
||||
fprintf(stderr, "Pipe Failed");
|
||||
REQUIRE(false);
|
||||
}
|
||||
|
||||
/* GpuId[0] for gfx90a exists--> 1 for yes and 0 for no */
|
||||
int GpuId[1] = {0};
|
||||
p = fork();
|
||||
|
||||
if (p < 0) {
|
||||
fprintf(stderr, "fork Failed");
|
||||
REQUIRE(false);
|
||||
} else if (p > 0) { // parent process
|
||||
close(fd1[1]); // Close writing end of first pipe
|
||||
// Wait for child to send a string
|
||||
wait(NULL);
|
||||
// Read string from child and close reading end.
|
||||
read(fd1[0], GpuId, 2 * sizeof(int));
|
||||
close(fd1[0]);
|
||||
if (GpuId[0] == 0) {
|
||||
WARN("This test is applicable for MI200."
|
||||
"Skipping the test!!");
|
||||
exit(0);
|
||||
}
|
||||
} else { // child process
|
||||
close(fd1[0]); // Close read end of first pipe
|
||||
hipDeviceProp_t prop;
|
||||
HIPCHECK(hipGetDeviceProperties(&prop, 0));
|
||||
char *p = NULL;
|
||||
p = strstr(prop.gcnArchName, "gfx90a");
|
||||
if (p) {
|
||||
WARN("gfx90a gpu found on this system!!");
|
||||
GpuId[0] = 1;
|
||||
}
|
||||
// Write concatenated string and close writing end
|
||||
write(fd1[1], GpuId, 2 * sizeof(int));
|
||||
close(fd1[1]);
|
||||
exit(0);
|
||||
}
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int managed = 0;
|
||||
HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory,
|
||||
0));
|
||||
if (managed == 1) {
|
||||
int *Ptr = nullptr, SIZE = sizeof(int);
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipMallocManaged() memory
|
||||
Ptr = reinterpret_cast<int*>(malloc(SIZE));
|
||||
*Ptr = 4;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
SquareKrnl<<<1, 1, 0, strm>>>(Ptr);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
if (*Ptr == 16) {
|
||||
// exit() with code 10 which indicates pass
|
||||
free(Ptr);
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
free(Ptr);
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test case description: The following test validates if fine memory
|
||||
behavior is observed or not with memory allocated using mmap()*/
|
||||
// The following test is failing on Nvidia platform hence disabling it for now
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_mmap_CoherentTst") {
|
||||
if ((setenv("HSA_XNACK", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HSA_XNACK, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
// The following code block is used to check for gfx906/8 so as to skip if
|
||||
// any of the gpus available
|
||||
int fd1[2]; // Used to store two ends of first pipe
|
||||
pid_t p;
|
||||
if (pipe(fd1) == -1) {
|
||||
fprintf(stderr, "Pipe Failed");
|
||||
REQUIRE(false);
|
||||
}
|
||||
|
||||
/* GpuId[0] for gfx90a exists--> 1 for yes and 0 for no */
|
||||
int GpuId[1] = {0};
|
||||
p = fork();
|
||||
|
||||
if (p < 0) {
|
||||
fprintf(stderr, "fork Failed");
|
||||
REQUIRE(false);
|
||||
} else if (p > 0) { // parent process
|
||||
close(fd1[1]); // Close writing end of first pipe
|
||||
// Wait for child to send a string
|
||||
wait(NULL);
|
||||
// Read string from child and close reading end.
|
||||
read(fd1[0], GpuId, 2 * sizeof(int));
|
||||
close(fd1[0]);
|
||||
if (GpuId[0] == 0) {
|
||||
WARN("This test is not applicable for MI200."
|
||||
"Skipping the test!!");
|
||||
exit(0);
|
||||
}
|
||||
} else { // child process
|
||||
close(fd1[0]); // Close read end of first pipe
|
||||
hipDeviceProp_t prop;
|
||||
HIPCHECK(hipGetDeviceProperties(&prop, 0));
|
||||
char *p = NULL;
|
||||
p = strstr(prop.gcnArchName, "gfx90a");
|
||||
if (p) {
|
||||
WARN("gfx90a gpu found on this system!!");
|
||||
GpuId[0] = 1;
|
||||
}
|
||||
// Write concatenated string and close writing end
|
||||
write(fd1[1], GpuId, 2 * sizeof(int));
|
||||
close(fd1[1]);
|
||||
exit(0);
|
||||
}
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int managed = 0;
|
||||
HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory,
|
||||
0));
|
||||
if (managed == 1) {
|
||||
bool HmmMem = true;
|
||||
int *Ptr = reinterpret_cast<int*>(mmap(NULL, sizeof(int),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0));
|
||||
if (Ptr == MAP_FAILED) {
|
||||
WARN("Mapping Failed\n");
|
||||
REQUIRE(false);
|
||||
}
|
||||
// Initializing the value with 1
|
||||
*Ptr = 1;
|
||||
TstCoherency(Ptr, HmmMem);
|
||||
int err = munmap(Ptr, sizeof(int));
|
||||
if (err != 0) {
|
||||
WARN("munmap failed\n");
|
||||
}
|
||||
if (YES_COHERENT) {
|
||||
exit(10);
|
||||
} else {
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test case description: The following test validates if coarse grain memory
|
||||
behavior is observed or not with memory allocated using mmap()*/
|
||||
// The following test is failing on Nvidia platform hence disabling it for now
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_mmap_CoherentTstWthAdvise") {
|
||||
if ((setenv("HSA_XNACK", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HSA_XNACK, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
// The following code block is used to check for gfx906/8 so as to skip if
|
||||
// any of the gpus available
|
||||
int fd1[2]; // Used to store two ends of first pipe
|
||||
pid_t p;
|
||||
if (pipe(fd1) == -1) {
|
||||
fprintf(stderr, "Pipe Failed");
|
||||
REQUIRE(false);
|
||||
}
|
||||
|
||||
/* GpuId[0] for gfx90a exists--> 1 for yes and 0 for no */
|
||||
int GpuId[1] = {0};
|
||||
p = fork();
|
||||
|
||||
if (p < 0) {
|
||||
fprintf(stderr, "fork Failed");
|
||||
REQUIRE(false);
|
||||
} else if (p > 0) { // parent process
|
||||
close(fd1[1]); // Close writing end of first pipe
|
||||
// Wait for child to send a string
|
||||
wait(NULL);
|
||||
// Read string from child and close reading end.
|
||||
read(fd1[0], GpuId, 2 * sizeof(int));
|
||||
close(fd1[0]);
|
||||
if (GpuId[0] == 0) {
|
||||
WARN("This test is applicable for MI200."
|
||||
"Skipping the test!!");
|
||||
exit(0);
|
||||
}
|
||||
} else { // child process
|
||||
close(fd1[0]); // Close read end of first pipe
|
||||
hipDeviceProp_t prop;
|
||||
HIPCHECK(hipGetDeviceProperties(&prop, 0));
|
||||
char *p = NULL;
|
||||
p = strstr(prop.gcnArchName, "gfx90a");
|
||||
if (p) {
|
||||
WARN("gfx90a gpu found on this system!!");
|
||||
GpuId[0] = 1;
|
||||
}
|
||||
// Write concatenated string and close writing end
|
||||
write(fd1[1], GpuId, 2 * sizeof(int));
|
||||
close(fd1[1]);
|
||||
exit(0);
|
||||
}
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int managed = 0;
|
||||
HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory,
|
||||
0));
|
||||
if (managed == 1) {
|
||||
int SIZE = sizeof(int);
|
||||
int *Ptr = reinterpret_cast<int*>(mmap(NULL, SIZE,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0));
|
||||
if (Ptr == MAP_FAILED) {
|
||||
WARN("Mapping Failed\n");
|
||||
REQUIRE(false);
|
||||
}
|
||||
HIP_CHECK(hipMemAdvise(Ptr, SIZE, hipMemAdviseSetCoarseGrain, 0));
|
||||
// Initializing the value with 9
|
||||
*Ptr = 9;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
SquareKrnl<<<1, 1, 0, strm>>>(Ptr);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
bool IfTstPassed = false;
|
||||
if (*Ptr == 81) {
|
||||
IfTstPassed = true;
|
||||
}
|
||||
int err = munmap(Ptr, SIZE);
|
||||
if (err != 0) {
|
||||
WARN("munmap failed\n");
|
||||
}
|
||||
if (IfTstPassed) {
|
||||
exit(10);
|
||||
} else {
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test Case Description: The following test checks if the memory is
|
||||
accessible when HIP_HOST_COHERENT is set to 0*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv0Flg1") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "0", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int *Ptr = nullptr, *PtrD = nullptr, SIZE = sizeof(int);
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE, hipHostMallocPortable));
|
||||
*Ptr = 4;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
HIP_CHECK(hipHostGetDevicePointer(reinterpret_cast<void**>(&PtrD), Ptr, 0));
|
||||
SquareKrnl<<<1, 1, 0, strm>>>(PtrD);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
if (*Ptr == 16) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test Case Description: The following test checks if the memory is
|
||||
accessible when HIP_HOST_COHERENT is set to 0*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv0Flg2") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "0", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int *Ptr = nullptr, *PtrD = nullptr, SIZE = sizeof(int);
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE, hipHostMallocWriteCombined));
|
||||
*Ptr = 4;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
HIP_CHECK(hipHostGetDevicePointer(reinterpret_cast<void**>(&PtrD), Ptr, 0));
|
||||
SquareKrnl<<<1, 1, 0, strm>>>(PtrD);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
if (*Ptr == 16) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test Case Description: The following test checks if the memory is
|
||||
accessible when HIP_HOST_COHERENT is set to 0*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv0Flg3") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "0", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int *Ptr = nullptr, *PtrD = nullptr, SIZE = sizeof(int);
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE, hipHostMallocNumaUser));
|
||||
*Ptr = 4;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
HIP_CHECK(hipHostGetDevicePointer(reinterpret_cast<void**>(&PtrD), Ptr, 0));
|
||||
SquareKrnl<<<1, 1, 0, strm>>>(PtrD);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
if (*Ptr == 16) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test Case Description: The following test checks if the memory is
|
||||
accessible when HIP_HOST_COHERENT is set to 0*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv0Flg4") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "0", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
if (fork() == 0) {
|
||||
int *Ptr = nullptr, *PtrD = nullptr, SIZE = sizeof(int);
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE, hipHostMallocNonCoherent));
|
||||
*Ptr = 4;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
HIP_CHECK(hipHostGetDevicePointer(reinterpret_cast<void**>(&PtrD), Ptr, 0));
|
||||
SquareKrnl<<<1, 1, 0, strm>>>(PtrD);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
if (*Ptr == 16) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Test Case Description: The following test checks if the memory exhibits
|
||||
fine grain behavior when HIP_HOST_COHERENT is set to 1*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv1") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
|
||||
if (fork() == 0) { // child process
|
||||
int *Ptr = nullptr, SIZE = sizeof(int);
|
||||
bool HmmMem = false;
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE));
|
||||
*Ptr = 4;
|
||||
TstCoherency(Ptr, HmmMem);
|
||||
if (YES_COHERENT) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else { // parent process
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Test Case Description: The following test checks if the memory exhibits
|
||||
fine grain behavior when HIP_HOST_COHERENT is set to 1*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv1Flg1") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
|
||||
if (fork() == 0) { // child process
|
||||
int *Ptr = nullptr, SIZE = sizeof(int);
|
||||
bool HmmMem = false;
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE, hipHostMallocPortable));
|
||||
*Ptr = 1;
|
||||
TstCoherency(Ptr, HmmMem);
|
||||
if (YES_COHERENT) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else { // parent process
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test Case Description: The following test checks if the memory exhibits
|
||||
fine grain behavior when HIP_HOST_COHERENT is set to 1*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv1Flg2") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
|
||||
if (fork() == 0) { // child process
|
||||
int *Ptr = nullptr, SIZE = sizeof(int);
|
||||
bool HmmMem = false;
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE, hipHostMallocWriteCombined));
|
||||
*Ptr = 4;
|
||||
TstCoherency(Ptr, HmmMem);
|
||||
if (YES_COHERENT) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else { // parent process
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test Case Description: The following test checks if the memory exhibits
|
||||
fine grain behavior when HIP_HOST_COHERENT is set to 1*/
|
||||
// The following test is AMD specific test hence skipping for Nvidia
|
||||
#if HT_AMD
|
||||
TEST_CASE("Unit_hipHostMalloc_WthEnv1Flg3") {
|
||||
if ((setenv("HIP_HOST_COHERENT", "1", 1)) != 0) {
|
||||
WARN("Unable to turn on HIP_HOST_COHERENT, hence terminating the Test case!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
int stat = 0;
|
||||
|
||||
if (fork() == 0) { // child process
|
||||
int *Ptr = nullptr, SIZE = sizeof(int);
|
||||
bool HmmMem = false;
|
||||
YES_COHERENT = false;
|
||||
// Allocating hipHostMalloc() memory
|
||||
HIP_CHECK(hipHostMalloc(&Ptr, SIZE, hipHostMallocNumaUser));
|
||||
*Ptr = 1;
|
||||
TstCoherency(Ptr, HmmMem);
|
||||
if (YES_COHERENT) {
|
||||
// exit() with code 10 which indicates pass
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(10);
|
||||
} else {
|
||||
// exit() with code 9 which indicates fail
|
||||
HIP_CHECK(hipHostFree(Ptr));
|
||||
exit(9);
|
||||
}
|
||||
} else { // parent process
|
||||
wait(&stat);
|
||||
int Result = WEXITSTATUS(stat);
|
||||
if (Result != 10) {
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
#include <hip_test_common.hh>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define ReadEnd 0
|
||||
#define WriteEnd 1
|
||||
#define MAX_SIZE 32
|
||||
#define FREE_MEM_TO_HIDE 4294967296
|
||||
#define SIZE_TO_ALLOCATE 2147483648
|
||||
/*
|
||||
* In main process allocate 2 GB of device memory.
|
||||
* Fork() a child process and verify that 2 GB has been
|
||||
* allocated in parent process.
|
||||
*/
|
||||
TEST_CASE("Unit_hipMemGetInfo_Functional_Scenario1") {
|
||||
constexpr size_t size = 2147483648; // 2GB
|
||||
int fd[2], fd1[2], status;
|
||||
status = pipe(fd);
|
||||
REQUIRE(status == 0);
|
||||
status = pipe(fd1);
|
||||
REQUIRE(status == 0);
|
||||
pid_t child_pid;
|
||||
child_pid = fork(); // Create a new child process
|
||||
if (child_pid < 0) {
|
||||
WARN("Fork failed!!!!");
|
||||
} else if (child_pid == 0) { // child
|
||||
close(fd1[WriteEnd]);
|
||||
close(fd[ReadEnd]);
|
||||
int result;
|
||||
size_t free = 0, total = 0;
|
||||
// Wait for signal from parent
|
||||
int check_child;
|
||||
status = read(fd1[ReadEnd], &check_child, sizeof(check_child));
|
||||
REQUIRE(status != -1);
|
||||
close(fd1[ReadEnd]);
|
||||
// Check the total and free memory which is allocated in parent
|
||||
HIP_CHECK(hipMemGetInfo(&free, &total));
|
||||
if ((total - free) >= size) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
// Write the result to parent
|
||||
status = write(fd[WriteEnd], &result, sizeof(result));
|
||||
REQUIRE(status != -1);
|
||||
close(fd[WriteEnd]);
|
||||
exit(0);
|
||||
} else { // Parent
|
||||
close(fd1[ReadEnd]);
|
||||
close(fd[WriteEnd]);
|
||||
// Allocate memory
|
||||
char* A_d = nullptr;
|
||||
HIP_CHECK(hipMalloc(&A_d, size));
|
||||
// Signal the child
|
||||
int check = 0;
|
||||
status = write(fd1[WriteEnd], &check, sizeof(check));
|
||||
REQUIRE(status != -1);
|
||||
close(fd1[WriteEnd]);
|
||||
// Read the result from Child
|
||||
int read_result;
|
||||
status = read(fd[ReadEnd], &read_result, sizeof(read_result));
|
||||
REQUIRE(status != -1);
|
||||
close(fd[ReadEnd]);
|
||||
REQUIRE(read_result == 1);
|
||||
HIP_CHECK(hipFree(A_d));
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* From main process Fork() a child process. In the child process allocate
|
||||
* 2 GB of device memory. Signal the parent process. Verify from the parent
|
||||
* process that 2 GB is allocated in the child process.
|
||||
*/
|
||||
TEST_CASE("Unit_hipMemGetInfo_Functional_Scenario2") {
|
||||
constexpr size_t size = 2147483648; // 2GB
|
||||
int fd[2], fd2[2], status;
|
||||
status = pipe(fd);
|
||||
REQUIRE(status == 0);
|
||||
status = pipe(fd2);
|
||||
REQUIRE(status == 0);
|
||||
pid_t child_pid;
|
||||
child_pid = fork(); // Create a new child process
|
||||
if (child_pid < 0) {
|
||||
WARN("Fork failed!!!!");
|
||||
} else if (child_pid == 0) { // Child
|
||||
close(fd[ReadEnd]);
|
||||
close(fd2[WriteEnd]);
|
||||
// Allocate memory
|
||||
float* A_d = nullptr;
|
||||
HIP_CHECK(hipMalloc(&A_d, size));
|
||||
// Signal the parent
|
||||
int data = 0;
|
||||
status = write(fd[WriteEnd], &data, sizeof(data));
|
||||
REQUIRE(status != -1);
|
||||
close(fd[WriteEnd]);
|
||||
int valid = 0;
|
||||
// Wait for Signal from parent before freeing memory and exiting
|
||||
status = read(fd2[ReadEnd], &valid, sizeof(valid));
|
||||
REQUIRE(status != -1);
|
||||
close(fd2[ReadEnd]);
|
||||
// Free allocated device memory
|
||||
HIP_CHECK(hipFree(A_d));
|
||||
exit(0);
|
||||
} else { // Parent
|
||||
size_t free = 0, total = 0;
|
||||
close(fd[WriteEnd]);
|
||||
close(fd2[ReadEnd]);
|
||||
// Wait for child signal
|
||||
int data = 0;
|
||||
status = read(fd[ReadEnd], &data, sizeof(data));
|
||||
REQUIRE(status != -1);
|
||||
close(fd[ReadEnd]);
|
||||
// Verify the memory
|
||||
HIP_CHECK(hipMemGetInfo(&free , &total));
|
||||
REQUIRE((total - free) >= size);
|
||||
// Signal child that validation is over and child can free memory
|
||||
int valid = 0;
|
||||
status = write(fd2[WriteEnd], &valid, sizeof(valid));
|
||||
REQUIRE(status != -1);
|
||||
close(fd2[WriteEnd]);
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* From main process Fork() a child process. In the child process
|
||||
* allocate 2 GB of device memory. Free the memory and exit from
|
||||
* child process. Verify from the parent process that 2 GB is
|
||||
* freed in the child process.
|
||||
*/
|
||||
TEST_CASE("Unit_hipMemGetInfo_Functional_Scenario3") {
|
||||
constexpr size_t size = 2147483648; // 2GB
|
||||
int fd[2], status;
|
||||
status = pipe(fd);
|
||||
REQUIRE(status == 0);
|
||||
pid_t child_pid;
|
||||
child_pid = fork(); // Create a new child process
|
||||
if (child_pid < 0) {
|
||||
WARN("Fork failed!!!!");
|
||||
} else if (child_pid == 0) { // Child
|
||||
close(fd[ReadEnd]);
|
||||
// Allocate the memory
|
||||
void* A_d = nullptr;
|
||||
HIP_CHECK(hipMalloc(&A_d, size));
|
||||
// Free the allocated memory
|
||||
HIP_CHECK(hipFree(A_d));
|
||||
// Signal the parent about memory free
|
||||
int check = 0;
|
||||
status = write(fd[WriteEnd], &check, sizeof(check));
|
||||
REQUIRE(status != -1);
|
||||
close(fd[WriteEnd]);
|
||||
exit(0);
|
||||
} else { // Parent
|
||||
close(fd[WriteEnd]);
|
||||
// Wait for the signal from child about memory free
|
||||
int check_parent;
|
||||
status = read(fd[ReadEnd], &check_parent, sizeof(check_parent));
|
||||
REQUIRE(status != -1);
|
||||
close(fd[ReadEnd]);
|
||||
size_t free = 0, total = 0;
|
||||
// Verify the memory
|
||||
HIP_CHECK(hipMemGetInfo(&free , &total));
|
||||
REQUIRE((total - free) >= 0);
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* From main process Fork() a child process. In the child process allocate
|
||||
* 2 GB of device memory. Exit from child process. Verify from the parent
|
||||
* process that 2 GB is freed in the child process.
|
||||
*/
|
||||
TEST_CASE("Unit_hipMemGetInfo_Functional_scenario4") {
|
||||
constexpr size_t size = 2147483648; // 2GB
|
||||
pid_t child_pid;
|
||||
child_pid = fork(); // Create a new child process
|
||||
if (child_pid < 0) {
|
||||
WARN("Fork failed!!!!");
|
||||
} else if (child_pid == 0) { // Child
|
||||
// Allocate the memory
|
||||
void* A_d = nullptr;
|
||||
HIP_CHECK(hipMalloc(&A_d, size));
|
||||
exit(0);
|
||||
} else { // Parent
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
size_t free = 0, total = 0;
|
||||
// Verify the memory
|
||||
HIP_CHECK(hipMemGetInfo(&free , &total));
|
||||
REQUIRE((total-free) >= 0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Multidevice Scenario: In main process allocate 2 GB of device memory
|
||||
* in every device. Verify that 2 GB is allocated using hipMemGetInfo.
|
||||
* Fork() a child process and verify that 2 GB has been allocated from
|
||||
* parent process in every device.
|
||||
*/
|
||||
TEST_CASE("Unit_hipMemGetInfo_Functional_MultiDevice_Scenario5") {
|
||||
constexpr size_t size = 2147483648; // 2GB
|
||||
size_t free = 0, total = 0;
|
||||
int fd1[2], fd2[2], status;
|
||||
status = pipe(fd1);
|
||||
REQUIRE(status == 0);
|
||||
status = pipe(fd2);
|
||||
REQUIRE(status == 0);
|
||||
pid_t child_pid;
|
||||
child_pid = fork(); // Create a new child process
|
||||
if (child_pid < 0) {
|
||||
WARN("Fork failed!!!!");
|
||||
} else if (child_pid == 0) { // Child
|
||||
close(fd1[WriteEnd]);
|
||||
close(fd2[ReadEnd]);
|
||||
// Wait for the signal from parent after memory allocatoin
|
||||
int check_child;
|
||||
status = read(fd1[ReadEnd], &check_child, sizeof(check_child));
|
||||
REQUIRE(status != -1);
|
||||
close(fd1[ReadEnd]);
|
||||
int num_devices, result, count = 0;
|
||||
// Get the device count
|
||||
HIP_CHECK(hipGetDeviceCount(&num_devices));
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
// Check the memory
|
||||
HIP_CHECK(hipMemGetInfo(&free , &total));
|
||||
if ((total - free) >= size) {
|
||||
count+=1;
|
||||
}
|
||||
}
|
||||
if ( count == num_devices ) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
// Write the result to Parent
|
||||
status = write(fd2[WriteEnd], &result, sizeof(result));
|
||||
REQUIRE(status != -1);
|
||||
close(fd2[WriteEnd]);
|
||||
exit(0);
|
||||
} else { // Parent
|
||||
close(fd1[ReadEnd]);
|
||||
close(fd2[WriteEnd]);
|
||||
int num_devices;
|
||||
// Get the device count
|
||||
HIP_CHECK(hipGetDeviceCount(&num_devices));
|
||||
std::vector<void*>v(num_devices, nullptr);
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
// verify the memory
|
||||
HIP_CHECK(hipMemGetInfo(&free , &total));
|
||||
// Allocate memory
|
||||
HIP_CHECK(hipMalloc(&v[i], size));
|
||||
// Verify the memory
|
||||
HIP_CHECK(hipMemGetInfo(&free , &total));
|
||||
}
|
||||
// Signal the child about memory allocation
|
||||
int check = 0;
|
||||
status = write(fd1[WriteEnd], &check, sizeof(check));
|
||||
REQUIRE(status != -1);
|
||||
close(fd1[WriteEnd]);
|
||||
// Read result from child
|
||||
int result_parent;
|
||||
status = read(fd2[ReadEnd], &result_parent, sizeof(result_parent));
|
||||
REQUIRE(status != -1);
|
||||
REQUIRE(result_parent == 1);
|
||||
close(fd2[ReadEnd]);
|
||||
// Free the allocated memory on each device
|
||||
for (int i = 0; i < num_devices; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipFree(v[i]));
|
||||
}
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* 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 designed to run on Linux based platforms
|
||||
* Verifies functionality of
|
||||
* -- hipSetDevice and hipGetDevice with different ROCR_VISIBLE_DEVICES and
|
||||
* HIP_VISIBLE_DEVICES values set
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define MAX_SIZE 30
|
||||
|
||||
/**
|
||||
* Fetches Gpu device count
|
||||
*/
|
||||
static void getDeviceCount(int *pdevCnt) {
|
||||
int fd[2], val = 0;
|
||||
pid_t childpid;
|
||||
|
||||
// create pipe descriptors
|
||||
pipe(fd);
|
||||
|
||||
// disable visible_devices env from shell
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
|
||||
|
||||
childpid = fork();
|
||||
|
||||
if (childpid > 0) { // Parent
|
||||
close(fd[1]);
|
||||
// parent will wait to read the device cnt
|
||||
read(fd[0], &val, sizeof(val));
|
||||
|
||||
// close the read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
// wait for child exit
|
||||
wait(NULL);
|
||||
|
||||
*pdevCnt = val;
|
||||
} else if (!childpid) { // Child
|
||||
int devCnt = 1;
|
||||
// writing only, no need for read-descriptor
|
||||
close(fd[0]);
|
||||
|
||||
HIP_CHECK(hipGetDeviceCount(&devCnt));
|
||||
|
||||
// send the value on the write-descriptor:
|
||||
write(fd[1], &devCnt, sizeof(devCnt));
|
||||
|
||||
// close the write descriptor:
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else { // failure
|
||||
*pdevCnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Pass either -1 in deviceNumber or invalid device number
|
||||
static void testInvalidDevice(int numDevices, bool useRocrEnv,
|
||||
int deviceNumber) {
|
||||
bool testResult = true;
|
||||
int device;
|
||||
int tempCount = 0;
|
||||
int setDeviceErrorCheck = 0;
|
||||
int getDeviceErrorCheck = 0;
|
||||
int getDeviceCountErrorCheck = 0;
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
snprintf(visibleDeviceString, MAX_SIZE, "%d", deviceNumber);
|
||||
|
||||
if (cPid == 0) { // child
|
||||
hipError_t err;
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#else
|
||||
if (true == useRocrEnv) {
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
} else {
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
}
|
||||
#endif
|
||||
err = hipGetDeviceCount(&tempCount);
|
||||
if (err != hipSuccess) {
|
||||
getDeviceCountErrorCheck = 1;
|
||||
}
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
err = hipSetDevice(i);
|
||||
if (err != hipSuccess) {
|
||||
setDeviceErrorCheck+= 1;
|
||||
}
|
||||
|
||||
err = hipGetDevice(&device);
|
||||
if (err != hipSuccess) {
|
||||
getDeviceErrorCheck+= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((getDeviceCountErrorCheck == 1) && (setDeviceErrorCheck == numDevices)
|
||||
&& (getDeviceErrorCheck == numDevices)) {
|
||||
testResult = true;
|
||||
|
||||
} else {
|
||||
printf("Test failed for invalid device, getDeviceCountErrorCheck %d,"
|
||||
"setDeviceErrorCheck %d, getDeviceErrorCheck %d\n",
|
||||
getDeviceCountErrorCheck, setDeviceErrorCheck,
|
||||
getDeviceErrorCheck);
|
||||
|
||||
testResult = false;
|
||||
}
|
||||
|
||||
close(fd[0]);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
|
||||
} else if (cPid > 0) { // parent
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
REQUIRE(testResult == true);
|
||||
}
|
||||
|
||||
|
||||
static void testValidDevices(int numDevices, bool useRocrEnv, int *deviceList,
|
||||
int deviceListLength) {
|
||||
bool testResult = true;
|
||||
int tempCount = 0;
|
||||
int device;
|
||||
int setDeviceErrorCheck = 0;
|
||||
int getDeviceErrorCheck = 0;
|
||||
int getDeviceCountErrorCheck = 0;
|
||||
int *deviceListPtr = deviceList;
|
||||
char visibleDeviceString[MAX_SIZE] = {};
|
||||
|
||||
if ((NULL == deviceList) || ((deviceListLength < 1) ||
|
||||
deviceListLength > numDevices)) {
|
||||
INFO("Invalid argument for number of devices. Skipping current test");
|
||||
REQUIRE(false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < deviceListLength; i++) {
|
||||
snprintf(visibleDeviceString + strlen(visibleDeviceString), MAX_SIZE, "%d,",
|
||||
*deviceListPtr++);
|
||||
}
|
||||
|
||||
visibleDeviceString[strlen(visibleDeviceString)-1] = 0;
|
||||
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
|
||||
if (cPid == 0) {
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
unsetenv("CUDA_VISIBLE_DEVICES");
|
||||
setenv("CUDA_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
#else
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
if (true == useRocrEnv) {
|
||||
setenv("ROCR_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
} else {
|
||||
setenv("HIP_VISIBLE_DEVICES", visibleDeviceString, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
hipError_t err;
|
||||
err = hipGetDeviceCount(&tempCount);
|
||||
|
||||
if (tempCount == deviceListLength) {
|
||||
getDeviceCountErrorCheck = 1;
|
||||
} else {
|
||||
printf("hipGetDeviceCount failed. return value: %u\n", hipError_t(err));
|
||||
}
|
||||
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
err = hipSetDevice(i);
|
||||
if (err != hipSuccess) {
|
||||
setDeviceErrorCheck+= 1;
|
||||
}
|
||||
|
||||
err = hipGetDevice(&device);
|
||||
if (err != hipSuccess) {
|
||||
getDeviceErrorCheck+= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((getDeviceCountErrorCheck == 1) && (setDeviceErrorCheck ==
|
||||
(numDevices-deviceListLength)) && (getDeviceErrorCheck == 0)) {
|
||||
testResult = true;
|
||||
|
||||
} else {
|
||||
printf("Test failed for device count %d\n", deviceListLength);
|
||||
testResult = false;
|
||||
}
|
||||
|
||||
close(fd[0]);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
|
||||
} else if (cPid > 0) {
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
REQUIRE(testResult == true);
|
||||
}
|
||||
|
||||
|
||||
static void Initialize(int *deviceList, int numDevices, int count,
|
||||
char min_visibleDeviceString[], char max_visibleDeviceString[]) {
|
||||
int *deviceListPtr = deviceList;
|
||||
for (int i =0; i < count; i++) {
|
||||
if (i == count-1) {
|
||||
snprintf(min_visibleDeviceString + strlen(min_visibleDeviceString),
|
||||
MAX_SIZE, "%d", *deviceListPtr++);
|
||||
} else {
|
||||
snprintf(min_visibleDeviceString + strlen(min_visibleDeviceString),
|
||||
MAX_SIZE, "%d,", *deviceListPtr++);
|
||||
}
|
||||
}
|
||||
for (int i =0; i < numDevices; i++) {
|
||||
if (i == numDevices-1) {
|
||||
snprintf(max_visibleDeviceString + strlen(max_visibleDeviceString),
|
||||
MAX_SIZE, "%d", i);
|
||||
} else {
|
||||
snprintf(max_visibleDeviceString + strlen(max_visibleDeviceString),
|
||||
MAX_SIZE, "%d,", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testMaxRvdMinHvd(int numDevices, int *deviceList, int count) {
|
||||
bool testResult = true;
|
||||
int device;
|
||||
int validateCount = 0;
|
||||
char min_visibleDeviceString[MAX_SIZE] = {0};
|
||||
char max_visibleDeviceString[MAX_SIZE] = {0};
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
Initialize(deviceList, numDevices,
|
||||
count, min_visibleDeviceString, max_visibleDeviceString);
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", max_visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", min_visibleDeviceString, 1);
|
||||
HIP_CHECK(hipGetDeviceCount(&numDevices));
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipGetDevice(&device));
|
||||
if (device == i) {
|
||||
validateCount+= 1;
|
||||
}
|
||||
}
|
||||
if (count != validateCount) {
|
||||
testResult = false;
|
||||
}
|
||||
} else if (cPid > 0) {
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
REQUIRE(testResult == true);
|
||||
}
|
||||
|
||||
static void testRvdCvd(int numDevices, int *deviceList, int count) {
|
||||
bool testResult = true;
|
||||
int device;
|
||||
int validateCount = 0;
|
||||
char min_visibleDeviceString[MAX_SIZE] = {0};
|
||||
char max_visibleDeviceString[MAX_SIZE] = {0};
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
Initialize(deviceList, numDevices, count,
|
||||
min_visibleDeviceString, max_visibleDeviceString);
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", max_visibleDeviceString, 1);
|
||||
setenv("CUDA_VISIBLE_DEVICES", min_visibleDeviceString, 1);
|
||||
HIP_CHECK(hipGetDeviceCount(&numDevices));
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipGetDevice(&device));
|
||||
if (device == i) {
|
||||
validateCount+= 1;
|
||||
}
|
||||
}
|
||||
if (count != validateCount) {
|
||||
testResult = false;
|
||||
}
|
||||
} else if (cPid > 0) {
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
REQUIRE(testResult == true);
|
||||
}
|
||||
|
||||
static void testMinRvdMaxHvd(int numDevices, int *deviceList, int count) {
|
||||
bool testResult = true;
|
||||
int device;
|
||||
int validateCount = 0;
|
||||
char min_visibleDeviceString[MAX_SIZE] = {0};
|
||||
char max_visibleDeviceString[MAX_SIZE] = {0};
|
||||
int fd[2];
|
||||
pipe(fd);
|
||||
pid_t cPid;
|
||||
cPid = fork();
|
||||
if (cPid == 0) { // child
|
||||
Initialize(deviceList, numDevices, count,
|
||||
min_visibleDeviceString, max_visibleDeviceString);
|
||||
unsetenv("ROCR_VISIBLE_DEVICES");
|
||||
unsetenv("HIP_VISIBLE_DEVICES");
|
||||
setenv("ROCR_VISIBLE_DEVICES", min_visibleDeviceString, 1);
|
||||
setenv("HIP_VISIBLE_DEVICES", max_visibleDeviceString, 1);
|
||||
HIP_CHECK(hipGetDeviceCount(&numDevices));
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipGetDevice(&device));
|
||||
if (device == i) {
|
||||
validateCount+= 1;
|
||||
}
|
||||
}
|
||||
if (count != validateCount) {
|
||||
testResult = false;
|
||||
}
|
||||
close(fd[0]);
|
||||
write(fd[1], &testResult, sizeof(testResult));
|
||||
close(fd[1]);
|
||||
exit(0);
|
||||
} else if (cPid > 0) {
|
||||
close(fd[1]);
|
||||
read(fd[0], &testResult, sizeof(testResult));
|
||||
close(fd[0]);
|
||||
wait(NULL);
|
||||
} else {
|
||||
printf("fork() failed\n");
|
||||
HIP_ASSERT(false);
|
||||
}
|
||||
|
||||
REQUIRE(testResult == true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario sets Invalid visible device list and checks behavior.
|
||||
*/
|
||||
TEST_CASE("Unit_hipSetDevice_InvalidVisibleDeviceList") {
|
||||
int numDevices = 0;
|
||||
|
||||
getDeviceCount(&numDevices);
|
||||
REQUIRE(numDevices != 0);
|
||||
|
||||
SECTION("Test setting -1 to HIP_VISIBLE_DEVICES") {
|
||||
testInvalidDevice(numDevices, false, -1);
|
||||
}
|
||||
|
||||
SECTION("Test setting invalid device to HIP_VISIBLE_DEVICES") {
|
||||
testInvalidDevice(numDevices, false, numDevices);
|
||||
}
|
||||
#ifndef __HIP_PLATFORM_NVCC__
|
||||
SECTION("Test setting -1 to ROCR_VISIBLE_DEVICES") {
|
||||
testInvalidDevice(numDevices, true, -1);
|
||||
}
|
||||
|
||||
SECTION("Test setting invalid device to ROCR_VISIBLE_DEVICES") {
|
||||
testInvalidDevice(numDevices, true, numDevices);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario sets valid visible device list and checks behavior.
|
||||
*/
|
||||
TEST_CASE("Unit_hipSetDevice_ValidVisibleDeviceList") {
|
||||
int numDevices = 0;
|
||||
int deviceList[MAX_SIZE];
|
||||
|
||||
getDeviceCount(&numDevices);
|
||||
REQUIRE(numDevices != 0);
|
||||
|
||||
// Test for all available devices
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
deviceList[i] = i;
|
||||
}
|
||||
|
||||
SECTION("Test setting valid hip visible device list") {
|
||||
testValidDevices(numDevices, false, deviceList, numDevices);
|
||||
}
|
||||
#ifndef __HIP_PLATFORM_NVCC__
|
||||
SECTION("Test setting valid rocr visible device list") {
|
||||
testValidDevices(numDevices, true, deviceList, numDevices);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario sets subset of available devices and checks behavior.
|
||||
*/
|
||||
TEST_CASE("Unit_hipSetDevice_SubsetOfAvailableDevices") {
|
||||
int numDevices = 0;
|
||||
int deviceList[MAX_SIZE];
|
||||
int deviceListLength = 1;
|
||||
|
||||
getDeviceCount(&numDevices);
|
||||
REQUIRE(numDevices != 0);
|
||||
|
||||
// Test for subset of available gpus
|
||||
for (int i=0; i < deviceListLength; i++) {
|
||||
deviceList[i] = deviceListLength-1-i;
|
||||
}
|
||||
|
||||
#ifndef __HIP_PLATFORM_NVCC__
|
||||
testValidDevices(numDevices, true, deviceList,
|
||||
deviceListLength);
|
||||
#endif
|
||||
testValidDevices(numDevices, false, deviceList,
|
||||
deviceListLength);
|
||||
}
|
||||
|
||||
#ifndef __HIP_PLATFORM_NVCC__
|
||||
/* Following tests apply only for AMD Platforms */
|
||||
|
||||
/**
|
||||
* Scenario tests getDevice behavior with Minimal Len of RVD
|
||||
* and Maximal Len of HVD
|
||||
*/
|
||||
TEST_CASE("Unit_hipSetDevice_MinRvdMaxHvdDevicesList") {
|
||||
int numDevices = 0;
|
||||
int deviceList[MAX_SIZE];
|
||||
int count = 0;
|
||||
|
||||
getDeviceCount(&numDevices);
|
||||
|
||||
REQUIRE(numDevices != 0);
|
||||
|
||||
if (numDevices == 1) {
|
||||
deviceList[0] = 0;
|
||||
count = 1;
|
||||
} else {
|
||||
for (int i=0; i < numDevices; i++) {
|
||||
if (i%2 == 0) {
|
||||
deviceList[count] = i;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testMinRvdMaxHvd(numDevices, deviceList, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario tests getDevice behavior with Maximal Len of RVD
|
||||
* and Minimal Len of HVD
|
||||
*/
|
||||
TEST_CASE("Unit_hipSetDevice_MaxRvdMinHvdDevicesList") {
|
||||
int numDevices = 0;
|
||||
int deviceList[MAX_SIZE];
|
||||
int count = 0;
|
||||
|
||||
getDeviceCount(&numDevices);
|
||||
|
||||
REQUIRE(numDevices != 0);
|
||||
|
||||
if (numDevices == 1) {
|
||||
deviceList[0] = 0;
|
||||
count = 1;
|
||||
} else {
|
||||
for (int i=0; i < numDevices; i++) {
|
||||
if (i%2 == 0) {
|
||||
deviceList[count] = i;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testMaxRvdMinHvd(numDevices, deviceList, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario tests getDevice behavior with combination of RVD and CVD
|
||||
*/
|
||||
TEST_CASE("Unit_hipSetDevice_RvdCvdDevicesList") {
|
||||
int numDevices = 0;
|
||||
int deviceList[MAX_SIZE];
|
||||
int count = 0;
|
||||
|
||||
getDeviceCount(&numDevices);
|
||||
|
||||
REQUIRE(numDevices != 0);
|
||||
|
||||
if (numDevices == 1) {
|
||||
deviceList[0] = 0;
|
||||
count = 1;
|
||||
} else {
|
||||
for (int i=0; i < numDevices; i++) {
|
||||
if (i%2 == 0) {
|
||||
deviceList[count] = i;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testRvdCvd(numDevices, deviceList, count);
|
||||
}
|
||||
#endif // __HIP_PLATFORM_NVCC__
|
||||
|
||||
#endif // __linux__
|
||||
@@ -0,0 +1,114 @@
|
||||
|
||||
# Copyright (c) 2016 - 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.
|
||||
cmake_minimum_required(VERSION 3.16.8)
|
||||
# vc 19.31.31107.0 gives issue while packaging using makefile
|
||||
# To avoid error NMAKE : fatal error U1065: invalid option 'w'
|
||||
# Windows to use Ninja generator like other projects
|
||||
# to skip the simple compiler test
|
||||
set(CMAKE_C_COMPILER_WORKS 1)
|
||||
set(CMAKE_CXX_COMPILER_WORKS 1)
|
||||
|
||||
project(tests)
|
||||
|
||||
MACRO(SUBDIRLIST result curdir)
|
||||
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
|
||||
SET(dirlist "")
|
||||
FOREACH(child ${children})
|
||||
IF(IS_DIRECTORY ${curdir}/${child})
|
||||
LIST(APPEND dirlist ${child})
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
SET(${result} ${dirlist})
|
||||
ENDMACRO()
|
||||
|
||||
SUBDIRLIST(SUBDIRS @PROJECT_BINARY_DIR@/@CATCH_BUILD_DIR@)
|
||||
|
||||
FOREACH(subdir ${SUBDIRS})
|
||||
set(CONTENT ${CONTENT} "subdirs(${subdir}) \n")
|
||||
ENDFOREACH()
|
||||
# Creating a CTestTestfile so ctest can be executed from @CATCH_BUILD_DIR@ level
|
||||
# This also helps in executing through jenkins and avoid permission issues
|
||||
file(WRITE @PROJECT_BINARY_DIR@/@CATCH_BUILD_DIR@/CTestTestfile.cmake
|
||||
${CONTENT})
|
||||
|
||||
install(DIRECTORY @PROJECT_BINARY_DIR@/@CATCH_BUILD_DIR@
|
||||
DESTINATION .
|
||||
USE_SOURCE_PERMISSIONS
|
||||
DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
|
||||
GROUP_WRITE GROUP_READ GROUP_EXECUTE
|
||||
WORLD_WRITE WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
install(FILES @PROJECT_BINARY_DIR@/CTestTestfile.cmake
|
||||
DESTINATION .)
|
||||
|
||||
#############################
|
||||
# Packaging steps
|
||||
#############################
|
||||
set(CPACK_SET_DESTDIR TRUE)
|
||||
set(CPACK_INSTALL_PREFIX @CPACK_INSTALL_PREFIX@)
|
||||
if(NOT DEFINED CPACK_INSTALL_PREFIX)
|
||||
set(CPACK_INSTALL_PREFIX "/opt/rocm/test/hip/")
|
||||
endif()
|
||||
|
||||
|
||||
set(PKG_NAME hip-catch-@HIP_PLATFORM@)
|
||||
set(CPACK_PACKAGE_NAME ${PKG_NAME})
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "HIP: Heterogenous-computing Interface for Portability [CATCH TESTS]")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "HIP:
|
||||
Heterogenous-computing Interface for Portability [CATCH TESTS]")
|
||||
set(CPACK_PACKAGE_VENDOR "Advanced Micro Devices, Inc.")
|
||||
set(CPACK_PACKAGE_CONTACT "HIP Support <hip.support@amd.com>")
|
||||
set(CPACK_PACKAGE_VERSION @HIP_VERSION_MAJOR@.@HIP_VERSION_MINOR@.@HIP_VERSION_PATCH_GITHASH@)
|
||||
|
||||
# to remove hip-catch-* package during uninstallation of rocm
|
||||
set (CPACK_DEBIAN_PACKAGE_DEPENDS "rocm-core")
|
||||
set (CPACK_RPM_PACKAGE_REQUIRES "rocm-core")
|
||||
|
||||
if(NOT WIN32)
|
||||
set(CPACK_GENERATOR "TGZ;DEB;RPM" CACHE STRING "Linux package types for catch tests")
|
||||
set(CPACK_BINARY_DEB "ON")
|
||||
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
|
||||
set(CPACK_DEBIAN_PACKAGE_PROVIDES "catch")
|
||||
set(CPACK_BINARY_RPM "ON")
|
||||
set(CPACK_RPM_FILE_NAME "RPM-DEFAULT")
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/opt")
|
||||
if (CPACK_PACKAGE_VERSION MATCHES "local" )
|
||||
#If building locally default value will cause build failure
|
||||
#DEBUG SYMBOL pacaking require SOURCE_DIR to be small
|
||||
set(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX ${CPACK_INSTALL_PREFIX})
|
||||
endif()
|
||||
|
||||
set(CPACK_SOURCE_GENERATOR "TGZ")
|
||||
# Install license file
|
||||
set ( CPACK_RESOURCE_FILE_LICENSE "@CMAKE_CURRENT_LIST_DIR@/../LICENSE.txt" )
|
||||
install(FILES ${CPACK_RESOURCE_FILE_LICENSE} DESTINATION .)
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
|
||||
else()
|
||||
# windows packaging
|
||||
set(CPACK_INSTALL_PREFIX "")
|
||||
set(CPACK_GENERATOR "ZIP" CACHE STRING "Windows package types for catch tests")
|
||||
set(CPACK_TGZ_FILE_NAME "ZIP-DEFAULT")
|
||||
set(CPACK_TEST_ZIP "ON")
|
||||
set(CPACK_ZIP_TEST_PACKAGE_NAME "catch")
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
@@ -0,0 +1,9 @@
|
||||
add_custom_target(stress_test COMMAND "${CMAKE_CTEST_COMMAND}" -R "Stress_"
|
||||
COMMENT "Build complete, now executing the stress test ...")
|
||||
|
||||
add_subdirectory(memory)
|
||||
if(HIP_PLATFORM MATCHES "amd")
|
||||
add_subdirectory(printf)
|
||||
add_subdirectory(stream)
|
||||
endif()
|
||||
add_subdirectory(deviceallocation)
|
||||
@@ -0,0 +1,8 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
Stress_deviceAllocationStress.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME devalloc
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME stress_test)
|
||||
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_kernels.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
#include <unistd.h>
|
||||
// Size Macros
|
||||
#define MEMORY_CHUNK_SIZE (1024*1024)
|
||||
#define MEMORY_CHUNK_SIZE_ODD (1025*1025)
|
||||
#define MAXIMUM_CHUNKS (256*1024)
|
||||
// Subtest Macros
|
||||
#define NO_ALLOCATION_ONHOST 0
|
||||
#define ALLOCATE_ONHOST_HIPMALLOCMANAGED 1
|
||||
#define ALLOCATE_ONHOST_HIPMALLOC 2
|
||||
// Test Type Macros
|
||||
#define TEST_MALLOC_FREE 1
|
||||
#define TEST_NEW_DELETE 2
|
||||
// GPU threads
|
||||
#define BLOCKSIZE 512
|
||||
#define GRIDSIZE 512
|
||||
// Test parameters
|
||||
// Two different loops
|
||||
#define NUM_OF_LOOP_SINGLE_KER 100000
|
||||
#define NUM_OF_LOOP_MULTIPLE_KER 20000
|
||||
|
||||
// The following flag is defined for platforms (nvidia)
|
||||
// which honors device memory limit. For AMD this flag
|
||||
// is disabled and defect is raised.
|
||||
#if HT_NVIDIA
|
||||
#define HT_HONORS_DEVICEMEMORY_LIMIT
|
||||
#endif
|
||||
|
||||
#ifdef HT_HONORS_DEVICEMEMORY_LIMIT
|
||||
__device__ static char* dev_mem_glob[MAXIMUM_CHUNKS];
|
||||
#endif
|
||||
__device__ static int* dev_mem[GRIDSIZE];
|
||||
__device__ static int* dev_common_ptr;
|
||||
|
||||
#ifdef HT_HONORS_DEVICEMEMORY_LIMIT
|
||||
/**
|
||||
* This kernel checks kernel allocation of size more than available
|
||||
* memory.
|
||||
*/
|
||||
static __global__ void kerTestDynamicAllocNeg(int test_type,
|
||||
size_t perThreadSize,
|
||||
int *ret) {
|
||||
// Allocate
|
||||
char* ptr = nullptr;
|
||||
printf("Memory to allocate in GPU = %zu \n", perThreadSize);
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
ptr = reinterpret_cast<char*> (malloc(perThreadSize));
|
||||
} else {
|
||||
ptr = new char[perThreadSize];
|
||||
}
|
||||
printf("Allocation Done \n");
|
||||
if (ptr == nullptr) {
|
||||
printf("Allocation Failed. PASSED! \n");
|
||||
*ret = 0;
|
||||
return;
|
||||
} else {
|
||||
// Free memory
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
free(ptr);
|
||||
} else {
|
||||
delete[] ptr;
|
||||
}
|
||||
*ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel allocates memory till nullptr is returned.
|
||||
*/
|
||||
static __global__ void kerAllocTillExhaust(int test_type,
|
||||
size_t *total_allocated_mem,
|
||||
size_t mem_chunk_size) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate memory in thread 0 of block 0
|
||||
if (0 == myId) {
|
||||
for (int idx = 0; idx < MAXIMUM_CHUNKS; idx++) {
|
||||
dev_mem_glob[idx] = nullptr;
|
||||
}
|
||||
int idx = 0;
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
do {
|
||||
dev_mem_glob[idx] =
|
||||
reinterpret_cast<char*> (malloc(mem_chunk_size));
|
||||
if (idx >= MAXIMUM_CHUNKS) {
|
||||
break;
|
||||
}
|
||||
} while (dev_mem_glob[idx++] != nullptr);
|
||||
} else {
|
||||
do {
|
||||
dev_mem_glob[idx] =
|
||||
reinterpret_cast<char*> (new char[mem_chunk_size]);
|
||||
if (idx >= MAXIMUM_CHUNKS) {
|
||||
break;
|
||||
}
|
||||
} while (dev_mem_glob[idx++] != nullptr);
|
||||
}
|
||||
idx = 0;
|
||||
*total_allocated_mem = 0;
|
||||
while ((dev_mem_glob[idx] != nullptr) &&
|
||||
(idx < MAXIMUM_CHUNKS)) {
|
||||
*total_allocated_mem = *total_allocated_mem + mem_chunk_size;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel deletes the memory.
|
||||
*/
|
||||
static __global__ void kerFreeAll(int test_type) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
if (0 == myId) {
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
int idx = 0;
|
||||
while (dev_mem_glob[idx] != nullptr) {
|
||||
free(dev_mem_glob[idx++]);
|
||||
if (idx >= MAXIMUM_CHUNKS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int idx = 0;
|
||||
while (dev_mem_glob[idx] != nullptr) {
|
||||
delete[] (dev_mem_glob[idx++]);
|
||||
if (idx >= MAXIMUM_CHUNKS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* This kernel allocates memory once in thread 0 of each block and
|
||||
* access this memory in all threads of the block. The memory is
|
||||
* finally deleted in last thread of each block.
|
||||
*/
|
||||
static __global__ void kerBlockLevelMemoryAllocation(int *outputBuf,
|
||||
int test_type) {
|
||||
int myThreadId = threadIdx.x, lastThreadId = (blockDim.x - 1);
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate memory in thread 0
|
||||
if (0 == myThreadId) {
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
dev_mem[blockIdx.x] =
|
||||
reinterpret_cast<int*> (malloc(blockDim.x*sizeof(int)));
|
||||
} else {
|
||||
dev_mem[blockIdx.x] =
|
||||
reinterpret_cast<int*> (new int[blockDim.x]);
|
||||
}
|
||||
}
|
||||
// All threads wait at this barrier
|
||||
__syncthreads();
|
||||
// Check allocated memory in all threads in block before access
|
||||
if (dev_mem[blockIdx.x] == nullptr) {
|
||||
printf("Device Allocation Failed in thread = %d \n", myId);
|
||||
return;
|
||||
}
|
||||
int *ptr = reinterpret_cast<int*> (dev_mem[blockIdx.x]);
|
||||
// Copy to buffer
|
||||
ptr[myThreadId] = myId;
|
||||
// All threads wait
|
||||
__syncthreads();
|
||||
// Copy memory to host and free the memory in thread <blockDim.x - 1>
|
||||
if (lastThreadId == myThreadId) {
|
||||
for (size_t idx = 0; idx < blockDim.x; idx++) {
|
||||
outputBuf[idx + blockDim.x * blockIdx.x] = ptr[idx];
|
||||
}
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
free(ptr);
|
||||
} else {
|
||||
delete[] ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel allocates memory in one thread.
|
||||
*/
|
||||
static __global__ void kerAlloc(int test_type) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Allocate memory in thread 0 of block 0
|
||||
if (0 == myId) {
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
dev_common_ptr =
|
||||
reinterpret_cast<int*> (malloc(blockDim.x*gridDim.x*sizeof(int)));
|
||||
} else {
|
||||
dev_common_ptr =
|
||||
reinterpret_cast<int*> (new int[blockDim.x*gridDim.x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel writes to memory allocated in <kerAlloc>.
|
||||
*/
|
||||
static __global__ void kerWrite() {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Check allocated memory in all threads in block before access
|
||||
if (dev_common_ptr == nullptr) {
|
||||
printf("Device Allocation Failed in thread = %d \n", myId);
|
||||
return;
|
||||
}
|
||||
// Copy to buffer
|
||||
dev_common_ptr[myId] = myId;
|
||||
}
|
||||
|
||||
/**
|
||||
* This kernel copies the contents of memory allocated in <kerAlloc>
|
||||
* to host and deletes the memory from thread 0.
|
||||
*/
|
||||
static __global__ void kerFree(int *outputBuf, int test_type) {
|
||||
int myId = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
// Check allocated memory in all threads in block before access
|
||||
if (dev_common_ptr == nullptr) {
|
||||
printf("Device Allocation Failed in thread = %d \n", myId);
|
||||
return;
|
||||
}
|
||||
if (0 == myId) {
|
||||
for (size_t idx = 0; idx < (blockDim.x*gridDim.x); idx++) {
|
||||
outputBuf[idx] = dev_common_ptr[idx];
|
||||
}
|
||||
if (test_type == TEST_MALLOC_FREE) {
|
||||
free(dev_common_ptr);
|
||||
} else {
|
||||
delete[] dev_common_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HT_HONORS_DEVICEMEMORY_LIMIT
|
||||
/**
|
||||
* Local function: Launch kerAllocTillExhaust<<<>>> and
|
||||
* kerFreeAll<<<>>> to test memory allocation till all device
|
||||
* memory is exhausted.
|
||||
*/
|
||||
static bool TestAllocationOfAllAvailableMemory(int test_type,
|
||||
int category, size_t mem_chunk_size) {
|
||||
size_t avail1 = 0, avail2 = 0, tot = 0;
|
||||
constexpr size_t host_alloc = 2147483648; // 2 GB
|
||||
HIP_CHECK(hipMemGetInfo(&avail1, &tot));
|
||||
#if HT_NVIDIA
|
||||
HIP_CHECK(hipDeviceSetLimit(hipLimitMallocHeapSize, avail1));
|
||||
#endif
|
||||
size_t *tot_alloc_mem_d = nullptr, *tot_alloc_mem_h = nullptr;
|
||||
tot_alloc_mem_h =
|
||||
reinterpret_cast<size_t*> (malloc(sizeof(size_t)));
|
||||
REQUIRE(nullptr != tot_alloc_mem_h);
|
||||
HIP_CHECK(hipMalloc(&tot_alloc_mem_d, sizeof(size_t)));
|
||||
REQUIRE(nullptr != tot_alloc_mem_d);
|
||||
char *devptrHost = nullptr;
|
||||
if (category == ALLOCATE_ONHOST_HIPMALLOCMANAGED) {
|
||||
HIP_CHECK(hipMallocManaged(&devptrHost, host_alloc));
|
||||
} else if (category == ALLOCATE_ONHOST_HIPMALLOC) {
|
||||
HIP_CHECK(hipMalloc(&devptrHost, host_alloc));
|
||||
}
|
||||
HIP_CHECK(hipMemGetInfo(&avail1, &tot));
|
||||
INFO("Total available memory " << tot);
|
||||
INFO("Available memory before allocation " << avail1);
|
||||
// Launch Test Kernel
|
||||
kerAllocTillExhaust<<<1, 1>>>(test_type, tot_alloc_mem_d,
|
||||
mem_chunk_size);
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Copy to host buffer
|
||||
HIP_CHECK(hipMemcpy(tot_alloc_mem_h, tot_alloc_mem_d,
|
||||
sizeof(size_t), hipMemcpyDefault));
|
||||
HIP_CHECK(hipMemGetInfo(&avail2, &tot));
|
||||
kerFreeAll<<<1, 1>>>(test_type);
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Copy to host buffer
|
||||
bool bPassed = false;
|
||||
INFO("Available memory after allocation " << avail2);
|
||||
if (category == NO_ALLOCATION_ONHOST) {
|
||||
size_t allocated_dev_mem = (tot - avail2);
|
||||
if (allocated_dev_mem >= *tot_alloc_mem_h) {
|
||||
bPassed = true;
|
||||
}
|
||||
} else if ((category == ALLOCATE_ONHOST_HIPMALLOCMANAGED) ||
|
||||
(category == ALLOCATE_ONHOST_HIPMALLOC)) {
|
||||
size_t allocated_dev_mem = (tot - avail2 - host_alloc);
|
||||
if (allocated_dev_mem >= *tot_alloc_mem_h) {
|
||||
bPassed = true;
|
||||
}
|
||||
hipFree(devptrHost);
|
||||
}
|
||||
hipFree(tot_alloc_mem_d);
|
||||
free(tot_alloc_mem_h);
|
||||
return bPassed;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Local function: Launch kerBlockLevelMemoryAllocation<<<>>>
|
||||
* in a loop to stress test allocation and deallocation.
|
||||
*/
|
||||
static bool TestMemoryAllocationInLoop(int test_type,
|
||||
bool isMultikernel = false) {
|
||||
int *outputVec_d{nullptr}, *outputVec_h{nullptr};
|
||||
int arraysize = (BLOCKSIZE * GRIDSIZE);
|
||||
outputVec_h = reinterpret_cast<int*> (malloc(sizeof(int) * arraysize));
|
||||
REQUIRE(outputVec_h != nullptr);
|
||||
HIP_CHECK(hipMalloc(&outputVec_d, (sizeof(int) * arraysize)));
|
||||
bool bPassed = true;
|
||||
// Launch Test Kernel
|
||||
int max_index = 0;
|
||||
if (isMultikernel) {
|
||||
max_index = NUM_OF_LOOP_MULTIPLE_KER;
|
||||
} else {
|
||||
max_index = NUM_OF_LOOP_SINGLE_KER;
|
||||
}
|
||||
for (int idx = 0; idx < max_index; idx++) {
|
||||
if (isMultikernel) {
|
||||
kerAlloc<<<GRIDSIZE, BLOCKSIZE>>>(test_type);
|
||||
kerWrite<<<GRIDSIZE, BLOCKSIZE>>>();
|
||||
kerFree<<<GRIDSIZE, BLOCKSIZE>>>(outputVec_d, test_type);
|
||||
} else {
|
||||
kerBlockLevelMemoryAllocation<<<GRIDSIZE, BLOCKSIZE>>>(outputVec_d,
|
||||
test_type);
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Copy to host buffer
|
||||
HIP_CHECK(hipMemcpy(outputVec_h, outputVec_d, sizeof(int) * arraysize,
|
||||
hipMemcpyDefault));
|
||||
bPassed = true;
|
||||
for (int idx = 0; idx < arraysize; idx++) {
|
||||
if (outputVec_h[idx] != idx) {
|
||||
bPassed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!bPassed) break;
|
||||
}
|
||||
HIP_CHECK(hipFree(outputVec_d));
|
||||
free(outputVec_h);
|
||||
return bPassed;
|
||||
}
|
||||
|
||||
#ifdef HT_HONORS_DEVICEMEMORY_LIMIT
|
||||
/**
|
||||
* Scenario: Test malloc till nullptr is returned using even chunksize.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_malloc_Even") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_MALLOC_FREE,
|
||||
NO_ALLOCATION_ONHOST, MEMORY_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: Test malloc till nullptr is returned using odd chunksize.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_malloc_Odd") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_MALLOC_FREE,
|
||||
NO_ALLOCATION_ONHOST, MEMORY_CHUNK_SIZE_ODD));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: Test new till nullptr is returned using even chunksize.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_new_Even") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_NEW_DELETE,
|
||||
NO_ALLOCATION_ONHOST, MEMORY_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: Test new till nullptr is returned using odd chunksize.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_new_Odd") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_NEW_DELETE,
|
||||
NO_ALLOCATION_ONHOST, MEMORY_CHUNK_SIZE_ODD));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test checks device allocation using malloc till nullptr
|
||||
* is returned. Device memory is also allocated using hipmallocmanaged
|
||||
* from host.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_malloc_hipmallocmanaged") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_MALLOC_FREE,
|
||||
ALLOCATE_ONHOST_HIPMALLOCMANAGED, MEMORY_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test checks device allocation using new till nullptr
|
||||
* is returned. Device memory is also allocated using hipmallocmanaged
|
||||
* from host.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_new_hipmallocmanaged") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_NEW_DELETE,
|
||||
ALLOCATE_ONHOST_HIPMALLOCMANAGED, MEMORY_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test checks device allocation using malloc till nullptr
|
||||
* is returned. Device memory is also allocated using hipmalloc from host.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_malloc_hipmalloc") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_MALLOC_FREE,
|
||||
ALLOCATE_ONHOST_HIPMALLOC, MEMORY_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test checks device allocation using new till nullptr
|
||||
* is returned. Device memory is also allocated using hipmalloc from host.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_new_hipmalloc") {
|
||||
REQUIRE(true == TestAllocationOfAllAvailableMemory(TEST_NEW_DELETE,
|
||||
ALLOCATE_ONHOST_HIPMALLOC, MEMORY_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test validates device allocation negative scenario
|
||||
* when size > available memory.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_Negative") {
|
||||
int *ret_d{nullptr}, *ret_h{nullptr};
|
||||
size_t avail = 0, tot = 0;
|
||||
HIP_CHECK(hipMemGetInfo(&avail, &tot));
|
||||
printf("Available Memory in GPU = %zu \n", avail);
|
||||
ret_h = reinterpret_cast<int*> (malloc(sizeof(int)));
|
||||
REQUIRE(ret_h != nullptr);
|
||||
HIP_CHECK(hipMalloc(&ret_d, (sizeof(int))));
|
||||
SECTION("Test allocation with malloc") {
|
||||
kerTestDynamicAllocNeg<<<1, 1>>>(TEST_MALLOC_FREE, (avail + 1), ret_d);
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
HIP_CHECK(hipMemcpy(ret_h, ret_d, sizeof(int), hipMemcpyDefault));
|
||||
REQUIRE(0 == *ret_h);
|
||||
}
|
||||
|
||||
SECTION("Test allocation with new") {
|
||||
kerTestDynamicAllocNeg<<<1, 1>>>(TEST_NEW_DELETE, (avail + 1), ret_d);
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
HIP_CHECK(hipMemcpy(ret_h, ret_d, sizeof(int), hipMemcpyDefault));
|
||||
REQUIRE(0 == *ret_h);
|
||||
}
|
||||
hipFree(ret_d);
|
||||
free(ret_h);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Scenario: This test performs stress test of malloc/free in a loop
|
||||
* using single kernel.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_malloc_loop_singlekernel") {
|
||||
REQUIRE(true == TestMemoryAllocationInLoop(TEST_MALLOC_FREE, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test performs stress test of new/delete in a loop
|
||||
* using single kernel.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_new_loop_singlekernel") {
|
||||
REQUIRE(true == TestMemoryAllocationInLoop(TEST_NEW_DELETE, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test performs stress test of malloc/free in a loop
|
||||
* using multiple kernel.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_malloc_loop_multkernel") {
|
||||
REQUIRE(true == TestMemoryAllocationInLoop(TEST_MALLOC_FREE, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario: This test performs stress test of new/delete in a loop
|
||||
* using multiple kernel.
|
||||
*/
|
||||
TEST_CASE("Stress_deviceAllocation_new_loop_multkernel") {
|
||||
REQUIRE(true == TestMemoryAllocationInLoop(TEST_NEW_DELETE, true));
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
memcpy.cc
|
||||
hipMemcpyMThreadMSize.cc
|
||||
hipMallocManagedStress.cc
|
||||
hipMemPrftchAsyncStressTst.cc
|
||||
hipHostMalloc.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME memory
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME stress_test)
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANNTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hip_test_common.hh"
|
||||
#include "hip_test_helper.hh"
|
||||
|
||||
// Stress allocation tests
|
||||
// Try to allocate as much memory as possible
|
||||
// But since max allocation can fail, we need to try the next value
|
||||
|
||||
TEST_CASE("Stress_hipHostMalloc_MaxAllocation") {
|
||||
size_t devMemAvail{0}, devMemFree{0};
|
||||
HIP_CHECK(hipMemGetInfo(&devMemFree, &devMemAvail));
|
||||
auto hostMemFree = HipTest::getMemoryAmount() /* In MB */ * 1024 * 1024; // In bytes
|
||||
REQUIRE(devMemFree > 0);
|
||||
REQUIRE(devMemAvail > 0);
|
||||
REQUIRE(hostMemFree > 0);
|
||||
|
||||
size_t memFree = std::min(devMemFree, hostMemFree); // which is the limiter cpu or gpu
|
||||
char* d_ptr{nullptr};
|
||||
size_t counter{0};
|
||||
|
||||
INFO("Max Allocation of " << memFree << " bytes!");
|
||||
while (hipHostMalloc(&d_ptr, memFree) != hipSuccess && memFree > 1) {
|
||||
counter++;
|
||||
INFO("Attempt to allocate " << memFree << " bytes out of " << devMemFree << "bytes Failed!");
|
||||
memFree >>= 1; // reduce the memory to be allocated by half
|
||||
REQUIRE(counter <= 2); // Make sure that we are atleast able to allocate 1/4th of max memory
|
||||
}
|
||||
|
||||
HIP_CHECK(hipMemset(d_ptr, 1, memFree));
|
||||
HIP_CHECK(hipDeviceSynchronize()); // Flush caches
|
||||
REQUIRE(std::all_of(d_ptr, d_ptr + memFree, [](unsigned char n) { return n == 1; }));
|
||||
HIP_CHECK(hipHostFree(d_ptr));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
#include <memory>
|
||||
|
||||
// Stress allocation tests
|
||||
// Try to allocate as much memory as possible
|
||||
// But since max allocation can fail, we need to be happy with atleast 1/4th of memory
|
||||
TEST_CASE("Stress_hipMalloc_HighSizeAlloc") {
|
||||
size_t devMemTotal{0}, devMemFree{0};
|
||||
HIP_CHECK(hipMemGetInfo(&devMemFree, &devMemTotal));
|
||||
REQUIRE(devMemFree > 0);
|
||||
REQUIRE(devMemTotal > 0);
|
||||
|
||||
char* d_ptr{nullptr};
|
||||
size_t counter{0};
|
||||
|
||||
INFO("Free Mem Available: " << devMemFree << " bytes out of " << devMemTotal << " bytes!");
|
||||
while (hipMalloc(&d_ptr, devMemFree) != hipSuccess && devMemFree > 1) {
|
||||
counter++;
|
||||
devMemFree >>= 1; // reduce the memory to be allocated by half
|
||||
INFO("Attempt to allocate " << devMemFree << " bytes out of " << devMemTotal
|
||||
<< " bytes failed!");
|
||||
REQUIRE(counter <= 2); // Make sure that we are atleast able to allocate 1/4th of max memory
|
||||
}
|
||||
|
||||
HIP_CHECK(hipMemset(d_ptr, 1, devMemFree));
|
||||
auto ptr = std::unique_ptr<unsigned char[]>{new unsigned char[devMemFree]};
|
||||
HIP_CHECK(hipMemcpy(ptr.get(), d_ptr, devMemFree, hipMemcpyDeviceToHost));
|
||||
HIP_CHECK(hipFree(d_ptr));
|
||||
REQUIRE(std::all_of(ptr.get(), ptr.get() + devMemFree, [](unsigned char n) { return n == 1; }));
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
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, INNCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANNY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER INN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR INN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// The following test case allocation, host access, device access of HMM
|
||||
// memory from size 1 to 10KB
|
||||
/* Test Case Description:
|
||||
1) Testing allocation, host access, device access of HMM
|
||||
memory from size 1 to 10KB
|
||||
2) The following test case tests the behavior of kernel with a HMM memory
|
||||
and hipMalloc memory
|
||||
3) The following test case tests when the same Hmm memory is used for
|
||||
launching multiple different kernels will results in any issue
|
||||
4) Testing the allocation of/scenarios around max possible memory
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
|
||||
#define INCRMNT 10
|
||||
// Kernel function
|
||||
__global__ void KrnlWth2MemTypesC(unsigned char *Hmm, unsigned char *Dptr,
|
||||
size_t n) {
|
||||
size_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
for (size_t i = index; i < n; i += stride) {
|
||||
Hmm[i] = Dptr[i] + INCRMNT;
|
||||
}
|
||||
}
|
||||
static bool IfTestPassed = true;
|
||||
|
||||
// Kernel functions
|
||||
__global__ void KrnlWth2MemTypes(int *Hmm, int *Dptr, size_t n) {
|
||||
size_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
for (size_t i = index; i < n; i++) {
|
||||
Hmm[i] = Dptr[i] + 10;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void KernelMulAdd_MngdMem(int *Hmm, size_t n) {
|
||||
size_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
for (size_t i = index; i < n; i += stride) {
|
||||
Hmm[i] = Hmm[i] * 2 + 10;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void KernelMul_MngdMem(int *Hmm, int *Dptr, size_t n) {
|
||||
size_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
size_t stride = blockDim.x * gridDim.x;
|
||||
for (size_t i = index; i < n; i += stride) {
|
||||
Hmm[i] = Dptr[i] * 10;
|
||||
}
|
||||
}
|
||||
|
||||
static void LaunchKrnl4(size_t NumElms, int InitVal) {
|
||||
int *Hmm = NULL, *Dptr = NULL, blockSize = 64, DataMismatch = 0;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
HIP_CHECK(hipMallocManaged(&Hmm, (sizeof(int) * NumElms)));
|
||||
HIP_CHECK(hipMalloc(&Dptr, (sizeof(int) * NumElms)));
|
||||
int *Hstptr = reinterpret_cast<int*>(new int[NumElms]);
|
||||
for (size_t i = 0; i < NumElms; ++i) {
|
||||
Hstptr[i] = InitVal;
|
||||
}
|
||||
HIP_CHECK(hipMemcpy(Dptr, Hstptr, (NumElms * sizeof(int)),
|
||||
hipMemcpyHostToDevice));
|
||||
dim3 dimBlock(blockSize, 1, 1);
|
||||
dim3 dimGrid((NumElms + blockSize -1)/blockSize, 1, 1);
|
||||
KrnlWth2MemTypes<<<dimGrid, dimBlock, 0, strm>>>(Hmm, Dptr, NumElms);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
for (size_t i = 0; i < NumElms; ++i) {
|
||||
if (Hmm[i] != (InitVal + 10)) {
|
||||
DataMismatch++;
|
||||
}
|
||||
}
|
||||
if (DataMismatch != 0) {
|
||||
INFO("Data Mismatch observed after the Kernel: KrnlWth2MemTypes!!\n");
|
||||
REQUIRE(false);
|
||||
}
|
||||
DataMismatch = 0;
|
||||
KernelMul_MngdMem<<<dimGrid, dimBlock, 0, strm>>>(Hmm, Dptr, NumElms);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
// Verifying the result
|
||||
for (size_t i = 0; i < NumElms; ++i) {
|
||||
if (Hmm[i] != (InitVal * 10)) {
|
||||
DataMismatch++;
|
||||
}
|
||||
}
|
||||
if (DataMismatch != 0) {
|
||||
INFO("Data Mismatch observedafter the Kernel: KernelMul_MngdMem!!\n");
|
||||
REQUIRE(false);
|
||||
}
|
||||
DataMismatch = 0;
|
||||
KernelMulAdd_MngdMem<<<dimGrid, dimBlock, 0, strm>>>(Hmm, NumElms);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
// Verifying the result
|
||||
|
||||
for (size_t i = 0; i < NumElms; ++i) {
|
||||
if (Hmm[i] != (InitVal * 10 * 2 + 10)) {
|
||||
DataMismatch++;
|
||||
}
|
||||
}
|
||||
if (DataMismatch != 0) {
|
||||
INFO("Data Mismatch observedafter the Kernel: KernelMul_MngdMem!!\n");
|
||||
REQUIRE(false);
|
||||
}
|
||||
delete[] Hstptr;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
HIP_CHECK(hipDeviceGetAttribute(&managed, hipDeviceAttributeManagedMemory,
|
||||
0));
|
||||
INFO("hipDeviceAttributeManagedMemory: " << managed);
|
||||
return managed;
|
||||
}
|
||||
// The following test case allocation, host access, device access of HMM
|
||||
// memory from size 1 to 10KB
|
||||
|
||||
TEST_CASE("Stress_hipMallocManaged_MultiSize") {
|
||||
IfTestPassed = true;
|
||||
int managed = HmmAttrPrint();
|
||||
if (managed == 1) {
|
||||
unsigned char *Hmm1 = nullptr, *Hmm2 = nullptr;
|
||||
int InitVal = 100, blockSize = 64, DataMismatch = 0;
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
dim3 dimBlock(blockSize, 1, 1);
|
||||
for (int i = 1; i < (1024*100); ++i) {
|
||||
HIP_CHECK(hipMallocManaged(&Hmm1, i));
|
||||
HIP_CHECK(hipMallocManaged(&Hmm2, i));
|
||||
for (int j = 0; j < i; ++j) {
|
||||
Hmm1[j] = InitVal;
|
||||
}
|
||||
dim3 dimGrid((i + blockSize -1)/blockSize, 1, 1);
|
||||
KrnlWth2MemTypesC<<<dimGrid, dimBlock, 0, strm>>>(Hmm2, Hmm1, i);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
// Verifying the results
|
||||
for (int k = 0; k < i; ++k) {
|
||||
if (Hmm2[k] != (InitVal + INCRMNT)) {
|
||||
DataMismatch++;
|
||||
}
|
||||
}
|
||||
if (DataMismatch != 0) {
|
||||
WARN("DataMismatch observed!\n");
|
||||
IfTestPassed = false;
|
||||
}
|
||||
DataMismatch = 0;
|
||||
HIP_CHECK(hipFree(Hmm1));
|
||||
HIP_CHECK(hipFree(Hmm2));
|
||||
if (IfTestPassed == false) {
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// The following test case tests the behavior of kernel with a HMM memory and
|
||||
// hipMalloc memory
|
||||
|
||||
TEST_CASE("Stress_hipMallocManaged_KrnlWth2MemTypes") {
|
||||
IfTestPassed = true;
|
||||
int *Hmm = NULL, *Dptr = NULL, InitVal = 123;
|
||||
size_t NumElms = (1024 * 1024);
|
||||
int *Hptr = new int[NumElms], blockSize = 64, DataMismatch = 0;
|
||||
int managed = HmmAttrPrint();
|
||||
if (managed == 1) {
|
||||
hipStream_t strm;
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
HIP_CHECK(hipMallocManaged(&Hmm, sizeof(int) * NumElms));
|
||||
HIP_CHECK(hipMalloc(&Dptr, sizeof(int) * NumElms));
|
||||
for (size_t i = 0; i < NumElms; ++i) {
|
||||
Hmm[i] = 0;
|
||||
Hptr[i] = InitVal;
|
||||
}
|
||||
HIP_CHECK(hipMemcpy(Dptr, Hptr, sizeof(int) * NumElms,
|
||||
hipMemcpyHostToDevice));
|
||||
dim3 dimBlock(blockSize, 1, 1);
|
||||
dim3 dimGrid((NumElms + blockSize -1)/blockSize, 1, 1);
|
||||
KrnlWth2MemTypes<<<dimGrid, dimBlock, 0, strm>>>(Hmm, Dptr, NumElms);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
// Verifying the results
|
||||
for (size_t k = 0; k < NumElms; ++k) {
|
||||
if (Hmm[k] != (InitVal + 10)) {
|
||||
DataMismatch++;
|
||||
}
|
||||
}
|
||||
if (DataMismatch != 0) {
|
||||
WARN("DataMismatch observed!\n");
|
||||
IfTestPassed = false;
|
||||
}
|
||||
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
HIP_CHECK(hipFree(Dptr));
|
||||
delete[] Hptr;
|
||||
REQUIRE(IfTestPassed);
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The following test case tests when the same Hmm memory is used for
|
||||
// launching multiple different kernels will results in any issue
|
||||
TEST_CASE("Stress_hipMallocManaged_MultiKrnlHmmAccess") {
|
||||
int managed = HmmAttrPrint();
|
||||
if (managed) {
|
||||
int InitVal = 123, NumElms = (1024 * 1024);
|
||||
LaunchKrnl4(NumElms, InitVal);
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Testing the allocation of/scenarios around max possible memory
|
||||
TEST_CASE("Stress_hipMallocManaged_ExtremeSizes") {
|
||||
int managed = HmmAttrPrint();
|
||||
if (managed == 1) {
|
||||
bool IfTestPassed = true;
|
||||
hipError_t err;
|
||||
void *Hmm = NULL;
|
||||
size_t totalDevMem = 0, freeDevMem = 0;
|
||||
int NumDevs = 0;
|
||||
HIP_CHECK(hipGetDeviceCount(&NumDevs));
|
||||
// Testing allocation of extreme and unusual mem values
|
||||
for (int i = 0; i < NumDevs; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipMemGetInfo(&freeDevMem, &totalDevMem));
|
||||
err = hipMallocManaged(&Hmm, 1, hipMemAttachGlobal);
|
||||
if (hipSuccess == err) {
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
} else {
|
||||
WARN("Observed error while allocating memory on GPU: " << i);
|
||||
WARN(" size 1 with");
|
||||
WARN(" hipMallocManaged() api with flag 'hipMemAttachGlobal'\n");
|
||||
WARN("Error: " << hipGetErrorString(err));
|
||||
IfTestPassed = false;
|
||||
}
|
||||
err = hipMallocManaged(&Hmm, freeDevMem, hipMemAttachGlobal);
|
||||
if (hipSuccess == err) {
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
} else {
|
||||
WARN("Observed error while allocating max free memory on GPU: " << i);
|
||||
WARN(" with hipMallocManaged() api with flag 'hipMemAttachGlobal'\n");
|
||||
WARN("Error: " << hipGetErrorString(err));
|
||||
IfTestPassed = false;
|
||||
}
|
||||
err = hipMallocManaged(&Hmm, (freeDevMem - 1), hipMemAttachGlobal);
|
||||
if (hipSuccess == err) {
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
} else {
|
||||
WARN("Observed error while allocating max (free - 1) memory on ");
|
||||
WARN("GPU: " << i);
|
||||
WARN(" using hipMallocManaged() api with flag 'hipMemAttachGlobal'\n");
|
||||
WARN("Error: " << hipGetErrorString(err));
|
||||
IfTestPassed = false;
|
||||
}
|
||||
err = hipMallocManaged(&Hmm, 1, hipMemAttachHost);
|
||||
if (hipSuccess == err) {
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
} else {
|
||||
WARN("Observed error while allocating memory size 1 on GPU: " << i);
|
||||
WARN(" with hipMallocManaged() api with flag 'hipMemAttachHost'\n");
|
||||
WARN("Error: " << hipGetErrorString(err));
|
||||
IfTestPassed = false;
|
||||
}
|
||||
err = hipMallocManaged(&Hmm, freeDevMem, hipMemAttachHost);
|
||||
if (hipSuccess == err) {
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
} else {
|
||||
WARN("Observed error while allocating max free memory on GPU: " << i);
|
||||
WARN(" with hipMallocManaged() api with flag 'hipMemAttachHost'\n");
|
||||
WARN("Error: " << hipGetErrorString(err));
|
||||
IfTestPassed = false;
|
||||
}
|
||||
err = hipMallocManaged(&Hmm, (freeDevMem - 1), hipMemAttachHost);
|
||||
if (hipSuccess == err) {
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
} else {
|
||||
WARN("Observed error while allocating max (freeDevMem - 1) memory"
|
||||
" on GPU: " << i);
|
||||
WARN(" with hipMallocManaged() api with flag 'hipMemAttachHost'\n");
|
||||
WARN("Error: " << hipGetErrorString(err));
|
||||
IfTestPassed = false;
|
||||
}
|
||||
}
|
||||
REQUIRE(IfTestPassed);
|
||||
} else {
|
||||
SUCCEED("Gpu doesnt support HMM! Hence skipping the test with PASS result");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
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 Case Description:
|
||||
The following test allocates a managed memory and prefetch it in
|
||||
one-to-all and all-to-one fashion followed by kernel launch within available
|
||||
devices*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
// Kernel function
|
||||
__global__ void MemPrftchAsyncKernel1(int* Hmm, 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) {
|
||||
Hmm[i] = Hmm[i] * Hmm[i];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void ReleaseResource(int *Hmm, hipStream_t *strm) {
|
||||
HIP_CHECK(hipFree(Hmm));
|
||||
HIP_CHECK(hipStreamDestroy(*strm));
|
||||
}
|
||||
|
||||
|
||||
/* The following test allocates a managed memory and prefetch it in
|
||||
one-to-all and all-to-one fahsion followed by kernel launch within available
|
||||
devices*/
|
||||
TEST_CASE("Unit_hipMemPrefetchAsyncOneToAll") {
|
||||
int MangdMem = HmmAttrPrint();
|
||||
if (MangdMem == 1) {
|
||||
int *Hmm1 = nullptr, NumDevs, MemSz = (4096 * 4);
|
||||
int InitVal = 123, NumElms = MemSz/4;
|
||||
bool IfTestPassed = true;
|
||||
HIP_CHECK(hipGetDeviceCount(&NumDevs));
|
||||
HIP_CHECK(hipMallocManaged(&Hmm1, MemSz));
|
||||
for (int i = 0; i < NumElms; ++i) {
|
||||
Hmm1[i] = InitVal;
|
||||
}
|
||||
hipStream_t strm;
|
||||
for (int i = -1; i < NumDevs; ++i) {
|
||||
HIP_CHECK(hipMemPrefetchAsync(Hmm1, MemSz, i, 0));
|
||||
for (int j = -1; j < NumDevs; ++j) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
if (j != -1) {
|
||||
HIP_CHECK(hipSetDevice(j));
|
||||
}
|
||||
HIP_CHECK(hipStreamCreate(&strm));
|
||||
// Prefetching memory from i to j
|
||||
HIP_CHECK(hipMemPrefetchAsync(Hmm1, MemSz, j, strm));
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
MemPrftchAsyncKernel1<<<(NumElms/32), 32, 0, strm>>>(Hmm1, NumElms);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
// Verifying the result
|
||||
for (int m = 0; m < NumElms; ++m) {
|
||||
if (Hmm1[m] != (InitVal * InitVal)) {
|
||||
IfTestPassed = false;
|
||||
}
|
||||
}
|
||||
if (!IfTestPassed) {
|
||||
ReleaseResource(Hmm1, &strm);
|
||||
INFO("Did not find expected value!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
// Prefetching memory from j to i
|
||||
HIP_CHECK(hipMemPrefetchAsync(Hmm1, MemSz, i, strm));
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
MemPrftchAsyncKernel1<<<(NumElms/32), 32, 0, strm>>>(Hmm1, NumElms);
|
||||
HIP_CHECK(hipStreamSynchronize(strm));
|
||||
// Verifying the result
|
||||
for (int m = 0; m < NumElms; ++m) {
|
||||
if (Hmm1[m] != (InitVal * InitVal)) {
|
||||
IfTestPassed = false;
|
||||
}
|
||||
}
|
||||
if (!IfTestPassed) {
|
||||
ReleaseResource(Hmm1, &strm);
|
||||
INFO("Did not find expected value!");
|
||||
REQUIRE(false);
|
||||
}
|
||||
|
||||
HIP_CHECK(hipStreamDestroy(strm));
|
||||
}
|
||||
}
|
||||
// Releasing the resources in case all the scenarios passed
|
||||
HIP_CHECK(hipFree(Hmm1));
|
||||
} else {
|
||||
SUCCEED("GPU 0 doesn't support hipDeviceAttributeManagedMemory "
|
||||
"attribute. Hence skipping the testing with Pass result.\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_kernels.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
/*
|
||||
This testfile verifies the following scenarios of all hipMemcpy API
|
||||
1. Multi thread
|
||||
2. Multi size
|
||||
*/
|
||||
|
||||
static auto Available_Gpus{0};
|
||||
static constexpr auto MAX_GPU{256};
|
||||
|
||||
enum apiToTest {TEST_MEMCPY, TEST_MEMCPYH2D, TEST_MEMCPYD2H, TEST_MEMCPYD2D,
|
||||
TEST_MEMCPYASYNC, TEST_MEMCPYH2DASYNC, TEST_MEMCPYD2HASYNC,
|
||||
TEST_MEMCPYD2DASYNC};
|
||||
|
||||
template<typename TestType>
|
||||
void Memcpy_And_verify(int NUM_ELM) {
|
||||
TestType *A_h, *B_h;
|
||||
for (apiToTest api = TEST_MEMCPY; api <= TEST_MEMCPYD2DASYNC;
|
||||
api = apiToTest(api + 1)) {
|
||||
HipTest::initArrays<TestType>(nullptr, nullptr, nullptr,
|
||||
&A_h, &B_h, nullptr,
|
||||
NUM_ELM);
|
||||
HIP_CHECK(hipGetDeviceCount(&Available_Gpus));
|
||||
TestType *A_d[MAX_GPU];
|
||||
hipStream_t stream[MAX_GPU];
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipMalloc(&A_d[i], NUM_ELM * sizeof(TestType)));
|
||||
if (api >= TEST_MEMCPYD2D) {
|
||||
HIP_CHECK(hipStreamCreate(&stream[i]));
|
||||
}
|
||||
}
|
||||
HIP_CHECK(hipSetDevice(0));
|
||||
int canAccessPeer = 0;
|
||||
switch (api) {
|
||||
case TEST_MEMCPY:
|
||||
{
|
||||
// To test hipMemcpy()
|
||||
// Copying data from host to individual devices followed by copying
|
||||
// back to host and verifying the data consistency.
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipMemcpy(A_d[i], A_h, NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyHostToDevice));
|
||||
HIP_CHECK(hipMemcpy(B_h, A_d[i], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDeviceToHost));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
// Device to Device copying for all combinations
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
for (int j = i+1; j < Available_Gpus; ++j) {
|
||||
canAccessPeer = 0;
|
||||
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, i, j));
|
||||
if (canAccessPeer) {
|
||||
HIP_CHECK(hipMemcpy(A_d[j], A_d[i], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDefault));
|
||||
// Copying in reverse dir of above to check if bidirectional
|
||||
// access is happening without any error
|
||||
HIP_CHECK(hipMemcpy(A_d[i], A_d[j], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDefault));
|
||||
// Copying data to host to verify the content
|
||||
HIP_CHECK(hipMemcpy(B_h, A_d[j], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDefault));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_MEMCPYH2D: // To test hipMemcpyHtoD()
|
||||
{
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipMemcpyHtoD(hipDeviceptr_t(A_d[i]),
|
||||
A_h, NUM_ELM * sizeof(TestType)));
|
||||
// Copying data from device to host to check data consistency
|
||||
HIP_CHECK(hipMemcpy(B_h, A_d[i], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDeviceToHost));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_MEMCPYD2H: // To test hipMemcpyDtoH()--done
|
||||
{
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipMemcpy(A_d[i], A_h, NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyHostToDevice));
|
||||
HIP_CHECK(hipMemcpyDtoH(B_h, hipDeviceptr_t(A_d[i]),
|
||||
NUM_ELM * sizeof(TestType)));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_MEMCPYD2D: // To test hipMemcpyDtoD()
|
||||
{
|
||||
if (Available_Gpus > 1) {
|
||||
// First copy data from H to D and then
|
||||
// from D to D followed by D to H
|
||||
// HIP_CHECK(hipMemcpyHtoD(A_d[0], A_h,
|
||||
// NUM_ELM * sizeof(TestType)));
|
||||
int canAccessPeer = 0;
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
for (int j = i+1; j < Available_Gpus; ++j) {
|
||||
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, i, j));
|
||||
if (canAccessPeer) {
|
||||
HIP_CHECK(hipMemcpyHtoD(hipDeviceptr_t(A_d[i]),
|
||||
A_h, NUM_ELM * sizeof(TestType)));
|
||||
HIP_CHECK(hipMemcpyDtoD(hipDeviceptr_t(A_d[j]),
|
||||
hipDeviceptr_t(A_d[i]), NUM_ELM * sizeof(TestType)));
|
||||
// Copying in direction reverse of above to check if
|
||||
// bidirectional
|
||||
// access is happening without any error
|
||||
HIP_CHECK(hipMemcpyDtoD(hipDeviceptr_t(A_d[i]),
|
||||
hipDeviceptr_t(A_d[j]), NUM_ELM * sizeof(TestType)));
|
||||
HIP_CHECK(hipMemcpy(B_h, A_d[i], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDeviceToHost));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// As DtoD is not possible transfer data from HtH(A_h to B_h)
|
||||
// so as to get through verification step
|
||||
HIP_CHECK(hipMemcpy(B_h, A_h, NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyHostToHost));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_MEMCPYASYNC:
|
||||
{
|
||||
// To test hipMemcpyAsync()
|
||||
// Copying data from host to individual devices followed by copying
|
||||
// back to host and verifying the data consistency.
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipMemcpyAsync(A_d[i], A_h, NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyHostToDevice, stream[i]));
|
||||
HIP_CHECK(hipMemcpyAsync(B_h, A_d[i], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDeviceToHost, stream[i]));
|
||||
HIP_CHECK(hipStreamSynchronize(stream[i]));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
// Device to Device copying for all combinations
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
for (int j = i+1; j < Available_Gpus; ++j) {
|
||||
canAccessPeer = 0;
|
||||
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, i, j));
|
||||
if (canAccessPeer) {
|
||||
HIP_CHECK(hipMemcpyAsync(A_d[j], A_d[i],
|
||||
NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDefault, stream[i]));
|
||||
// Copying in direction reverse of above to
|
||||
// check if bidirectional
|
||||
// access is happening without any error
|
||||
HIP_CHECK(hipMemcpyAsync(A_d[i], A_d[j],
|
||||
NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDefault, stream[i]));
|
||||
HIP_CHECK(hipStreamSynchronize(stream[i]));
|
||||
HIP_CHECK(hipMemcpy(B_h, A_d[j], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDefault));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_MEMCPYH2DASYNC: // To test hipMemcpyHtoDAsync()
|
||||
{
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipMemcpyHtoDAsync(hipDeviceptr_t(A_d[i]), A_h,
|
||||
NUM_ELM * sizeof(TestType), stream[i]));
|
||||
HIP_CHECK(hipStreamSynchronize(stream[i]));
|
||||
// Copying data from device to host to check data consistency
|
||||
HIP_CHECK(hipMemcpy(B_h, A_d[i], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDeviceToHost));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_MEMCPYD2HASYNC: // To test hipMemcpyDtoHAsync()
|
||||
{
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipMemcpy(A_d[i], A_h, NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyHostToDevice));
|
||||
HIP_CHECK(hipMemcpyDtoHAsync(B_h, hipDeviceptr_t(A_d[i]),
|
||||
NUM_ELM * sizeof(TestType), stream[i]));
|
||||
HIP_CHECK(hipStreamSynchronize(stream[i]));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEST_MEMCPYD2DASYNC: // To test hipMemcpyDtoDAsync()
|
||||
{
|
||||
if (Available_Gpus > 1) {
|
||||
// First copy data from H to D and then from D to D followed by D2H
|
||||
HIP_CHECK(hipMemcpyHtoD(hipDeviceptr_t(A_d[0]),
|
||||
A_h, NUM_ELM * sizeof(TestType)));
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
for (int j = i+1; j < Available_Gpus; ++j) {
|
||||
canAccessPeer = 0;
|
||||
HIP_CHECK(hipDeviceCanAccessPeer(&canAccessPeer, i, j));
|
||||
if (canAccessPeer) {
|
||||
HIP_CHECK(hipSetDevice(j));
|
||||
HIP_CHECK(hipMemcpyDtoDAsync(hipDeviceptr_t(A_d[j]),
|
||||
hipDeviceptr_t(A_d[i]), NUM_ELM * sizeof(TestType),
|
||||
stream[i]));
|
||||
// Copying in direction reverse of above to check if
|
||||
// bidirectional
|
||||
// access is happening without any error
|
||||
HIP_CHECK(hipMemcpyDtoDAsync(hipDeviceptr_t(A_d[i]),
|
||||
hipDeviceptr_t(A_d[j]), NUM_ELM * sizeof(TestType),
|
||||
stream[i]));
|
||||
HIP_CHECK(hipStreamSynchronize(stream[i]));
|
||||
HIP_CHECK(hipMemcpy(B_h, A_d[i], NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyDeviceToHost));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// As DtoD is not possible we will transfer data
|
||||
// from HtH(A_h to B_h)
|
||||
// so as to get through verification step
|
||||
HIP_CHECK(hipMemcpy(B_h, A_h, NUM_ELM * sizeof(TestType),
|
||||
hipMemcpyHostToHost));
|
||||
HipTest::checkTest(A_h, B_h, NUM_ELM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < Available_Gpus; ++i) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
HIP_CHECK(hipFree((A_d[i])));
|
||||
if (api >= TEST_MEMCPYD2D) {
|
||||
HIP_CHECK(hipStreamDestroy(stream[i]));
|
||||
}
|
||||
}
|
||||
HipTest::freeArrays<TestType>(nullptr, nullptr, nullptr,
|
||||
A_h, B_h, nullptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
TEMPLATE_TEST_CASE("Stress_hipMemcpy_multiDevice-AllAPIs", "",
|
||||
char, int, size_t, long double) {
|
||||
auto diff_size = GENERATE(1, 5, 10, 100, 1024, 10*1024, 100*1024,
|
||||
1024*1024, 10*1024*1024, 100*1024*1024,
|
||||
1024*1024*1024);
|
||||
size_t free = 0, total = 0;
|
||||
HIP_CHECK(hipMemGetInfo(&free, &total));
|
||||
if ((diff_size * sizeof(TestType)) <= free) {
|
||||
Memcpy_And_verify<TestType>(diff_size);
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
TEST_CASE("Stress_hipMalloc", "DifferentSizes") {
|
||||
int* d_a = nullptr;
|
||||
SECTION("Size 10") {
|
||||
auto res = hipMalloc(&d_a, sizeof(10));
|
||||
REQUIRE(res == hipSuccess);
|
||||
HIP_CHECK(hipFree(d_a));
|
||||
d_a = nullptr;
|
||||
}
|
||||
SECTION("Size 100") {
|
||||
auto res = hipMalloc(&d_a, sizeof(100));
|
||||
REQUIRE(res == hipSuccess);
|
||||
HIP_CHECK(hipFree(d_a));
|
||||
d_a = nullptr;
|
||||
}
|
||||
SECTION("Size 1000") {
|
||||
auto res = hipMalloc(&d_a, sizeof(1000));
|
||||
REQUIRE(res == hipSuccess);
|
||||
HIP_CHECK(hipFree(d_a));
|
||||
d_a = nullptr;
|
||||
}
|
||||
SECTION("Size 10000") {
|
||||
auto res = hipMalloc(&d_a, sizeof(10000));
|
||||
REQUIRE(res == hipSuccess);
|
||||
HIP_CHECK(hipFree(d_a));
|
||||
d_a = nullptr;
|
||||
}
|
||||
SECTION("Size MAX") {
|
||||
auto res = hipMalloc(&d_a, ~(size_t)0);
|
||||
REQUIRE(res == hipErrorOutOfMemory);
|
||||
d_a = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
Stress_printf_ComplexKernels.cc
|
||||
Stress_printf_SimpleKernels.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME printf
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME stress_test)
|
||||
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
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 <hip/hip_runtime.h>
|
||||
#ifdef __linux__
|
||||
#include "printf_common.h"
|
||||
#endif
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
#define MAX_BLOCK_SIZE 523
|
||||
#define MAX_GRID_SIZE 503
|
||||
#define CHUNK_SIZE 1024
|
||||
#define NUM_STREAM 4
|
||||
#define CONST_WEIGHTING_FACT1 7
|
||||
#define CONST_WEIGHTING_FACT2 5
|
||||
|
||||
namespace hipPrintfStressTest {
|
||||
struct printInfo {
|
||||
uint32_t printSizeinBytes, lineCount;
|
||||
};
|
||||
|
||||
__device__ __host__ struct printInfo startPrint(uint32_t tid,
|
||||
uint32_t iterCount, uint32_t *a, uint32_t *b) {
|
||||
uint32_t printSize = 0;
|
||||
uint32_t lineCount = 0;
|
||||
// The 2nd modulus operand is arbitrarily chosen as 7 below to
|
||||
// diversify the printf output as much as possible while also being
|
||||
// a prime number. This number is fixed to 7 and should not be changed.
|
||||
uint32_t mod = tid % 7;
|
||||
// Perform some calculations and print the values.
|
||||
uint32_t uiresult;
|
||||
int32_t iresult;
|
||||
float fresult;
|
||||
for (uint32_t count = 0; count < iterCount; count++) {
|
||||
if (0 == mod) {
|
||||
// Perform Vector Multiplication a(i)*b(i)
|
||||
// Print both tid and result
|
||||
uiresult = a[tid]*b[tid];
|
||||
printSize +=
|
||||
printf("tid %u: Value of result=%u or %x\n",
|
||||
tid, uiresult, uiresult);
|
||||
lineCount++;
|
||||
} else if (1 == mod) {
|
||||
// Perform Array Addition a(i) + b(i)
|
||||
// Print both tid and result
|
||||
uiresult = a[tid] + b[tid];
|
||||
printSize +=
|
||||
printf("tid %u: Value of result=%u or %x \n",
|
||||
tid, uiresult, uiresult);
|
||||
lineCount++;
|
||||
} else if (2 == mod) {
|
||||
// Perform Array Subtraction a(i) - b(i)
|
||||
// Print both tid and result (as both int, uint)
|
||||
iresult = a[tid] - b[tid];
|
||||
printSize +=
|
||||
printf("tid %u: Value of result=%d or %x\n",
|
||||
tid, iresult, iresult);
|
||||
lineCount++;
|
||||
} else if (3 == mod) {
|
||||
// Perform Sum of Squares a(i)*a(i) + b(i)*b(i)
|
||||
// Print both tid and result
|
||||
uiresult = a[tid]*a[tid] + b[tid]*b[tid];
|
||||
printSize +=
|
||||
printf("tid %u: Value of result=%u or %x\n",
|
||||
tid, uiresult, uiresult);
|
||||
lineCount++;
|
||||
} else if (4 == mod) {
|
||||
// Perform (a(i)*a(i) + b(i)*b(i))/a(i)*b(i)
|
||||
// Print both tid and result (in float upto 2 decimal precision)
|
||||
fresult = (a[tid]*a[tid] + b[tid]*b[tid])/(a[tid]*b[tid]);
|
||||
printSize +=
|
||||
printf("tid %u: Value of result[%d] = %.2f or %.2e\n",
|
||||
tid, tid, fresult, fresult);
|
||||
lineCount++;
|
||||
} else if (5 == mod) {
|
||||
// Perform (a(i)*a(i) - b(i)*b(i))/a(i)*b(i)
|
||||
// Print both tid and result (in float upto 4 decimal precision)
|
||||
fresult = (a[tid]*a[tid] - b[tid]*b[tid])/(a[tid]*b[tid]);
|
||||
printSize +=
|
||||
printf("tid %u: Value of result[%d] = %.4f or %.4e \n",
|
||||
tid, tid, fresult, fresult);
|
||||
lineCount++;
|
||||
} else if (6 == mod) {
|
||||
// Perform (a(i)*a(i) + b(i)*b(i))/(a(i)*a(i) - b(i)*b(i))
|
||||
// Print both tid and result (in float upto 6 decimal precision)
|
||||
fresult = (a[tid]*a[tid] + b[tid]*b[tid])/
|
||||
(a[tid]*a[tid] - b[tid]*b[tid]);
|
||||
printSize +=
|
||||
printf("tid %u: Value of result[%d] = %.6f or %.6e \n",
|
||||
tid, tid, fresult, fresult);
|
||||
lineCount++;
|
||||
}
|
||||
// Print a random character string of variable size
|
||||
// and number.
|
||||
const char* msg;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
int imod = (i % 6);
|
||||
if (0 == imod) {
|
||||
msg = "jhwehde2hl";
|
||||
} else if (1 == imod) {
|
||||
msg = "jhwehde2hlmc,prmlsl4";
|
||||
} else if (2 == imod) {
|
||||
msg = "xkdojdewnd34dMMnl2o4AAdeBEjbX0";
|
||||
} else if (3 == imod) {
|
||||
msg = "mcropkaA234dmelmfhja44ndalomkfokdMDFK328";
|
||||
} else if (4 == imod) {
|
||||
msg =
|
||||
"udnekc8939MDkdnjj3knsdlmnekdlgJNls328419i905409dfm";
|
||||
} else if (5 == imod) {
|
||||
msg =
|
||||
"lfjweknm4349u34sdlk09j3mAADDSDkeffe575675fdvfLKMWMORMFREKLkl";
|
||||
}
|
||||
printSize += printf("tid %u: %s imod = %d \n", tid, msg, imod);
|
||||
lineCount++;
|
||||
}
|
||||
// Print a long string with data
|
||||
msg =
|
||||
"jheku83290dnmnd##9u9BJKHFJLKsMMMMdkejwejjj232indnfdmsnndnsdn****bsXxZz";
|
||||
float pi = 3.141592;
|
||||
uint32_t unum = 123456789;
|
||||
int32_t inum = -123456789;
|
||||
printSize +=
|
||||
printf("%s,%d,%s,%u,%s,%x,%s,%f,%s,%e\n",
|
||||
msg, inum, msg, unum, msg, unum, msg, pi, msg, pi);
|
||||
lineCount++;
|
||||
// Print different data types using different specifiers
|
||||
float fmaxvalue = std::numeric_limits<float>::max();
|
||||
float fminvalue = std::numeric_limits<float>::min();
|
||||
double dmaxvalue = std::numeric_limits<double>::max();
|
||||
double dminvalue = std::numeric_limits<double>::min();
|
||||
printSize +=
|
||||
printf("%f, %f, %e, %e \n", fmaxvalue, fminvalue, fmaxvalue, fminvalue);
|
||||
printSize +=
|
||||
printf("%f, %f, %e, %e \n", dmaxvalue, dminvalue, dmaxvalue, dminvalue);
|
||||
printSize +=
|
||||
printf("%a, %a, %A, %A \n", fmaxvalue, fminvalue, fmaxvalue, fminvalue);
|
||||
printSize +=
|
||||
printf("%a, %a, %A, %A \n", dmaxvalue, dminvalue, dmaxvalue, dminvalue);
|
||||
lineCount+=4;
|
||||
size_t size_tmaxvalue = std::numeric_limits<size_t>::max();
|
||||
size_t size_tminvalue = std::numeric_limits<size_t>::min();
|
||||
long long llmaxvalue = std::numeric_limits<long long>::max();
|
||||
long long llminvalue = std::numeric_limits<long long>::min();
|
||||
unsigned long long ullmaxvalue =
|
||||
std::numeric_limits<unsigned long long>::max();
|
||||
unsigned long long ullminvalue =
|
||||
std::numeric_limits<unsigned long long>::min();
|
||||
long lmaxvalue = std::numeric_limits<long>::max();
|
||||
long lminvalue = std::numeric_limits<long>::min();
|
||||
unsigned long ulmaxvalue = std::numeric_limits<unsigned long>::max();
|
||||
unsigned long ulminvalue = std::numeric_limits<unsigned long>::min();
|
||||
short smaxvalue = std::numeric_limits<short>::max();
|
||||
short sminvalue = std::numeric_limits<short>::min();
|
||||
unsigned short usmaxvalue = std::numeric_limits<unsigned short>::max();
|
||||
unsigned short usminvalue = std::numeric_limits<unsigned short>::min();
|
||||
char cmaxvalue = std::numeric_limits<char>::max();
|
||||
char cminvalue = std::numeric_limits<char>::min();
|
||||
unsigned char ucmaxvalue = std::numeric_limits<unsigned char>::max();
|
||||
unsigned char ucminvalue = std::numeric_limits<unsigned char>::min();
|
||||
int32_t imaxvalue = std::numeric_limits<int32_t>::max();
|
||||
int32_t iminvalue = std::numeric_limits<int32_t>::min();
|
||||
uint32_t uimaxvalue = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t uiminvalue = std::numeric_limits<uint32_t>::min();
|
||||
printSize +=
|
||||
printf("%zu, %zu, %lli, %lli, %llu, %llu, %li, %li, %lu, %lu\n",
|
||||
size_tmaxvalue, size_tminvalue, llmaxvalue, llminvalue,
|
||||
ullmaxvalue, ullminvalue, lmaxvalue, lminvalue,
|
||||
ulmaxvalue, ulminvalue);
|
||||
printSize +=
|
||||
printf("%zx, %zx, %llx, %llx, %llx, %llx, %lx, %lx, %lx, %lx\n",
|
||||
size_tmaxvalue, size_tminvalue, llmaxvalue, llminvalue,
|
||||
ullmaxvalue, ullminvalue, lmaxvalue, lminvalue,
|
||||
ulmaxvalue, ulminvalue);
|
||||
printSize +=
|
||||
printf("%zX, %zX, %llX, %llX, %llX, %llX, %lX, %lX, %lX, %lX\n",
|
||||
size_tmaxvalue, size_tminvalue, llmaxvalue, llminvalue,
|
||||
ullmaxvalue, ullminvalue, lmaxvalue, lminvalue,
|
||||
ulmaxvalue, ulminvalue);
|
||||
printSize +=
|
||||
printf("%zo, %zo, %llo, %llo, %llo, %llo, %lo, %lo, %lo, %lo\n",
|
||||
size_tmaxvalue, size_tminvalue, llmaxvalue, llminvalue,
|
||||
ullmaxvalue, ullminvalue, lmaxvalue, lminvalue,
|
||||
ulmaxvalue, ulminvalue);
|
||||
printSize +=
|
||||
printf("%hd, %hd, %hu, %hu, %hhd, %hhd, %hhu, %hhu, %d, %d, %u, %u\n",
|
||||
smaxvalue, sminvalue, usmaxvalue, usminvalue,
|
||||
cmaxvalue, cminvalue, ucmaxvalue, ucminvalue,
|
||||
imaxvalue, iminvalue, uimaxvalue, uiminvalue);
|
||||
printSize +=
|
||||
printf("%hx, %hx, %hx, %hx, %hhx, %hhx, %hhx, %hhx, %x, %x, %x, %x\n",
|
||||
smaxvalue, sminvalue, usmaxvalue, usminvalue,
|
||||
cmaxvalue, cminvalue, ucmaxvalue, ucminvalue,
|
||||
imaxvalue, iminvalue, uimaxvalue, uiminvalue);
|
||||
printSize +=
|
||||
printf("%hX, %hX, %hX, %hX, %hhX, %hhX, %hhX, %hhX, %X, %X, %X, %X\n",
|
||||
smaxvalue, sminvalue, usmaxvalue, usminvalue,
|
||||
cmaxvalue, cminvalue, ucmaxvalue, ucminvalue,
|
||||
imaxvalue, iminvalue, uimaxvalue, uiminvalue);
|
||||
printSize +=
|
||||
printf("%ho, %ho, %ho, %ho, %hho, %hho, %hho, %hho, %o, %o, %o, %o\n",
|
||||
smaxvalue, sminvalue, usmaxvalue, usminvalue,
|
||||
cmaxvalue, cminvalue, ucmaxvalue, ucminvalue,
|
||||
imaxvalue, iminvalue, uimaxvalue, uiminvalue);
|
||||
printSize +=
|
||||
printf("%c, %c, %c, %c\n", cmaxvalue, cminvalue, ucmaxvalue, ucminvalue);
|
||||
lineCount+=9;
|
||||
}
|
||||
struct printInfo pInfo = {printSize, lineCount};
|
||||
return pInfo;
|
||||
}
|
||||
// This kernel is launched only in X dimension
|
||||
__global__ void kernel_complex_opX(uint32_t *a, uint32_t *b,
|
||||
uint32_t iterCount) {
|
||||
uint32_t tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
startPrint(tid, iterCount, a, b);
|
||||
}
|
||||
// This kernel is launched only in Y dimension
|
||||
__global__ void kernel_complex_opY(uint32_t *a, uint32_t *b,
|
||||
uint32_t iterCount) {
|
||||
uint32_t tid = threadIdx.y + blockIdx.y * blockDim.y;
|
||||
startPrint(tid, iterCount, a, b);
|
||||
}
|
||||
// This kernel is launched only in Z dimension
|
||||
__global__ void kernel_complex_opZ(uint32_t *a, uint32_t *b,
|
||||
uint32_t iterCount) {
|
||||
uint32_t tid = threadIdx.z + blockIdx.z * blockDim.z;
|
||||
startPrint(tid, iterCount, a, b);
|
||||
}
|
||||
#ifdef __linux__
|
||||
// Performs printf stress test on a single GPU using multiple streams.
|
||||
bool test_printf_multistream(uint32_t num_blocks,
|
||||
uint32_t threads_per_block,
|
||||
uint32_t iterCount) {
|
||||
uint32_t buffsize = num_blocks*threads_per_block;
|
||||
size_t actualFileSize = 0;
|
||||
uint32_t totalActualLinecount = 0;
|
||||
uint32_t *Ah, *Bh;
|
||||
uint32_t *Ad, *Bd;
|
||||
Ah = new uint32_t[buffsize];
|
||||
Bh = new uint32_t[buffsize];
|
||||
for (uint32_t i = 0; i < buffsize; i++) {
|
||||
Ah[i] = i + 1;
|
||||
Bh[i] = buffsize - i;
|
||||
}
|
||||
HIP_CHECK(hipMalloc(&Ad, buffsize*sizeof(uint32_t)));
|
||||
HIP_CHECK(hipMalloc(&Bd, buffsize*sizeof(uint32_t)));
|
||||
HIP_CHECK(hipMemcpy(Ad, Ah, buffsize*sizeof(uint32_t),
|
||||
hipMemcpyHostToDevice));
|
||||
HIP_CHECK(hipMemcpy(Bd, Bh, buffsize*sizeof(uint32_t),
|
||||
hipMemcpyHostToDevice));
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipStream_t stream[NUM_STREAM];
|
||||
for (int i = 0; i < NUM_STREAM; i++) {
|
||||
HIP_CHECK(hipStreamCreate(&stream[i]));
|
||||
hipLaunchKernelGGL(kernel_complex_opX, dim3(num_blocks, 1, 1),
|
||||
dim3(threads_per_block, 1, 1),
|
||||
0, stream[i], Ad, Bd, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipLaunchKernelGGL(kernel_complex_opY, dim3(1, num_blocks, 1),
|
||||
dim3(1, threads_per_block, 1),
|
||||
0, stream[i], Ad, Bd, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipLaunchKernelGGL(kernel_complex_opZ, dim3(1, 1, num_blocks),
|
||||
dim3(1, 1, threads_per_block),
|
||||
0, stream[i], Ad, Bd, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
for (int i = 0; i < NUM_STREAM; i++) {
|
||||
HIP_CHECK(hipStreamDestroy(stream[i]));
|
||||
}
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
totalActualLinecount++;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
struct printInfo pInfo;
|
||||
size_t estimatedPrintSize = 0;
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
uint32_t lop = 0;
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
for (int j = 0; j < NUM_STREAM; j++) {
|
||||
for (uint32_t tid = 0; tid < (buffsize); tid++) {
|
||||
pInfo = startPrint(tid, iterCount, Ah, Bh);
|
||||
lop += pInfo.lineCount;
|
||||
estimatedPrintSize += pInfo.printSizeinBytes;
|
||||
}
|
||||
for (uint32_t tid = 0; tid < (buffsize); tid++) {
|
||||
pInfo = startPrint(tid, iterCount, Ah, Bh);
|
||||
lop += pInfo.lineCount;
|
||||
estimatedPrintSize += pInfo.printSizeinBytes;
|
||||
}
|
||||
for (uint32_t tid = 0; tid < (buffsize); tid++) {
|
||||
pInfo = startPrint(tid, iterCount, Ah, Bh);
|
||||
lop += pInfo.lineCount;
|
||||
estimatedPrintSize += pInfo.printSizeinBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("estimatedPrintSize = %zu, actualFileSize = %zu\n",
|
||||
estimatedPrintSize, actualFileSize);
|
||||
printf("estimatedLinesPrinted = %u, actualLinesPrinted = %u\n",
|
||||
lop, totalActualLinecount-1);
|
||||
HIP_CHECK(hipFree(Bd));
|
||||
HIP_CHECK(hipFree(Ad));
|
||||
delete[] Bh;
|
||||
delete[] Ah;
|
||||
if ((estimatedPrintSize != actualFileSize)||
|
||||
(lop != (totalActualLinecount-1))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_printf_multigpu(int gpu,
|
||||
uint32_t num_blocks,
|
||||
uint32_t threads_per_block,
|
||||
uint32_t iterCount,
|
||||
size_t *actualFileSize,
|
||||
uint32_t *totalActualLinecount) {
|
||||
uint32_t buffsize = num_blocks*threads_per_block;
|
||||
uint32_t *Ah, *Bh;
|
||||
uint32_t *Ad, *Bd;
|
||||
HIP_CHECK(hipSetDevice(gpu));
|
||||
Ah = new uint32_t[buffsize];
|
||||
Bh = new uint32_t[buffsize];
|
||||
for (uint32_t i = 0; i < buffsize; i++) {
|
||||
Ah[i] = i + 1;
|
||||
Bh[i] = buffsize - i;
|
||||
}
|
||||
HIP_CHECK(hipMalloc(&Ad, buffsize*sizeof(uint32_t)));
|
||||
HIP_CHECK(hipMalloc(&Bd, buffsize*sizeof(uint32_t)));
|
||||
HIP_CHECK(hipMemcpy(Ad, Ah, buffsize*sizeof(uint32_t),
|
||||
hipMemcpyHostToDevice));
|
||||
HIP_CHECK(hipMemcpy(Bd, Bh, buffsize*sizeof(uint32_t),
|
||||
hipMemcpyHostToDevice));
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipLaunchKernelGGL(kernel_complex_opX, dim3(num_blocks, 1, 1),
|
||||
dim3(threads_per_block, 1, 1),
|
||||
0, 0, Ad, Bd, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipLaunchKernelGGL(kernel_complex_opY, dim3(1, num_blocks, 1),
|
||||
dim3(1, threads_per_block, 1),
|
||||
0, 0, Ad, Bd, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipLaunchKernelGGL(kernel_complex_opZ, dim3(1, 1, num_blocks),
|
||||
dim3(1, 1, threads_per_block),
|
||||
0, 0, Ad, Bd, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
*totalActualLinecount += 1;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
*actualFileSize += st.st_size;
|
||||
}
|
||||
HIP_CHECK(hipFree(Bd));
|
||||
HIP_CHECK(hipFree(Ad));
|
||||
delete[] Bh;
|
||||
delete[] Ah;
|
||||
*totalActualLinecount -= 1; // Removing Empty Line
|
||||
HIP_CHECK(hipSetDevice(0));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Performs printf stress test on all GPUs present in the system.
|
||||
bool testPrintfMultGPU(int numOfGPUs,
|
||||
uint32_t num_blocks,
|
||||
uint32_t threads_per_block,
|
||||
uint32_t iterCount) {
|
||||
uint32_t buffsize = num_blocks*threads_per_block;
|
||||
size_t actualFileSize = 0;
|
||||
uint32_t totalActualLinecount = 0;
|
||||
for (int gpu = 0; gpu < numOfGPUs; gpu++) {
|
||||
test_printf_multigpu(gpu, num_blocks, threads_per_block,
|
||||
iterCount, &actualFileSize, &totalActualLinecount);
|
||||
}
|
||||
struct printInfo pInfo;
|
||||
size_t estimatedPrintSize = 0;
|
||||
uint32_t *Ah, *Bh;
|
||||
Ah = new uint32_t[buffsize];
|
||||
Bh = new uint32_t[buffsize];
|
||||
for (uint32_t i = 0; i < buffsize; i++) {
|
||||
Ah[i] = i + 1;
|
||||
Bh[i] = buffsize - i;
|
||||
}
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
uint32_t lop = 0;
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
for (int gpu = 0; gpu < numOfGPUs; gpu++) {
|
||||
for (uint32_t tid = 0; tid < (buffsize); tid++) {
|
||||
pInfo = startPrint(tid, iterCount, Ah, Bh);
|
||||
lop += pInfo.lineCount;
|
||||
estimatedPrintSize += pInfo.printSizeinBytes;
|
||||
}
|
||||
for (uint32_t tid = 0; tid < (buffsize); tid++) {
|
||||
pInfo = startPrint(tid, iterCount, Ah, Bh);
|
||||
lop += pInfo.lineCount;
|
||||
estimatedPrintSize += pInfo.printSizeinBytes;
|
||||
}
|
||||
for (uint32_t tid = 0; tid < (buffsize); tid++) {
|
||||
pInfo = startPrint(tid, iterCount, Ah, Bh);
|
||||
lop += pInfo.lineCount;
|
||||
estimatedPrintSize += pInfo.printSizeinBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] Bh;
|
||||
delete[] Ah;
|
||||
printf("estimatedPrintSize = %zu, actualFileSize = %zu\n",
|
||||
estimatedPrintSize, actualFileSize);
|
||||
printf("estimatedLinesPrinted = %u, actualLinesPrinted = %u\n",
|
||||
lop, totalActualLinecount);
|
||||
if ((estimatedPrintSize != actualFileSize)||
|
||||
(lop != totalActualLinecount)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
} // namespace hipPrintfStressTest
|
||||
|
||||
TEST_CASE("Stress_printf_ComplexKernelMultStream") {
|
||||
#ifdef __linux__
|
||||
printf("Test - Stress_printf_ComplexKernelMultStream start\n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = MAX_BLOCK_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 4; // = 4 GB
|
||||
uint32_t iterCount = 1;
|
||||
// num_blocks is calculated using an approximate formula to arrive at
|
||||
// the required print data quantity. CONST_WEIGHTING_FACT1 and
|
||||
// CONST_WEIGHTING_FACT2 are empirically determined.
|
||||
uint32_t num_blocks = (MAX_GRID_SIZE*print_limit)/CONST_WEIGHTING_FACT1
|
||||
- (CONST_WEIGHTING_FACT2*print_limit);
|
||||
TestPassed =
|
||||
hipPrintfStressTest::test_printf_multistream(num_blocks, threads_per_block,
|
||||
iterCount);
|
||||
REQUIRE(TestPassed);
|
||||
printf("Test - Stress_printf_ComplexKernelMultStream completed \n");
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_ComplexKernelMultStreamMultGpu") {
|
||||
#ifdef __linux__
|
||||
printf("Test - Stress_printf_ComplexKernelMultStreamMultGpu start \n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = MAX_BLOCK_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 4; // = 4 GB
|
||||
uint32_t iterCount = 1;
|
||||
int numOfGPUs = 0;
|
||||
HIP_CHECK(hipGetDeviceCount(&numOfGPUs));
|
||||
if (numOfGPUs < 2) {
|
||||
printf("Skipping test because numOfGPUs < 2\n");
|
||||
return;
|
||||
}
|
||||
// num_blocks is calculated using an approximate formula to arrive at
|
||||
// the required print data quantity. CONST_WEIGHTING_FACT1 and
|
||||
// CONST_WEIGHTING_FACT2 are empirically determined.
|
||||
uint32_t num_blocks =
|
||||
(((MAX_GRID_SIZE*print_limit)/CONST_WEIGHTING_FACT1 -
|
||||
(CONST_WEIGHTING_FACT2*print_limit))*4)/numOfGPUs;
|
||||
TestPassed =
|
||||
hipPrintfStressTest::testPrintfMultGPU(numOfGPUs, num_blocks,
|
||||
threads_per_block,
|
||||
iterCount);
|
||||
REQUIRE(TestPassed);
|
||||
printf("Test - Stress_printf_ComplexKernelMultStreamMultGpu end \n");
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,795 @@
|
||||
/*
|
||||
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 <hip/hip_runtime.h>
|
||||
#ifdef __linux__
|
||||
#include "printf_common.h"
|
||||
#endif
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
#define BLOCK_SIZE 512
|
||||
#define GRID_SIZE 512
|
||||
#define CHUNK_SIZE 256
|
||||
#define CONST_STR "Hello World from Device.Iam printing 55 bytes of data.\n"
|
||||
#define CONST_STR1 "Hello World from Device.Iam printing from even thread.\n"
|
||||
#define CONST_STR2 "Hello World from Device.This is odd thread.\n"
|
||||
#define CONST_STR3 "Hello World from Device. The sum of all threadID = "
|
||||
|
||||
namespace hipPrintfStressTest {
|
||||
struct SizeStruct {
|
||||
unsigned int block_size;
|
||||
unsigned int grid_size;
|
||||
unsigned int iteration;
|
||||
};
|
||||
// These values are empirically determined for kernel_divergent_str3
|
||||
// Any modification to the function or CONST_STR3 will change these values
|
||||
const struct SizeStruct EmpiricalValues1[12] = {
|
||||
{512, 512, 16},
|
||||
{512, 512, 32},
|
||||
{512, 512, 48},
|
||||
{512, 512, 64},
|
||||
{512, 512, 80},
|
||||
{512, 512, 96},
|
||||
{512, 512, 110},
|
||||
{512, 512, 126},
|
||||
{512, 512, 140},
|
||||
{512, 512, 156},
|
||||
{512, 512, 172},
|
||||
{512, 512, 186}
|
||||
};
|
||||
// These values are empirically determined for kernel_dependent_calc
|
||||
// and kernel_dependent_calc_atomic.
|
||||
// Any modification to the functions will change these values.
|
||||
const struct SizeStruct EmpiricalValues2[12] = {
|
||||
{512, 512, 20},
|
||||
{512, 512, 40},
|
||||
{512, 512, 60},
|
||||
{512, 512, 80},
|
||||
{512, 512, 100},
|
||||
{512, 512, 120},
|
||||
{512, 512, 140},
|
||||
{512, 512, 160},
|
||||
{512, 512, 180},
|
||||
{512, 512, 200},
|
||||
{512, 512, 220},
|
||||
{512, 512, 240}
|
||||
};
|
||||
// Print a constant string in a kernel for 'n' iterations per thread
|
||||
// using 'b' block size and 'g' grid size such that
|
||||
// (total bytes per iteration)*n*b*g ≈ N GB where N is user input.
|
||||
__global__ void kernel_printf_conststr(uint iterCount) {
|
||||
for (uint count = 0; count < iterCount; count++) {
|
||||
printf("%s", CONST_STR);
|
||||
}
|
||||
}
|
||||
// Print 2 different constant strings (using if and else conditionals)
|
||||
// in a kernel for 'n' iterations per thread using 'b' block size and
|
||||
// 'g' grid size such that (total bytes per iteration)*n*b*g ≈ N GB,
|
||||
// where N is user input.
|
||||
__global__ void kernel_printf_two_conditionalstr(uint iterCount) {
|
||||
uint tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
uint mod_tid = (tid % 2);
|
||||
if (0 == mod_tid) {
|
||||
for (uint count = 0; count < iterCount; count++) {
|
||||
printf("%s", CONST_STR1);
|
||||
}
|
||||
} else {
|
||||
for (uint count = 0; count < iterCount; count++) {
|
||||
printf("%s", CONST_STR2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Print a constant string (using only if condition) in a kernel for 'n'
|
||||
// iterations per thread using 'b' block size and 'g' grid size such that
|
||||
// (total bytes per iteration)*n*b*g ≈ N GB, where N is user input.
|
||||
__global__ void kernel_printf_single_conditionalstr(uint iterCount) {
|
||||
uint tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
uint mod_tid = (tid % 2);
|
||||
if (0 == mod_tid) {
|
||||
for (uint count = 0; count < iterCount; count++) {
|
||||
printf("%s", CONST_STR1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Please do not nodify this function.
|
||||
// Any modification to this function will fail the test case.
|
||||
// Print variable size string using integer data in a kernel for 'n'
|
||||
// iterations per thread using 'b' block size and 'g' grid size such
|
||||
// that (total bytes per iteration)*n*b*g ≈ N GB, where N is user input.
|
||||
__global__ void kernel_printf_variablestr(uint iterCount, int *ret) {
|
||||
uint tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
int retlocal = 0;
|
||||
const char *const_str =
|
||||
"Hello World from Device.Iam printing (threadID,number)=";
|
||||
for (int count = 0; count < (const int)iterCount; count++) {
|
||||
retlocal += printf("%s%u,%d\n", const_str, tid, count);
|
||||
retlocal += printf("%s%u,%d\n", const_str, tid, 10*count);
|
||||
retlocal += printf("%s%u,%d\n", const_str, tid, 100*count);
|
||||
retlocal += printf("%s%u,%d\n", const_str, tid, 1000*count);
|
||||
}
|
||||
ret[tid] = retlocal;
|
||||
}
|
||||
// Please do not nodify this function.
|
||||
// Any modification to this function will fail the test case.
|
||||
// Perform dependent calculations and print the result after each
|
||||
// calculation in a kernel for 'n' iterations per thread using 'b' block
|
||||
// size and 'g' grid size such that
|
||||
// (total bytes per iteration)*n*b*g ≈ N GB, where N is user input.
|
||||
__global__ void kernel_dependent_calc(uint32_t iterCount, int *ret) {
|
||||
uint32_t tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
int retlocal = 0;
|
||||
const char *const_str =
|
||||
"Hello World from Device.Iam printing number=";
|
||||
for (int count = 0; count < (const int)iterCount; count++) {
|
||||
uint32_t x = tid + count;
|
||||
retlocal += printf("%s%u\n", const_str, x);
|
||||
uint32_t y = x + tid;
|
||||
retlocal += printf("%s%u\n", const_str, y);
|
||||
uint32_t z = x*y;
|
||||
retlocal += printf("%s%u\n", const_str, z);
|
||||
uint32_t a = z/(tid + 1);
|
||||
retlocal += printf("%s%u\n", const_str, a);
|
||||
}
|
||||
ret[tid] = retlocal;
|
||||
}
|
||||
// Please do not nodify this function.
|
||||
// Any modification to this function will fail the test case.
|
||||
// Perform atomic calculations and print the result after each
|
||||
// calculation in a kernel for 'n' iterations per thread using 'b' block
|
||||
// size and 'g' grid size such that
|
||||
// (total bytes per iteration)*n*b*g ≈ N GB, where N is user input.
|
||||
__global__ void kernel_dependent_calc_atomic(uint32_t iterCount,
|
||||
int *ret) {
|
||||
uint32_t tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
int retlocal = 0;
|
||||
const char *const_str =
|
||||
"Hello World from Device.Iam printing number=";
|
||||
for (uint32_t count = 0; count < iterCount; count++) {
|
||||
uint32_t x = tid;
|
||||
atomicAdd(&x, count);
|
||||
retlocal += printf("%s%u\n", const_str, x);
|
||||
uint32_t y = x;
|
||||
atomicAdd(&y, tid);
|
||||
retlocal += printf("%s%u\n", const_str, y);
|
||||
uint32_t z = y;
|
||||
atomicSub(&z, count);
|
||||
retlocal += printf("%s%u\n", const_str, z);
|
||||
uint32_t a = z;
|
||||
atomicAnd(&a, 0x0000ffff);
|
||||
retlocal += printf("%s%u\n", const_str, a);
|
||||
}
|
||||
ret[tid] = retlocal;
|
||||
}
|
||||
// Print variable size string using floating point data of varying
|
||||
// precision in a kernel for 'n' iterations per thread using 'b' block
|
||||
// size and 'g' grid size such that
|
||||
// (total bytes per iteration)*n*b*g ≈ N GB, where N is user input.
|
||||
__device__ __host__ int printPi(int maxPrecision) {
|
||||
int printSize = 0;
|
||||
size_t expo = 1000000000000;
|
||||
double pi = 3.1415926535;
|
||||
double piScaled = pi*expo;
|
||||
const char *const_str =
|
||||
"Hello World from Device.Iam printing decimal number=";
|
||||
for (int prec = 0; prec <= maxPrecision ; prec++) {
|
||||
printSize += printf("%s%.*f %.*e\n", const_str, prec, pi,
|
||||
prec, piScaled);
|
||||
}
|
||||
return printSize;
|
||||
}
|
||||
|
||||
__global__ void kernel_decimal_calculation(uint iterCount,
|
||||
int maxPrecision) {
|
||||
for (int count = 0; count < (const int)iterCount; count++) {
|
||||
printPi(maxPrecision);
|
||||
}
|
||||
}
|
||||
// Print the value of shared memory variable using a stream of size 'n',
|
||||
// 'b' block size and 'g' grid size such that
|
||||
// (total bytes per thread)*n*b*g ≈ N GB, where N is user input.
|
||||
__global__ void kernel_shared_mem() {
|
||||
__shared__ uint32_t sharedMem;
|
||||
sharedMem = 0;
|
||||
__syncthreads();
|
||||
atomicAdd(&sharedMem, threadIdx.x);
|
||||
__syncthreads();
|
||||
printf("%s%u\n", CONST_STR3, sharedMem);
|
||||
}
|
||||
// Synchronize the prints in a block using __syncthreads. Only 1 block
|
||||
// is launched in a stream of size 'n'. The size of the block is 'b'.
|
||||
// (total bytes per thread)*n*b ≈ N GB. where N is user input.
|
||||
__global__ void kernel_synchronized_printf() {
|
||||
printf("%s%u\n", CONST_STR3, 0);
|
||||
__syncthreads();
|
||||
printf("%s%u\n", CONST_STR3, 1);
|
||||
__syncthreads();
|
||||
printf("%s%u\n", CONST_STR3, 2);
|
||||
}
|
||||
#ifdef __linux__
|
||||
// Launches kernel_printf_conststr to generate the printf log file
|
||||
// and validates the generated file size and number of printed lines
|
||||
// with the calculated file size and lines.
|
||||
bool test_printf_conststr(uint32_t num_blocks, uint32_t threads_per_block,
|
||||
uint32_t print_limit) {
|
||||
uint32_t iterCount = 0;
|
||||
uint32_t sizePrintString = (sizeof(CONST_STR)-1); // Excluding NULL character
|
||||
// Calculate the number of iterations from print_limit.
|
||||
size_t stress_limit_bytes = ((size_t)print_limit*1024*1024*1024);
|
||||
iterCount = static_cast<uint32_t>(1 +
|
||||
stress_limit_bytes/(num_blocks*threads_per_block*sizePrintString));
|
||||
// Calculate expected lines of print and file size.
|
||||
uint32_t totalExpectedLines = num_blocks*threads_per_block*iterCount;
|
||||
size_t expectedFileSize = ((size_t)totalExpectedLines*sizePrintString);
|
||||
size_t actualFileSize = 0;
|
||||
uint32_t totalActualLinecount = 0;
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipLaunchKernelGGL(kernel_printf_conststr, dim3(num_blocks, 1, 1),
|
||||
dim3(threads_per_block, 1, 1),
|
||||
0, 0, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipStreamSynchronize(0));
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
totalActualLinecount++;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
printf("totalExpectedLines = %u \n", totalExpectedLines);
|
||||
// Excluding the trailing newline
|
||||
printf("totalActualLinecount = %u \n", totalActualLinecount-1);
|
||||
printf("expectedFileSize = %zu \n", expectedFileSize);
|
||||
printf("actualFileSize = %zu \n", actualFileSize);
|
||||
if ((totalExpectedLines != (totalActualLinecount - 1))||
|
||||
(expectedFileSize != actualFileSize)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Launches kernel_printf_two_conditionalstr to generate the printf log file
|
||||
// and validates the generated file size and number of printed lines
|
||||
// with the calculated file size and lines.
|
||||
bool test_printf_two_conditionalstr(uint32_t num_blocks,
|
||||
uint32_t threads_per_block,
|
||||
uint32_t print_limit) {
|
||||
uint32_t iterCount = 0;
|
||||
uint32_t sizePrintStringEven, sizePrintStringOdd, avgsizePrintString;
|
||||
sizePrintStringEven = (sizeof(CONST_STR1)-1); // Excluding NULL character
|
||||
sizePrintStringOdd = (sizeof(CONST_STR2)-1); // Excluding NULL character
|
||||
avgsizePrintString = (sizePrintStringEven + sizePrintStringOdd)/2;
|
||||
// Calculate the number of iterations from print_limit
|
||||
size_t stress_limit_bytes = ((size_t)print_limit*1024*1024*1024);
|
||||
iterCount = static_cast<uint32_t>(1 +
|
||||
stress_limit_bytes/(num_blocks*threads_per_block*avgsizePrintString));
|
||||
// Calculate expected lines of print and file size.
|
||||
uint32_t totalExpectedEvenLines, totalExpectedOddLines;
|
||||
// 0, 1, 2, 3
|
||||
// 0, 1, 2
|
||||
totalExpectedEvenLines = ((num_blocks*threads_per_block)%2 == 0)?
|
||||
(num_blocks*threads_per_block*iterCount)/2 :
|
||||
(((num_blocks*threads_per_block)/2)+ 1)*iterCount;
|
||||
totalExpectedOddLines = (num_blocks*threads_per_block*iterCount
|
||||
- totalExpectedEvenLines);
|
||||
size_t expectedFileSize =
|
||||
((size_t)totalExpectedEvenLines*sizePrintStringEven +
|
||||
(size_t)totalExpectedOddLines*sizePrintStringOdd);
|
||||
size_t actualFileSize = 0;
|
||||
uint32_t totalActualEvenLines = 0, totalActualOddLines = 0;
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipLaunchKernelGGL(kernel_printf_two_conditionalstr,
|
||||
dim3(num_blocks, 1, 1),
|
||||
dim3(threads_per_block, 1, 1),
|
||||
0, 0, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipStreamSynchronize(0));
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
uint32_t bufferlen = strlen(buffer);
|
||||
if ((sizePrintStringEven - 1) == bufferlen) {
|
||||
totalActualEvenLines++;
|
||||
} else if ((sizePrintStringOdd - 1) == bufferlen) {
|
||||
totalActualOddLines++;
|
||||
}
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
printf("totalExpectedEvenLines = %u \n", totalExpectedEvenLines);
|
||||
printf("totalActualEvenLines = %u \n", totalActualEvenLines);
|
||||
printf("totalExpectedOddLines = %u \n", totalExpectedOddLines);
|
||||
printf("totalActualOddLines = %u \n", totalActualOddLines);
|
||||
printf("expectedFileSize = %zu \n", expectedFileSize);
|
||||
printf("actualFileSize = %zu \n", actualFileSize);
|
||||
if ((totalExpectedEvenLines != totalActualEvenLines)||
|
||||
(totalExpectedOddLines != totalActualOddLines)||
|
||||
(expectedFileSize != actualFileSize)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Launches kernel_printf_single_conditionalstr to generate the printf log
|
||||
// and validates the generated file size and number of printed lines
|
||||
// with the calculated file size and lines.
|
||||
bool test_printf_single_conditionalstr(uint32_t num_blocks,
|
||||
uint32_t threads_per_block,
|
||||
uint32_t print_limit) {
|
||||
uint32_t iterCount = 0;
|
||||
uint32_t sizePrintStringEven = (sizeof(CONST_STR1)-1);
|
||||
// Excluding NULL character
|
||||
// Calculate the number of iterations from print_limit
|
||||
size_t stress_limit_bytes = ((size_t)print_limit*1024*1024*1024);
|
||||
iterCount = static_cast<uint32_t>((2*stress_limit_bytes)/
|
||||
(num_blocks*threads_per_block*sizePrintStringEven));
|
||||
// Calculate expected lines of print and file size.
|
||||
uint32_t totalExpectedLines;
|
||||
totalExpectedLines = ((num_blocks*threads_per_block)%2 == 0)?
|
||||
(num_blocks*threads_per_block*iterCount)/2 :
|
||||
(((num_blocks*threads_per_block)/2)+ 1)*iterCount;
|
||||
size_t expectedFileSize =
|
||||
(size_t)totalExpectedLines*sizePrintStringEven;
|
||||
size_t actualFileSize = 0;
|
||||
uint32_t totalActualLines = 0;
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipLaunchKernelGGL(kernel_printf_single_conditionalstr,
|
||||
dim3(num_blocks, 1, 1),
|
||||
dim3(threads_per_block, 1, 1),
|
||||
0, 0, iterCount);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipStreamSynchronize(0));
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
totalActualLines++;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
printf("totalExpectedLines = %u \n", totalExpectedLines);
|
||||
printf("totalActualLines = %u \n", totalActualLines-1);
|
||||
printf("expectedFileSize = %zu \n", expectedFileSize);
|
||||
printf("actualFileSize = %zu \n", actualFileSize);
|
||||
if ((totalExpectedLines != (totalActualLines - 1))||
|
||||
(expectedFileSize != actualFileSize)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Launches kernel_printf_variablestr Or kernel_dependent_calc Or
|
||||
// kernel_dependent_calc_atomic to generate the printf log
|
||||
// and validates the generated file size and number of printed lines
|
||||
// with the calculated file size and lines.
|
||||
bool test_variable_str(uint32_t print_limit,
|
||||
void(*func)(uint32_t, int *),
|
||||
const struct SizeStruct* table) {
|
||||
uint32_t iterCount = table[print_limit - 1].iteration;
|
||||
uint32_t num_blocks = table[print_limit - 1].grid_size;
|
||||
uint32_t threads_per_block = table[print_limit - 1].block_size;
|
||||
// Calculate expected lines of print and file size.
|
||||
size_t actualFileSize = 0;
|
||||
uint32_t totalActualLines = 0;
|
||||
uint32_t totalExpectedLines = 4*iterCount*num_blocks*threads_per_block;
|
||||
size_t expectedFileSize = 0;
|
||||
|
||||
uint32_t buffsize = threads_per_block*num_blocks;
|
||||
int32_t *Ah;
|
||||
int32_t *Ad;
|
||||
Ah = new int32_t[buffsize];
|
||||
for (uint32_t i = 0; i < buffsize; i++) {
|
||||
Ah[i] = 0;
|
||||
}
|
||||
HIP_CHECK(hipMalloc(&Ad, buffsize*sizeof(int32_t)));
|
||||
HIP_CHECK(hipMemcpy(Ad, Ah, buffsize*sizeof(int32_t),
|
||||
hipMemcpyHostToDevice));
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipLaunchKernelGGL(func, dim3(num_blocks, 1, 1),
|
||||
dim3(threads_per_block, 1, 1),
|
||||
0, 0, iterCount, Ad);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipStreamSynchronize(0));
|
||||
HIP_CHECK(hipMemcpy(Ah, Ad, buffsize*sizeof(int32_t),
|
||||
hipMemcpyDeviceToHost));
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
totalActualLines++;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
for (uint32_t i = 0; i < buffsize; i++) {
|
||||
expectedFileSize += Ah[i];
|
||||
}
|
||||
HIP_CHECK(hipFree(Ad));
|
||||
delete[] Ah;
|
||||
printf("totalExpectedLines = %u \n", totalExpectedLines);
|
||||
printf("totalActualLines = %u \n", totalActualLines-1);
|
||||
printf("expectedFileSize = %zu \n", expectedFileSize);
|
||||
printf("actualFileSize = %zu \n", actualFileSize);
|
||||
if ((totalExpectedLines != (totalActualLines - 1))||
|
||||
(expectedFileSize != actualFileSize)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Launches kernel_decimal_calculation to generate the printf log file
|
||||
// and validates the generated file size and number of printed lines
|
||||
// with the calculated file size and lines.
|
||||
bool test_decimal_str(uint32_t num_blocks, uint32_t threads_per_block,
|
||||
uint32_t print_limit) {
|
||||
// Calculate the number of iterations from print_limit
|
||||
size_t stress_limit_bytes = ((size_t)print_limit*1024*1024*1024);
|
||||
int maxPrecision = 10;
|
||||
int totalPrintSizePerIter = printPi(maxPrecision);
|
||||
uint32_t iterCount = static_cast<uint32_t>(1+ stress_limit_bytes/
|
||||
(num_blocks*threads_per_block*totalPrintSizePerIter));
|
||||
// Calculate expected lines of print and file size.
|
||||
size_t actualFileSize = 0;
|
||||
size_t expectedFileSize =
|
||||
(size_t)num_blocks*threads_per_block*iterCount*totalPrintSizePerIter;
|
||||
uint32_t totalActualLines = 0;
|
||||
uint32_t totalExpectedLines =
|
||||
(maxPrecision + 1)*iterCount*num_blocks*threads_per_block;
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipLaunchKernelGGL(kernel_decimal_calculation, dim3(num_blocks, 1, 1),
|
||||
dim3(threads_per_block, 1, 1),
|
||||
0, 0, iterCount, maxPrecision);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
HIP_CHECK(hipStreamSynchronize(0));
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
totalActualLines++;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
printf("totalExpectedLines = %u \n", totalExpectedLines);
|
||||
printf("totalActualLines = %u \n", totalActualLines-1);
|
||||
printf("expectedFileSize = %zu \n", expectedFileSize);
|
||||
printf("actualFileSize = %zu \n", actualFileSize);
|
||||
if ((totalExpectedLines != (totalActualLines - 1))||
|
||||
(expectedFileSize != actualFileSize)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Launches kernel_shared_mem to generate the printf log file
|
||||
// and validates the generated file size and number of printed lines
|
||||
// with the calculated file size and lines.
|
||||
bool test_shared_mem(uint32_t num_blocks, uint32_t threads_per_block,
|
||||
uint32_t print_limit) {
|
||||
// Calculate the number of iterations from print_limit
|
||||
size_t stress_limit_bytes = ((size_t)print_limit*1024*1024*1024);
|
||||
unsigned total_0_to_blksize = (BLOCK_SIZE - 1)*BLOCK_SIZE / 2;
|
||||
char buffer[CHUNK_SIZE];
|
||||
int totalPrintSizePerThread = snprintf(buffer, CHUNK_SIZE,
|
||||
"%s%u\n", CONST_STR3, total_0_to_blksize);
|
||||
uint32_t iterCount = static_cast<uint32_t>(1+ stress_limit_bytes/
|
||||
(num_blocks*threads_per_block*totalPrintSizePerThread));
|
||||
// Calculate expected lines of print and file size.
|
||||
size_t actualFileSize = 0;
|
||||
size_t expectedFileSize =
|
||||
(size_t)num_blocks*threads_per_block*iterCount*totalPrintSizePerThread;
|
||||
uint32_t totalActualLines = 0;
|
||||
uint32_t totalExpectedLines = iterCount*num_blocks*threads_per_block;
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipStream_t stream;
|
||||
HIP_CHECK(hipStreamCreate(&stream));
|
||||
for (int count = 0; count < (const int)iterCount; count++) {
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_shared_mem,
|
||||
dim3(num_blocks, 1, 1), dim3(threads_per_block, 1, 1),
|
||||
NULL, 0, stream));
|
||||
}
|
||||
HIP_CHECK(hipStreamSynchronize(stream));
|
||||
HIP_CHECK(hipStreamDestroy(stream));
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
totalActualLines++;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found \n");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
printf("totalExpectedLines = %u \n", totalExpectedLines);
|
||||
printf("totalActualLines = %u \n", totalActualLines-1);
|
||||
printf("expectedFileSize = %zu \n", expectedFileSize);
|
||||
printf("actualFileSize = %zu \n", actualFileSize);
|
||||
if ((totalExpectedLines != (totalActualLines - 1))||
|
||||
(expectedFileSize != actualFileSize)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Launches kernel_synchronized_printf to generate the printf log file
|
||||
// and validates the generated file size and number of printed lines
|
||||
// with the calculated file size and lines.
|
||||
bool test_synchronized_printf(uint32_t num_blocks,
|
||||
uint32_t threads_per_block,
|
||||
uint32_t print_limit) {
|
||||
// Calculate the number of iterations from print_limit
|
||||
size_t stress_limit_bytes = ((size_t)print_limit*1024*1024*1024);
|
||||
char buffer0[CHUNK_SIZE], buffer1[CHUNK_SIZE], buffer2[CHUNK_SIZE];
|
||||
int totalPrintSizePerThread = snprintf(buffer0, CHUNK_SIZE,
|
||||
"%s%u\n", CONST_STR3, 0);
|
||||
totalPrintSizePerThread += snprintf(buffer1, CHUNK_SIZE,
|
||||
"%s%u\n", CONST_STR3, 1);
|
||||
totalPrintSizePerThread += snprintf(buffer2, CHUNK_SIZE,
|
||||
"%s%u\n", CONST_STR3, 2);
|
||||
uint32_t iterCount = static_cast<uint32_t>(1+ stress_limit_bytes/
|
||||
(num_blocks*threads_per_block*totalPrintSizePerThread));
|
||||
// Calculate expected lines of print and file size.
|
||||
size_t actualFileSize = 0;
|
||||
size_t expectedFileSize =
|
||||
(size_t)num_blocks*threads_per_block*iterCount*totalPrintSizePerThread;
|
||||
uint32_t totalActualLines = 0;
|
||||
uint32_t totalExpectedLines = 3*iterCount*num_blocks*threads_per_block;
|
||||
bool TestPassed = true;
|
||||
size_t len = strlen(buffer0) - 1;
|
||||
// DO NOT PUT ANY PRINTF WITHIN THIS BLOCK OF CODE
|
||||
{
|
||||
CaptureStream captured(stdout);
|
||||
hipStream_t stream;
|
||||
HIP_CHECK(hipStreamCreate(&stream));
|
||||
for (int count = 0; count < (const int)iterCount; count++) {
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_synchronized_printf,
|
||||
dim3(num_blocks, 1, 1), dim3(threads_per_block, 1, 1),
|
||||
NULL, 0, stream));
|
||||
}
|
||||
HIP_CHECK(hipStreamSynchronize(stream));
|
||||
HIP_CHECK(hipStreamDestroy(stream));
|
||||
std::ifstream CapturedData = captured.getCapturedData();
|
||||
char *buffer = new char[CHUNK_SIZE];
|
||||
while (CapturedData.good()) {
|
||||
CapturedData.getline(buffer, CHUNK_SIZE);
|
||||
if (!strcmp(buffer, "")) {
|
||||
break;
|
||||
}
|
||||
if (0 == ((totalActualLines / threads_per_block) % 3)) {
|
||||
if (strncmp(buffer, buffer0, len)) {
|
||||
TestPassed = false;
|
||||
break;
|
||||
}
|
||||
} else if (1 == ((totalActualLines / threads_per_block) % 3)) {
|
||||
if (strncmp(buffer, buffer1, len)) {
|
||||
TestPassed = false;
|
||||
break;
|
||||
}
|
||||
} else if (2 == ((totalActualLines / threads_per_block) % 3)) {
|
||||
if (strncmp(buffer, buffer2, len)) {
|
||||
TestPassed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
totalActualLines++;
|
||||
}
|
||||
delete[] buffer;
|
||||
struct stat st;
|
||||
if (stat(captured.getTempFilename(), &st)) {
|
||||
printf("Temp File not found");
|
||||
return false;
|
||||
}
|
||||
actualFileSize = st.st_size;
|
||||
}
|
||||
printf("totalExpectedLines = %u \n", totalExpectedLines);
|
||||
printf("totalActualLines = %u \n", totalActualLines);
|
||||
printf("expectedFileSize = %zu \n", expectedFileSize);
|
||||
printf("actualFileSize = %zu \n", actualFileSize);
|
||||
if ((TestPassed == false)||
|
||||
(expectedFileSize != actualFileSize)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
} // namespace hipPrintfStressTest
|
||||
|
||||
TEST_CASE("Stress_printf_ConstStr") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_ConstStr\n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = BLOCK_SIZE;
|
||||
uint num_blocks = GRID_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed =
|
||||
hipPrintfStressTest::test_printf_conststr(num_blocks, threads_per_block,
|
||||
print_limit);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_IfElseConditionalStr") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_IfElseConditionalStr\n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = BLOCK_SIZE;
|
||||
uint num_blocks = GRID_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed =
|
||||
hipPrintfStressTest::test_printf_two_conditionalstr(num_blocks,
|
||||
threads_per_block, print_limit);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_IfConditionalStr") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_IfConditionalStr\n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = BLOCK_SIZE;
|
||||
uint num_blocks = GRID_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed =
|
||||
hipPrintfStressTest::test_printf_single_conditionalstr(num_blocks,
|
||||
threads_per_block, print_limit);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_VariableStr") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_VariableStr\n");
|
||||
bool TestPassed = true;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed = hipPrintfStressTest::test_variable_str(print_limit,
|
||||
hipPrintfStressTest::kernel_printf_variablestr,
|
||||
hipPrintfStressTest::EmpiricalValues1);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_DependentCalc") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_DependentCalc\n");
|
||||
bool TestPassed = true;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed = hipPrintfStressTest::test_variable_str(print_limit,
|
||||
hipPrintfStressTest::kernel_dependent_calc,
|
||||
hipPrintfStressTest::EmpiricalValues2);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_DecimalStr") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_DecimalStr\n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = BLOCK_SIZE;
|
||||
uint num_blocks = GRID_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed = hipPrintfStressTest::test_decimal_str(num_blocks,
|
||||
threads_per_block, print_limit);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_SharedMem") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_SharedMem\n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = BLOCK_SIZE;
|
||||
uint num_blocks = GRID_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed = hipPrintfStressTest::test_shared_mem(num_blocks,
|
||||
threads_per_block, print_limit);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_SynchronizedPrintf") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_SynchronizedPrintf\n");
|
||||
bool TestPassed = true;
|
||||
uint threads_per_block = BLOCK_SIZE;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed = hipPrintfStressTest::test_synchronized_printf(1,
|
||||
threads_per_block, print_limit);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_printf_AtomicCalc") {
|
||||
#ifdef __linux__
|
||||
printf("Test: Stress_printf_AtomicCalc\n");
|
||||
bool TestPassed = true;
|
||||
// N provide the print limit
|
||||
unsigned int print_limit = 1; // = 1 GB
|
||||
TestPassed = hipPrintfStressTest::test_variable_str(print_limit,
|
||||
hipPrintfStressTest::kernel_dependent_calc_atomic,
|
||||
hipPrintfStressTest::EmpiricalValues2);
|
||||
REQUIRE(TestPassed);
|
||||
#else
|
||||
printf("This test is skipped due to non linux environment.\n");
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef _STRESSTEST_PRINTF_COMMON_H_
|
||||
#define _STRESSTEST_PRINTF_COMMON_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <math.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
struct CaptureStream {
|
||||
int saved_fd;
|
||||
int orig_fd;
|
||||
int temp_fd;
|
||||
|
||||
char tempname[13] = "mytestXXXXXX";
|
||||
|
||||
explicit CaptureStream(FILE *original) {
|
||||
orig_fd = fileno(original);
|
||||
saved_fd = dup(orig_fd);
|
||||
|
||||
if ((temp_fd = mkstemp(tempname)) == -1) {
|
||||
error(0, errno, "Error");
|
||||
assert(false);
|
||||
}
|
||||
|
||||
fflush(nullptr);
|
||||
if (dup2(temp_fd, orig_fd) == -1) {
|
||||
error(0, errno, "Error");
|
||||
assert(false);
|
||||
}
|
||||
if (close(temp_fd) != 0) {
|
||||
error(0, errno, "Error");
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void restoreStream() {
|
||||
if (saved_fd == -1)
|
||||
return;
|
||||
fflush(nullptr);
|
||||
if (dup2(saved_fd, orig_fd) == -1) {
|
||||
error(0, errno, "Error");
|
||||
assert(false);
|
||||
}
|
||||
if (close(saved_fd) != 0) {
|
||||
error(0, errno, "Error");
|
||||
assert(false);
|
||||
}
|
||||
saved_fd = -1;
|
||||
}
|
||||
|
||||
const char *getTempFilename() {
|
||||
return (const char*)tempname;
|
||||
}
|
||||
|
||||
std::ifstream getCapturedData() {
|
||||
restoreStream();
|
||||
std::ifstream temp(tempname);
|
||||
return temp;
|
||||
}
|
||||
|
||||
~CaptureStream() {
|
||||
restoreStream();
|
||||
if (remove(tempname) != 0) {
|
||||
error(0, errno, "Error");
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _STRESSTEST_PRINTF_COMMON_H_
|
||||
@@ -0,0 +1,10 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
Stress_hipStreamCreate.cc
|
||||
streamEnqueue.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME stream
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME stress_test
|
||||
COMPILE_OPTIONS -std=c++14)
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
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 <hip_test_common.hh>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
#define NUM_ITER 100000
|
||||
#define TOTALSEQ 18
|
||||
|
||||
namespace hipStreamCreateStressTest {
|
||||
__global__ void kernel_do_nothing() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
int stream_seq[TOTALSEQ][4] = {
|
||||
{0, 1, 2, 0} , // Launch0->Launch1->Launch2->Sync0
|
||||
{0, 2, 1, 0} , // Launch0->Launch2->Launch1->Sync0
|
||||
{1, 0, 2, 0} , // Launch1->Launch0->Launch2->Sync0
|
||||
{1, 2, 0, 0} , // Launch1->Launch2->Launch0->Sync0
|
||||
{2, 0, 1, 0} , // Launch2->Launch0->Launch1->Sync0
|
||||
{2, 1, 0, 0} , // Launch2->Launch1->Launch0->Sync0
|
||||
{0, 1, 2, 1} , // Launch0->Launch1->Launch2->Sync1
|
||||
{0, 2, 1, 1} , // Launch0->Launch2->Launch1->Sync1
|
||||
{1, 0, 2, 1} , // Launch1->Launch0->Launch2->Sync1
|
||||
{1, 2, 0, 1} , // Launch1->Launch2->Launch0->Sync1
|
||||
{2, 0, 1, 1} , // Launch2->Launch0->Launch1->Sync1
|
||||
{2, 1, 0, 1} , // Launch2->Launch1->Launch0->Sync1
|
||||
{0, 1, 2, 2} , // Launch0->Launch1->Launch2->Sync2
|
||||
{0, 2, 1, 2} , // Launch0->Launch2->Launch1->Sync2
|
||||
{1, 0, 2, 2} , // Launch1->Launch0->Launch2->Sync2
|
||||
{1, 2, 0, 2} , // Launch1->Launch2->Launch0->Sync2
|
||||
{2, 0, 1, 2} , // Launch2->Launch0->Launch1->Sync2
|
||||
{2, 1, 0, 2} // Launch2->Launch1->Launch0->Sync2
|
||||
};
|
||||
|
||||
/**
|
||||
* Scenario: This test extends the DTEST introduced for SWDEV-238360 to test
|
||||
* all the possible scenarios mentioned under comments section
|
||||
* in SWDEV-237846.
|
||||
*/
|
||||
|
||||
void testhipStreamCreate(int *stream_sequence) {
|
||||
printf("%s: Testing sequence %d->%d->%d->sync(%d) \n", __func__,
|
||||
stream_sequence[0], stream_sequence[1], stream_sequence[2],
|
||||
stream_sequence[3]);
|
||||
// Streams
|
||||
hipStream_t stream[3];
|
||||
stream[0] = 0;
|
||||
HIP_CHECK(hipStreamCreate(&stream[1]));
|
||||
HIP_CHECK(hipStreamCreate(&stream[2]));
|
||||
// Run test loop
|
||||
for (int k = 0; k < NUM_ITER; ++k) {
|
||||
// Sync
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[0]]));
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[1]]));
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[2]]));
|
||||
// Sync stream 1
|
||||
HIP_CHECK(hipStreamSynchronize(stream[stream_sequence[3]]));
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Clean up
|
||||
HIP_CHECK(hipStreamDestroy(stream[1]));
|
||||
HIP_CHECK(hipStreamDestroy(stream[2]));
|
||||
}
|
||||
/**
|
||||
* Scenario: This test extends the above test by using 2 streams
|
||||
* (of highest and lowest priority) created using hipStreamCreateWithPriority
|
||||
* along with the default stream.
|
||||
*/
|
||||
void testhipStreamCreatePriority(int *stream_sequence,
|
||||
unsigned int flag) {
|
||||
printf("%s: Testing sequence %d->%d->%d->sync(%d) \n", __func__,
|
||||
stream_sequence[0], stream_sequence[1], stream_sequence[2],
|
||||
stream_sequence[3]);
|
||||
// Streams
|
||||
hipStream_t stream[3];
|
||||
stream[0] = 0;
|
||||
int priority_low = 0;
|
||||
int priority_high = 0;
|
||||
HIP_CHECK(hipDeviceGetStreamPriorityRange(&priority_low, &priority_high));
|
||||
if (priority_low == priority_high) {
|
||||
printf("Exiting test since priorities are not supported \n");
|
||||
return;
|
||||
}
|
||||
HIP_CHECK(hipStreamCreateWithPriority(&stream[1],
|
||||
flag, priority_high));
|
||||
HIP_CHECK(hipStreamCreateWithPriority(&stream[2],
|
||||
flag, priority_low));
|
||||
// Run test loop
|
||||
for (int k = 0; k < NUM_ITER; ++k) {
|
||||
// Sync
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[0]]));
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[1]]));
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[2]]));
|
||||
// Sync stream 1
|
||||
HIP_CHECK(hipStreamSynchronize(stream[stream_sequence[3]]));
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Clean up
|
||||
HIP_CHECK(hipStreamDestroy(stream[1]));
|
||||
HIP_CHECK(hipStreamDestroy(stream[2]));
|
||||
}
|
||||
/**
|
||||
* Scenario: This test extends the above test by using 2 streams
|
||||
* created using hipStreamCreateWithFlags along with the default stream.
|
||||
*/
|
||||
void testhipStreamCreateFlags(int *stream_sequence,
|
||||
unsigned int flag) {
|
||||
printf("%s: Testing sequence %d->%d->%d->sync(%d) \n", __func__,
|
||||
stream_sequence[0], stream_sequence[1], stream_sequence[2],
|
||||
stream_sequence[3]);
|
||||
// Streams
|
||||
hipStream_t stream[3];
|
||||
stream[0] = 0;
|
||||
HIP_CHECK(hipStreamCreateWithFlags(&stream[1], flag));
|
||||
HIP_CHECK(hipStreamCreateWithFlags(&stream[2], flag));
|
||||
// Run test loop
|
||||
for (int k = 0; k < NUM_ITER; ++k) {
|
||||
// Sync
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[0]]));
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[1]]));
|
||||
HIP_CHECK(hipLaunchKernel((const void*)kernel_do_nothing,
|
||||
dim3(1, 1, 1), dim3(1, 1, 1), NULL, 0,
|
||||
stream[stream_sequence[2]]));
|
||||
// Sync stream 1
|
||||
HIP_CHECK(hipStreamSynchronize(stream[stream_sequence[3]]));
|
||||
}
|
||||
HIP_CHECK(hipDeviceSynchronize());
|
||||
// Clean up
|
||||
HIP_CHECK(hipStreamDestroy(stream[1]));
|
||||
HIP_CHECK(hipStreamDestroy(stream[2]));
|
||||
}
|
||||
} // namespace hipStreamCreateStressTest
|
||||
|
||||
TEST_CASE("Stress_hipStreamCreate_SyncTest") {
|
||||
printf("hipStreamCreate stress test:\n");
|
||||
for (int i = 0; i < TOTALSEQ; i++) {
|
||||
hipStreamCreateStressTest::testhipStreamCreate(
|
||||
hipStreamCreateStressTest::stream_seq[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_hipStreamCreatePriority_SyncTest") {
|
||||
printf("hipStreamCreateWithPriority(hipStreamDefault) stress test:\n");
|
||||
for (int i = 0; i < TOTALSEQ; i++) {
|
||||
hipStreamCreateStressTest::testhipStreamCreatePriority(
|
||||
hipStreamCreateStressTest::stream_seq[i], hipStreamDefault);
|
||||
}
|
||||
printf("hipStreamCreateWithPriority(hipStreamNonBlocking) stress test:\n");
|
||||
for (int i = 0; i < TOTALSEQ; i++) {
|
||||
hipStreamCreateStressTest::testhipStreamCreatePriority(
|
||||
hipStreamCreateStressTest::stream_seq[i], hipStreamNonBlocking);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Stress_hipStreamCreateWithFlags_SyncTest") {
|
||||
printf("hipStreamCreateWithFlags(hipStreamDefault) stress test:\n");
|
||||
for (int i = 0; i < TOTALSEQ; i++) {
|
||||
hipStreamCreateStressTest::testhipStreamCreateFlags(
|
||||
hipStreamCreateStressTest::stream_seq[i], hipStreamDefault);
|
||||
}
|
||||
printf("hipStreamCreateWithFlags(hipStreamNonBlocking) stress test:\n");
|
||||
for (int i = 0; i < TOTALSEQ; i++) {
|
||||
hipStreamCreateStressTest::testhipStreamCreateFlags(
|
||||
hipStreamCreateStressTest::stream_seq[i], hipStreamNonBlocking);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
__global__ void addVal(unsigned long long* ptr, size_t index,
|
||||
unsigned long long val) {
|
||||
atomicAdd(ptr + index, val);
|
||||
}
|
||||
|
||||
// Create a copy constructible AtomicWrap around std::atomic so that
|
||||
// we can put it in a vector
|
||||
template <typename T> struct AtomicWrap {
|
||||
std::atomic<T> data;
|
||||
|
||||
AtomicWrap() : data() {}
|
||||
|
||||
AtomicWrap(T i) : data(i) {}
|
||||
|
||||
AtomicWrap(const std::atomic<T>& a) : data(a.load()) {}
|
||||
|
||||
AtomicWrap(const AtomicWrap& other) : data(other.data.load()) {}
|
||||
|
||||
AtomicWrap& operator=(const AtomicWrap& other) {
|
||||
data.store(other.data.load());
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Have multiple threads and enqueue commands from them on a single stream
|
||||
// Validate at the end that all commands have completed successfully
|
||||
TEST_CASE("Stress_StreamEnqueue_DifferentThreads") {
|
||||
auto hwThreads = std::thread::hardware_concurrency();
|
||||
hwThreads = (hwThreads >= 2) ? hwThreads : 2; // Run atleast 2 threads
|
||||
|
||||
std::vector<AtomicWrap<unsigned long long>> hostData(hwThreads, 0);
|
||||
|
||||
unsigned long long* dPtr{nullptr};
|
||||
HIP_CHECK(hipMalloc(&dPtr, sizeof(unsigned long long) * hwThreads));
|
||||
REQUIRE(dPtr != nullptr);
|
||||
|
||||
HIP_CHECK(hipMemset(dPtr, 0, sizeof(unsigned long long) * hwThreads));
|
||||
|
||||
std::random_device device;
|
||||
std::mt19937 engine(device());
|
||||
|
||||
constexpr size_t maxWork = 10000;
|
||||
constexpr size_t maxVal = 10;
|
||||
|
||||
std::uniform_int_distribution<std::mt19937::result_type> genIndex(0,
|
||||
hwThreads - 1);
|
||||
std::uniform_int_distribution<std::mt19937::result_type> genWork(0, maxWork);
|
||||
std::uniform_int_distribution<std::mt19937::result_type> genVal(0, maxVal);
|
||||
|
||||
auto enqueueKernelThread = [&](hipStream_t stream) {
|
||||
auto iter = genWork(engine); // Generate work to be done via thread
|
||||
for (unsigned long i = 0; i < iter; i++) {
|
||||
auto index = genIndex(engine); // Generate Index to add to
|
||||
auto val = genVal(engine); // Generate value to add to the destination
|
||||
hostData[index].data += val; // Replicate it on host
|
||||
addVal<<<1, 1, 0, stream>>>(dPtr, static_cast<size_t>(index),
|
||||
static_cast<unsigned long long>(val)); // And on device
|
||||
}
|
||||
};
|
||||
|
||||
hipStream_t stream{};
|
||||
HIP_CHECK(hipStreamCreate(&stream));
|
||||
std::vector<std::thread> threadPool{};
|
||||
threadPool.reserve(hwThreads);
|
||||
|
||||
// Launch work
|
||||
for (size_t i = 0; i < hwThreads; i++) {
|
||||
threadPool.emplace_back(std::thread(enqueueKernelThread, stream));
|
||||
}
|
||||
|
||||
// Wait for work to finish
|
||||
for (auto& i : threadPool) {
|
||||
i.join();
|
||||
}
|
||||
|
||||
HIP_CHECK(hipStreamDestroy(stream));
|
||||
|
||||
auto hPtr = std::make_unique<unsigned long long[]>(hwThreads);
|
||||
HIP_CHECK(hipMemcpy(hPtr.get(), dPtr, sizeof(unsigned long long) * hwThreads,
|
||||
hipMemcpyDeviceToHost));
|
||||
|
||||
HIP_CHECK(hipFree(dPtr));
|
||||
|
||||
// Validate that CPU and GPU has the same results
|
||||
for (size_t i = 0; i < hwThreads; i++) {
|
||||
INFO("Check for Index " << i);
|
||||
REQUIRE(hostData[i].data.load() == hPtr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void doOperation(int* dPtr, int val) {
|
||||
auto i = threadIdx.x;
|
||||
atomicAdd(dPtr + i, val);
|
||||
}
|
||||
|
||||
// Allocate mulitple stream for same device.
|
||||
// Same device stream operate on same memory
|
||||
TEST_CASE("Stress_StreamEnqueue_DifferentThreads_MultiGPU") {
|
||||
int deviceCount{0};
|
||||
HIP_CHECK(hipGetDeviceCount(&deviceCount));
|
||||
REQUIRE(deviceCount > 0);
|
||||
|
||||
// Skip the test if devices less than 2
|
||||
if (deviceCount <= 1) {
|
||||
HipTest::HIP_SKIP_TEST("Skipping because devices <= 1");
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr size_t streamPerGPU{3}; // Stream per gpu
|
||||
|
||||
std::vector<hipStream_t> streamPool{};
|
||||
streamPool.reserve(deviceCount * streamPerGPU);
|
||||
// Map of stream and device memory
|
||||
std::map<hipStream_t, int*> streamToDeviceMemory;
|
||||
// Map of stream and host result
|
||||
std::map<hipStream_t, AtomicWrap<int>> streamToHostMemory;
|
||||
// Map of stream and device it was created on
|
||||
std::map<hipStream_t, size_t> streamToDeviceIndex;
|
||||
constexpr size_t size = 1024;
|
||||
|
||||
for (int i = 0; i < deviceCount; i++) {
|
||||
HIP_CHECK(hipSetDevice(i));
|
||||
|
||||
for (size_t j = 0; j < streamPerGPU; j++) {
|
||||
hipStream_t stream{nullptr};
|
||||
HIP_CHECK(hipStreamCreate(&stream));
|
||||
REQUIRE(stream != nullptr);
|
||||
streamPool.push_back(stream);
|
||||
|
||||
int* dPtr{nullptr};
|
||||
HIP_CHECK(hipMalloc(&dPtr, sizeof(int) * size));
|
||||
REQUIRE(dPtr != nullptr);
|
||||
HIP_CHECK(hipMemset(dPtr, 0, sizeof(int) * size));
|
||||
// All streams work on exclusive memory
|
||||
streamToDeviceMemory[stream] = dPtr;
|
||||
|
||||
streamToHostMemory[stream] = AtomicWrap<int>(0); // CPU result
|
||||
|
||||
streamToDeviceIndex[stream] = i; // Capture device id for stream
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t maxVal = 5;
|
||||
constexpr size_t maxWorkPerThread = 10000;
|
||||
|
||||
// Boiler plate code to generate a random number
|
||||
std::random_device device;
|
||||
std::mt19937 engine(device());
|
||||
|
||||
std::uniform_int_distribution<std::mt19937::result_type> genVal(-maxVal,
|
||||
maxVal);
|
||||
std::uniform_int_distribution<std::mt19937::result_type> genStream(0,
|
||||
streamPool.size() - 1);
|
||||
|
||||
#if HT_NVIDIA
|
||||
std::mutex ness; // On nvidia, current device needs to match stream's device
|
||||
#endif
|
||||
|
||||
auto enqueueKernelThread = [&]() {
|
||||
for (size_t i = 0; i < maxWorkPerThread; i++) {
|
||||
#if HT_NVIDIA
|
||||
std::unique_lock<std::mutex> lock(ness); // Lock on creation
|
||||
#endif
|
||||
// Get a random stream
|
||||
hipStream_t stream = streamPool[genStream(engine)];
|
||||
|
||||
// TODO use HIP_CHECK_THREAD when PR#2664 is merged
|
||||
if (hipSuccess != hipSetDevice(streamToDeviceIndex[stream])) {
|
||||
return;
|
||||
}
|
||||
|
||||
int val = genVal(engine); // Generate Value to add/sub to
|
||||
// Replicate result on CPU
|
||||
streamToHostMemory[stream].data.fetch_add(val);
|
||||
auto dPtr = streamToDeviceMemory[stream];
|
||||
doOperation<<<1, 1024, 0, stream>>>(dPtr, val); // On GPU
|
||||
}
|
||||
};
|
||||
|
||||
auto maxThreads = std::thread::hardware_concurrency();
|
||||
maxThreads = (maxThreads >= 2) ? maxThreads : 2; // Run atleast 2 threads
|
||||
|
||||
std::vector<std::thread> threadPool{};
|
||||
threadPool.reserve(maxThreads);
|
||||
|
||||
// Launch Threads
|
||||
for (size_t i = 0; i < maxThreads; i++) {
|
||||
threadPool.emplace_back(std::thread(enqueueKernelThread));
|
||||
}
|
||||
|
||||
// Wait for them to stop
|
||||
for (auto& i : threadPool) {
|
||||
i.join();
|
||||
}
|
||||
|
||||
// Sync and check results
|
||||
for (auto& i : streamPool) {
|
||||
HIP_CHECK(hipStreamSynchronize(i));
|
||||
auto dResult = std::make_unique<int[]>(size);
|
||||
HIP_CHECK(hipMemcpy(dResult.get(), streamToDeviceMemory[i],
|
||||
sizeof(int) * size, hipMemcpyDeviceToHost));
|
||||
HIP_CHECK(hipFree(streamToDeviceMemory[i]));
|
||||
HIP_CHECK(hipStreamDestroy(i));
|
||||
auto res = streamToHostMemory[i].data.load();
|
||||
INFO("Matching CPU: " << res << " GPU: " << dResult[0] << " Dev Ptr: "
|
||||
<< streamToDeviceMemory[i] << " on Device: " << streamToDeviceIndex[i]);
|
||||
REQUIRE(std::all_of(dResult.get(), dResult.get() + size,
|
||||
[=](int r) { return r == res; }));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# 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.
|
||||
|
||||
add_subdirectory(rtc)
|
||||
add_subdirectory(deviceLib)
|
||||
add_subdirectory(graph)
|
||||
add_subdirectory(memory)
|
||||
add_subdirectory(stream)
|
||||
add_subdirectory(event)
|
||||
add_subdirectory(occupancy)
|
||||
add_subdirectory(device)
|
||||
add_subdirectory(printf)
|
||||
add_subdirectory(texture)
|
||||
add_subdirectory(streamperthread)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(multiThread)
|
||||
add_subdirectory(compiler)
|
||||
add_subdirectory(errorHandling)
|
||||
add_subdirectory(cooperativeGrps)
|
||||
#if(HIP_PLATFORM STREQUAL "amd")
|
||||
#add_subdirectory(clock)
|
||||
#endif()
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# Common Tests - Test independent of all platforms
|
||||
|
||||
set(TEST_SRC
|
||||
hipClockCheck.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME ClockCheckTest
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME build_tests)
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip_test_checkers.hh>
|
||||
#include <hip/hip_ext.h>
|
||||
|
||||
#define ONESECOND 1000 // in ms
|
||||
#define HALFSECOND 500 // in ms
|
||||
|
||||
enum CLOCK_MODE {
|
||||
CLOCK_MODE_CLOCK64,
|
||||
CLOCK_MODE_WALL_CLOCK64
|
||||
};
|
||||
|
||||
__global__ void kernel_c(int clockRate, uint64_t wait_t) {
|
||||
uint64_t start = clock64() / clockRate, cur = 0; // in ms
|
||||
do { cur = clock64() / clockRate-start;} while (cur < wait_t);
|
||||
}
|
||||
|
||||
__global__ void kernel_w(int clockRate, uint64_t wait_t) {
|
||||
uint64_t start = wall_clock64() / clockRate, cur = 0; // in ms
|
||||
do { cur = wall_clock64() / clockRate-start;} while (cur < wait_t);
|
||||
}
|
||||
|
||||
bool verifyTimeExecution(CLOCK_MODE m, float time1, float time2,
|
||||
float expectedTime1, float expectedTime2) {
|
||||
bool testStatus = false;
|
||||
float ratio = m == CLOCK_MODE_CLOCK64 ? 0.5 : 0.01;
|
||||
|
||||
if (fabs(time1 - expectedTime1) < ratio * expectedTime1
|
||||
&& fabs(time2 - expectedTime2) < ratio * expectedTime2) {
|
||||
WARN("Succeeded: Expected Vs Actual: Kernel1 - " << expectedTime1 << " Vs " << time1
|
||||
<< ", Kernel2 - " << expectedTime2 << " Vs " << time2);
|
||||
testStatus = true;
|
||||
} else {
|
||||
FAIL_CHECK("Failed: Expected Vs Actual: Kernel1 -" << expectedTime1 << " Vs " << time1
|
||||
<< ", Kernel2 - " << expectedTime2 << " Vs " << time2);
|
||||
testStatus = false;
|
||||
}
|
||||
return testStatus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Launching kernel1 and kernel2 and then we try to
|
||||
* get the event elapsed time of each kernel using the start and
|
||||
* end events.The event elapsed time should return us the kernel
|
||||
* execution time for that particular kernel
|
||||
*/
|
||||
bool kernelTimeExecution(CLOCK_MODE m, int clockRate,
|
||||
uint64_t expectedTime1, uint64_t expectedTime2) {
|
||||
hipStream_t stream;
|
||||
hipEvent_t start_event1, end_event1, start_event2, end_event2;
|
||||
float time1 = 0, time2 = 0;
|
||||
HIPCHECK(hipEventCreate(&start_event1));
|
||||
HIPCHECK(hipEventCreate(&end_event1));
|
||||
HIPCHECK(hipEventCreate(&start_event2));
|
||||
HIPCHECK(hipEventCreate(&end_event2));
|
||||
HIPCHECK(hipStreamCreate(&stream));
|
||||
hipExtLaunchKernelGGL( m == CLOCK_MODE_CLOCK64 ? kernel_c : kernel_w,
|
||||
dim3(1), dim3(1), 0, stream, start_event1, end_event1, 0, clockRate, expectedTime1);
|
||||
hipExtLaunchKernelGGL( m == CLOCK_MODE_CLOCK64 ? kernel_c : kernel_w,
|
||||
dim3(1), dim3(1), 0, stream, start_event2, end_event2, 0, clockRate, expectedTime2);
|
||||
HIPCHECK(hipStreamSynchronize(stream));
|
||||
HIPCHECK(hipEventElapsedTime(&time1, start_event1, end_event1));
|
||||
HIPCHECK(hipEventElapsedTime(&time2, start_event2, end_event2));
|
||||
|
||||
HIPCHECK(hipStreamDestroy(stream));
|
||||
HIPCHECK(hipEventDestroy(start_event1));
|
||||
HIPCHECK(hipEventDestroy(end_event1));
|
||||
HIPCHECK(hipEventDestroy(start_event2));
|
||||
HIPCHECK(hipEventDestroy(end_event2));
|
||||
|
||||
return verifyTimeExecution(m, time1, time2, expectedTime1, expectedTime2);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClock64_Check") {
|
||||
HIPCHECK(hipSetDevice(0));
|
||||
int clockRate = 0; // in KHz
|
||||
HIPCHECK(hipDeviceGetAttribute(&clockRate, hipDeviceAttributeClockRate, 0));
|
||||
|
||||
SECTION("Verify kernel execution time via clock64()") {
|
||||
CHECK(kernelTimeExecution(CLOCK_MODE_CLOCK64, clockRate, ONESECOND, HALFSECOND));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipWallClock64_Check") {
|
||||
HIPCHECK(hipSetDevice(0));
|
||||
int clockRate = 0; // in KHz
|
||||
HIPCHECK(hipDeviceGetAttribute(&clockRate, hipDeviceAttributeWallClockRate, 0));
|
||||
|
||||
if(!clockRate) {
|
||||
INFO("hipDeviceAttributeWallClockRate has not been supported. Skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
SECTION("Verify kernel execution time via wall_clock64()") {
|
||||
CHECK(kernelTimeExecution(CLOCK_MODE_WALL_CLOCK64, clockRate, ONESECOND, HALFSECOND));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
hipClassKernel.cc
|
||||
)
|
||||
|
||||
hip_add_exe_to_target(NAME CompilerTest
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME build_tests)
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
Copyright (c) 2015 - 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.
|
||||
*/
|
||||
/* HIT_START
|
||||
* BUILD: %t %s ../test_common.cpp
|
||||
* TEST: %t
|
||||
* HIT_END
|
||||
*/
|
||||
#include "hipClassKernel.h"
|
||||
|
||||
__global__ void
|
||||
ovrdClassKernel(bool* result_ecd){
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
testOvrD tobj1;
|
||||
result_ecd[tid] = (tobj1.ovrdFunc1() == 30);
|
||||
}
|
||||
|
||||
__global__ void
|
||||
ovldClassKernel(bool* result_ecd){
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
testFuncOvld tfo1;
|
||||
result_ecd[tid] = (tfo1.func1(10) == 20)
|
||||
&& (tfo1.func1(10,10) == 30);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClassKernel_Overload_Override") {
|
||||
bool *result_ecd, *result_ech;
|
||||
result_ech = AllocateHostMemory();
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
|
||||
hipLaunchKernelGGL(ovrdClassKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
result_ecd);
|
||||
|
||||
VerifyResult(result_ech,result_ecd);
|
||||
FreeMem(result_ech,result_ecd);
|
||||
|
||||
result_ech = AllocateHostMemory();
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
|
||||
hipLaunchKernelGGL(ovldClassKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
result_ecd);
|
||||
|
||||
VerifyResult(result_ech,result_ecd);
|
||||
FreeMem(result_ech,result_ecd);
|
||||
}
|
||||
|
||||
// check for friend
|
||||
__global__ void
|
||||
friendClassKernel(bool* result_ecd){
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
testFrndB tfb1;
|
||||
result_ecd[tid] = (tfb1.showA() == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClassKernel_Friend") {
|
||||
bool *result_ecd;
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
hipLaunchKernelGGL(friendClassKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
result_ecd);
|
||||
}
|
||||
|
||||
// check sizeof empty class is 1
|
||||
__global__ void
|
||||
emptyClassKernel(bool* result_ecd) {
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
testClassEmpty ob1,ob2;
|
||||
result_ecd[tid] = (sizeof(testClassEmpty) == 1)
|
||||
&& (&ob1 != &ob2);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClassKernel_Empty") {
|
||||
bool *result_ecd, *result_ech;
|
||||
result_ech = AllocateHostMemory();
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
|
||||
hipLaunchKernelGGL(emptyClassKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
result_ecd);
|
||||
|
||||
VerifyResult(result_ech,result_ecd);
|
||||
FreeMem(result_ech,result_ecd);
|
||||
}
|
||||
|
||||
// tests for classes >8 bytes
|
||||
__global__ void
|
||||
sizeClassBKernel(bool* result_ecd) {
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
result_ecd[tid] = (sizeof(testSizeB) == 12)
|
||||
&& (sizeof(testSizeC) == 16)
|
||||
&& (sizeof(testSizeP1) == 6)
|
||||
&& (sizeof(testSizeP2) == 13)
|
||||
&& (sizeof(testSizeP3) == 8);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClassKernel_BSize") {
|
||||
bool *result_ecd, *result_ech;
|
||||
result_ech = AllocateHostMemory();
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
|
||||
hipLaunchKernelGGL(sizeClassBKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
result_ecd);
|
||||
|
||||
VerifyResult(result_ech,result_ecd);
|
||||
FreeMem(result_ech,result_ecd);
|
||||
}
|
||||
|
||||
__global__ void
|
||||
sizeClassKernel(bool* result_ecd) {
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
result_ecd[tid] = (sizeof(testSizeA) == 16)
|
||||
&& (sizeof(testSizeDerived) == 24)
|
||||
&& (sizeof(testSizeDerived2) == 20);
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClassKernel_Size") {
|
||||
bool *result_ecd, *result_ech;
|
||||
result_ech = AllocateHostMemory();
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
|
||||
hipLaunchKernelGGL(sizeClassKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
result_ecd);
|
||||
|
||||
VerifyResult(result_ech,result_ecd);
|
||||
FreeMem(result_ech,result_ecd);
|
||||
}
|
||||
|
||||
__global__ void
|
||||
sizeVirtualClassKernel(bool* result_ecd) {
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
result_ecd[tid] = (sizeof(testSizeDV) == 16)
|
||||
&& (sizeof(testSizeDerivedDV) == 16)
|
||||
&& (sizeof(testSizeVirtDerPack) == 24)
|
||||
&& (sizeof(testSizeVirtDer) == 24)
|
||||
&& (sizeof(testSizeDerMulti) == 48) ;
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClassKernel_Virtual") {
|
||||
bool *result_ecd, *result_ech;
|
||||
result_ech = AllocateHostMemory();
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
|
||||
hipLaunchKernelGGL(sizeVirtualClassKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
result_ecd);
|
||||
|
||||
VerifyResult(result_ech,result_ecd);
|
||||
FreeMem(result_ech,result_ecd);
|
||||
}
|
||||
|
||||
// check pass by value
|
||||
__global__ void
|
||||
passByValueKernel(testPassByValue obj, bool* result_ecd) {
|
||||
int tid = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
result_ecd[tid] = (obj.exI == 10)
|
||||
&& (obj.exC == 'C');
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipClassKernel_Value") {
|
||||
bool *result_ecd,*result_ech;
|
||||
result_ech = AllocateHostMemory();
|
||||
result_ecd = AllocateDeviceMemory();
|
||||
|
||||
testPassByValue exObj;
|
||||
exObj.exI = 10;
|
||||
exObj.exC = 'C';
|
||||
hipLaunchKernelGGL(passByValueKernel,
|
||||
dim3(BLOCKS),
|
||||
dim3(THREADS_PER_BLOCK),
|
||||
0,
|
||||
0,
|
||||
exObj,
|
||||
result_ecd);
|
||||
|
||||
VerifyResult(result_ech,result_ecd);
|
||||
FreeMem(result_ech,result_ecd);
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright (c) 2015 - 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.
|
||||
*/
|
||||
#ifndef _COMPILER_HIPCLASSKERNEL_H_
|
||||
#define _COMPILER_HIPCLASSKERNEL_H_
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
static const int BLOCKS = 512;
|
||||
static const int THREADS_PER_BLOCK = 1;
|
||||
size_t NBOOL = BLOCKS * sizeof(bool);
|
||||
|
||||
class testFuncOvld{
|
||||
public:
|
||||
int __host__ __device__ func1(int a){
|
||||
return a + 10;
|
||||
}
|
||||
|
||||
int __host__ __device__ func1(int a , int b){
|
||||
return a + b + 10;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class testOvrB{
|
||||
public:
|
||||
int __host__ __device__ ovrdFunc1(){
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class testOvrD: public testOvrB{
|
||||
public:
|
||||
int __host__ __device__ ovrdFunc1(){
|
||||
int x = testOvrB::ovrdFunc1();
|
||||
return x + 20;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class testFrndA{
|
||||
private:
|
||||
int fa = 10;
|
||||
public:
|
||||
friend class testFrndB;
|
||||
};
|
||||
|
||||
class testFrndB{
|
||||
public:
|
||||
__host__ __device__ int showA(){
|
||||
testFrndA x;
|
||||
return x.fa;
|
||||
}
|
||||
};
|
||||
|
||||
class testClassEmpty {};
|
||||
|
||||
class testPassByValue{
|
||||
public:
|
||||
int exI;
|
||||
char exC;
|
||||
};
|
||||
|
||||
class testSizeA {
|
||||
public:
|
||||
float xa;
|
||||
int ia;
|
||||
double da;
|
||||
static char ca;
|
||||
};
|
||||
|
||||
class testSizeDerived : testSizeA {
|
||||
public:
|
||||
float fd;
|
||||
};
|
||||
|
||||
#pragma pack(push,4)
|
||||
class testSizeDerived2 : testSizeA {
|
||||
public:
|
||||
float fd;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class testSizeB {
|
||||
public:
|
||||
char ab;
|
||||
int ib;
|
||||
char cb;
|
||||
};
|
||||
|
||||
class testSizeVirtDer : public virtual testSizeB {
|
||||
public:
|
||||
int ivd;
|
||||
};
|
||||
|
||||
class testSizeVirtDer1 : public virtual testSizeB {
|
||||
public:
|
||||
int ivd1;
|
||||
};
|
||||
|
||||
class testSizeDerMulti : public testSizeVirtDer, public testSizeVirtDer1 {
|
||||
public:
|
||||
int ivd2;
|
||||
};
|
||||
|
||||
#pragma pack(push,4)
|
||||
class testSizeVirtDerPack : public virtual testSizeB {
|
||||
public:
|
||||
int ivd;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class testSizeC {
|
||||
public:
|
||||
char ac;
|
||||
int ic;
|
||||
int bc[2];
|
||||
};
|
||||
|
||||
class testSizeDV {
|
||||
public:
|
||||
virtual void __host__ __device__ func1();
|
||||
private:
|
||||
int iDV;
|
||||
|
||||
};
|
||||
|
||||
class testSizeDerivedDV : testSizeDV {
|
||||
public:
|
||||
virtual void __host__ __device__ funcD1();
|
||||
private:
|
||||
int iDDV;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class testSizeP1 {
|
||||
public:
|
||||
char ap;
|
||||
int ip;
|
||||
char cp;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class testSizeP2 {
|
||||
public:
|
||||
char ap1;
|
||||
int ip1;
|
||||
int bp1[2];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 2)
|
||||
class testSizeP3 {
|
||||
public:
|
||||
char ap2;
|
||||
int ip2;
|
||||
char cp2;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class testDeviceClass {
|
||||
public:
|
||||
int iVar;
|
||||
__host__ __device__ testDeviceClass();
|
||||
__host__ __device__ testDeviceClass(int a);
|
||||
__host__ __device__ ~testDeviceClass();
|
||||
};
|
||||
|
||||
__host__ __device__
|
||||
testDeviceClass::testDeviceClass() {
|
||||
iVar = 5;
|
||||
}
|
||||
|
||||
__host__ __device__
|
||||
testDeviceClass::testDeviceClass(int a) {
|
||||
iVar = a;
|
||||
}
|
||||
|
||||
bool* AllocateHostMemory(void){
|
||||
bool *result_ech;
|
||||
HIPCHECK(hipHostMalloc(&result_ech,
|
||||
NBOOL,
|
||||
hipHostMallocDefault));
|
||||
return result_ech;
|
||||
}
|
||||
|
||||
bool* AllocateDeviceMemory(void){
|
||||
bool* result_ecd;
|
||||
HIPCHECK(hipMalloc(&result_ecd,
|
||||
NBOOL));
|
||||
HIPCHECK(hipMemset(result_ecd,
|
||||
false,
|
||||
NBOOL));
|
||||
return result_ecd;
|
||||
}
|
||||
|
||||
void VerifyResult(bool* result_ech, bool* result_ecd){
|
||||
HIPCHECK(hipMemcpy(result_ech,
|
||||
result_ecd,
|
||||
BLOCKS*sizeof(bool),
|
||||
hipMemcpyDeviceToHost));
|
||||
// validation on host side
|
||||
for (int i = 0; i < BLOCKS; i++) {
|
||||
HIPASSERT(result_ech[i] == true);
|
||||
}
|
||||
}
|
||||
|
||||
void FreeMem(bool* result_ech, bool* result_ecd){
|
||||
HIPCHECK(hipHostFree(result_ech));
|
||||
HIPCHECK(hipFree(result_ecd));
|
||||
}
|
||||
|
||||
#endif // _HIPCLASSKERNEL_H_
|
||||
@@ -0,0 +1,22 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
hipCGThreadBlockType.cc
|
||||
hipCGThreadBlockTypeViaBaseType.cc
|
||||
hipCGThreadBlockTypeViaPublicApi.cc
|
||||
hipCGMultiGridGroupType.cc
|
||||
hipCGMultiGridGroupTypeViaBaseType.cc
|
||||
hipCGMultiGridGroupTypeViaPublicApi.cc
|
||||
)
|
||||
if(HIP_PLATFORM STREQUAL "nvidia")
|
||||
set_source_files_properties(hipCGMultiGridGroupType.cc PROPERTIES COMPILE_FLAGS "-rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80")
|
||||
set_source_files_properties(hipCGMultiGridGroupTypeViaBaseType.cc PROPERTIES COMPILE_FLAGS "-D_CG_ABI_EXPERIMENTAL -rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80")
|
||||
set_source_files_properties(hipCGMultiGridGroupTypeViaPublicApi.cc PROPERTIES COMPILE_FLAGS "-rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80")
|
||||
hip_add_exe_to_target(NAME coopGrpTest
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME build_tests
|
||||
LINKER_LIBS "-rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80")
|
||||
else()
|
||||
hip_add_exe_to_target(NAME coopGrpTest
|
||||
TEST_SRC ${TEST_SRC}
|
||||
TEST_TARGET_NAME build_tests)
|
||||
endif()
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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.
|
||||
*/
|
||||
// Test Description:
|
||||
/* This test implements sum reduction kernel, first with each threads own rank
|
||||
as input and comparing the sum with expected sum output derieved from n(n-1)/2
|
||||
formula.
|
||||
This sample tests functionality of intrinsics provided by thread_block_tile type,
|
||||
shfl_down and shfl_xor.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_cooperative_groups.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace cooperative_groups;
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) assert(lhs == rhs)
|
||||
#define WAVE_SIZE 32
|
||||
|
||||
__device__ int reduction_kernel_shfl_down(coalesced_group const& g, int val) {
|
||||
int sz = g.size();
|
||||
|
||||
for (int i = sz / 2; i > 0; i >>= 1) {
|
||||
val += g.shfl_down(val, i);
|
||||
}
|
||||
|
||||
// Choose the 0'th indexed thread that holds the reduction value to return
|
||||
if (g.thread_rank() == 0) {
|
||||
return val;
|
||||
}
|
||||
// Rest of the threads return no useful values
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void kernel_shfl_down (int * dPtr, int *dResults, int lane_delta, int cg_sizes) {
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (id % cg_sizes == 0) {
|
||||
coalesced_group const& g = coalesced_threads();
|
||||
int rank = g.thread_rank();
|
||||
int val = dPtr[rank];
|
||||
dResults[rank] = g.shfl_down(val, lane_delta);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void kernel_cg_group_partition(int* result, unsigned int tileSz, int cg_sizes) {
|
||||
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
if (id % cg_sizes == 0) {
|
||||
coalesced_group threadBlockCGTy = coalesced_threads();
|
||||
int input, outputSum, expectedSum;
|
||||
|
||||
// Choose a leader thread to print the results
|
||||
if (threadBlockCGTy.thread_rank() == 0) {
|
||||
printf(" Creating %d groups, of tile size %d threads:\n\n",
|
||||
(int)threadBlockCGTy.size() / tileSz, tileSz);
|
||||
}
|
||||
|
||||
threadBlockCGTy.sync();
|
||||
|
||||
coalesced_group tiledPartition = tiled_partition(threadBlockCGTy, tileSz);
|
||||
int threadRank = tiledPartition.thread_rank();
|
||||
|
||||
input = tiledPartition.thread_rank();
|
||||
|
||||
// (n-1)(n)/2
|
||||
expectedSum = ((tileSz - 1) * tileSz / 2);
|
||||
|
||||
outputSum = reduction_kernel_shfl_down(tiledPartition, input);
|
||||
|
||||
if (tiledPartition.thread_rank() == 0) {
|
||||
printf(
|
||||
" Sum of all ranks 0..%d in this tiledPartition group using shfl_down is %d (expected "
|
||||
"%d)\n",
|
||||
tiledPartition.size() - 1, outputSum, expectedSum);
|
||||
result[threadBlockCGTy.thread_rank() / (tileSz)] = outputSum;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void verifyResults(int* ptr, int expectedResult, int numTiles) {
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
if (ptr[i] != expectedResult) {
|
||||
INFO(" Results do not match! ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void compareResults(int* cpu, int* gpu, int size) {
|
||||
for (unsigned int i = 0; i < size / sizeof(int); i++) {
|
||||
if (cpu[i] != gpu[i]) {
|
||||
INFO(" results do not match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printResults(int* ptr, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
std::cout << ptr[i] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
static void test_group_partition(unsigned int tileSz) {
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
int threadsPerBlock = 32;
|
||||
|
||||
std::vector<unsigned int> cg_sizes = {1, 2, 3};
|
||||
for (auto i : cg_sizes) {
|
||||
|
||||
int numTiles = ((blockSize * threadsPerBlock) / i) / tileSz;
|
||||
int expectedSum = ((tileSz - 1) * tileSz / 2);
|
||||
int* expectedResult = new int[numTiles];
|
||||
|
||||
// numTiles = 0 when partitioning is possible. The below statement is to avoid
|
||||
// out-of-bounds error and still evaluate failure case.
|
||||
numTiles = (numTiles == 0) ? 1 : numTiles;
|
||||
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
expectedResult[i] = expectedSum;
|
||||
}
|
||||
|
||||
int* dResult = NULL;
|
||||
int* hResult = NULL;
|
||||
|
||||
hipHostMalloc(&hResult, numTiles * sizeof(int), hipHostMallocDefault);
|
||||
memset(hResult, 0, numTiles * sizeof(int));
|
||||
|
||||
hipMalloc(&dResult, numTiles * sizeof(int));
|
||||
|
||||
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_cg_group_partition, blockSize, threadsPerBlock,
|
||||
threadsPerBlock * sizeof(int), 0, dResult, tileSz, i);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
|
||||
|
||||
hipMemcpy(hResult, dResult, sizeof(int) * numTiles, hipMemcpyDeviceToHost);
|
||||
|
||||
verifyResults(hResult, expectedSum, numTiles);
|
||||
|
||||
// Free all allocated memory on host and device
|
||||
hipFree(dResult);
|
||||
hipFree(hResult);
|
||||
delete[] expectedResult;
|
||||
|
||||
printf("\n...PASSED.\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_shfl_down() {
|
||||
|
||||
std::vector<unsigned int> cg_sizes = {1, 2, 3};
|
||||
for (auto i : cg_sizes) {
|
||||
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
int threadsPerBlock = WAVE_SIZE;
|
||||
|
||||
int totalThreads = blockSize * threadsPerBlock;
|
||||
int group_size = totalThreads / i;
|
||||
int group_size_in_bytes = group_size * sizeof(int);
|
||||
|
||||
int* hPtr = NULL;
|
||||
int* dPtr = NULL;
|
||||
int* dResults = NULL;
|
||||
int lane_delta = rand() % group_size;
|
||||
std::cout << "Testing coalesced_groups shfl_down with lane_delta " << lane_delta << "and group size "
|
||||
<< WAVE_SIZE << '\n' << std::endl;
|
||||
|
||||
int arrSize = blockSize * threadsPerBlock * sizeof(int);
|
||||
|
||||
hipHostMalloc(&hPtr, arrSize);
|
||||
// Fill up the array
|
||||
for (int i = 0; i < WAVE_SIZE; i++) {
|
||||
hPtr[i] = rand() % 1000;
|
||||
}
|
||||
|
||||
int* cpuResultsArr = (int*)malloc(group_size_in_bytes);
|
||||
for (int i = 0; i < group_size; i++) {
|
||||
cpuResultsArr[i] = (i + lane_delta >= group_size) ? hPtr[i] : hPtr[i + lane_delta];
|
||||
}
|
||||
//printf("Array passed to GPU for computation\n");
|
||||
//printResults(hPtr, WAVE_SIZE);
|
||||
hipMalloc(&dPtr, group_size_in_bytes);
|
||||
hipMalloc(&dResults, group_size_in_bytes);
|
||||
|
||||
hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice);
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_shfl_down, blockSize, threadsPerBlock,
|
||||
threadsPerBlock * sizeof(int), 0, dPtr, dResults, lane_delta, i);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost);
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
//printf("GPU results: \n");
|
||||
//printResults(hPtr, WAVE_SIZE);
|
||||
//printf("Printing cpu to be verified array\n");
|
||||
//printResults(cpuResultsArr, WAVE_SIZE);
|
||||
|
||||
compareResults(hPtr, cpuResultsArr, group_size_in_bytes);
|
||||
std::cout << "Results verified!\n";
|
||||
|
||||
hipFree(hPtr);
|
||||
hipFree(dPtr);
|
||||
free(cpuResultsArr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE("Unit_coalesced_groups_shfl_down") {
|
||||
// Use default device for validating the test
|
||||
int deviceId;
|
||||
ASSERT_EQUAL(hipGetDevice(&deviceId), hipSuccess);
|
||||
hipDeviceProp_t deviceProperties;
|
||||
ASSERT_EQUAL(hipGetDeviceProperties(&deviceProperties, deviceId), hipSuccess);
|
||||
int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock;
|
||||
|
||||
// Test shfl_down with random group sizes
|
||||
for (int i = 0; i < 100; i++) {
|
||||
test_shfl_down();
|
||||
}
|
||||
|
||||
std::cout << "Testing static tiled_partition for different tile sizes using shfl_down"
|
||||
<< std::endl;
|
||||
|
||||
int testNo = 1;
|
||||
std::vector<unsigned int> tileSizes = {2, 4, 8, 16, 32};
|
||||
for (auto i : tileSizes) {
|
||||
std::cout << "TEST " << testNo << ":" << '\n' << std::endl;
|
||||
test_group_partition(i);
|
||||
testNo++;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
// Test Description:
|
||||
/* This test implements prefix sum(scan) kernel, first with each threads own rank
|
||||
as input and comparing the sum with expected serial summation output on CPU.
|
||||
|
||||
This sample tests functionality of intrinsics provided by coalesced_group,
|
||||
shfl_up.
|
||||
*/
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_cooperative_groups.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace cooperative_groups;
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) assert(lhs == rhs)
|
||||
#define WAVE_SIZE 32
|
||||
__device__ int prefix_sum_kernel(coalesced_group const& g, int val) {
|
||||
int sz = g.size();
|
||||
for (int i = 1; i < sz; i <<= 1) {
|
||||
int temp = g.shfl_up(val, i);
|
||||
|
||||
if (g.thread_rank() >= i) {
|
||||
val += temp;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
__global__ void kernel_shfl_up (int * dPtr, int *dResults, int lane_delta, int cg_sizes) {
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (id % cg_sizes == 0) {
|
||||
coalesced_group g = coalesced_threads();
|
||||
int rank = g.thread_rank();
|
||||
int val = dPtr[rank];
|
||||
dResults[rank] = g.shfl_up(val, lane_delta);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__global__ void kernel_cg_group_partition(int* dPtr, unsigned int tileSz, int cg_sizes) {
|
||||
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
if (id % cg_sizes == 0) {
|
||||
coalesced_group threadBlockCGTy = coalesced_threads();
|
||||
int input, outputSum;
|
||||
|
||||
// we pass its own thread rank as inputs
|
||||
input = threadBlockCGTy.thread_rank();
|
||||
|
||||
// Choose a leader thread to print the results
|
||||
if (threadBlockCGTy.thread_rank() == 0) {
|
||||
printf(" Creating %d groups, of tile size %d threads:\n\n",
|
||||
(int)threadBlockCGTy.size() / tileSz, tileSz);
|
||||
}
|
||||
|
||||
threadBlockCGTy.sync();
|
||||
|
||||
coalesced_group tiledPartition = tiled_partition(threadBlockCGTy, tileSz);
|
||||
|
||||
input = tiledPartition.thread_rank();
|
||||
|
||||
outputSum = prefix_sum_kernel(tiledPartition, input);
|
||||
|
||||
// Update the result array with the corresponsing prefix sum
|
||||
dPtr[threadBlockCGTy.thread_rank()] = outputSum;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void serialScan(int* ptr, int size) {
|
||||
// Fill up the array
|
||||
for (int i = 0; i < size; i++) {
|
||||
ptr[i] = i;
|
||||
}
|
||||
|
||||
int acc = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
acc = acc + ptr[i];
|
||||
ptr[i] = acc;
|
||||
}
|
||||
}
|
||||
|
||||
void printResults(int* ptr, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
std::cout << ptr[i] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
void verifyResults(int* cpu, int* gpu, int size) {
|
||||
for (unsigned int i = 0; i < size / sizeof(int); i++) {
|
||||
if (cpu[i] != gpu[i]) {
|
||||
INFO(" Results do not match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_group_partition(unsigned tileSz) {
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
int threadsPerBlock = WAVE_SIZE;
|
||||
|
||||
int* hPtr = NULL;
|
||||
int* dPtr = NULL;
|
||||
int* cpuPrefixSum = NULL;
|
||||
|
||||
std::vector<unsigned int> cg_sizes = {1, 2, 3};
|
||||
for (auto i : cg_sizes) {
|
||||
|
||||
int arrSize = blockSize * threadsPerBlock * sizeof(int);
|
||||
|
||||
hipHostMalloc(&hPtr, arrSize);
|
||||
hipMalloc(&dPtr, arrSize);
|
||||
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_cg_group_partition, blockSize, threadsPerBlock,
|
||||
threadsPerBlock * sizeof(int), 0, dPtr, tileSz, i);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipMemcpy(hPtr, dPtr, arrSize, hipMemcpyDeviceToHost);
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
|
||||
cpuPrefixSum = new int[tileSz];
|
||||
serialScan(cpuPrefixSum, tileSz);
|
||||
//std::cout << "\nPrefix sum results on CPU\n";
|
||||
//printResults(cpuPrefixSum, tileSz);
|
||||
|
||||
//std::cout << "\nPrefix sum results on GPU\n";
|
||||
//printResults(hPtr, tileSz);
|
||||
std::cout << "\n";
|
||||
verifyResults(hPtr, cpuPrefixSum, tileSz);
|
||||
std::cout << "Results verified!\n";
|
||||
|
||||
delete[] cpuPrefixSum;
|
||||
hipFree(hPtr);
|
||||
hipFree(dPtr);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_shfl_up() {
|
||||
|
||||
std::vector<unsigned int> cg_sizes = {1, 2, 3};
|
||||
for (auto i : cg_sizes) {
|
||||
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
|
||||
int threadsPerBlock = WAVE_SIZE;
|
||||
int totalThreads = blockSize * threadsPerBlock;
|
||||
int group_size = totalThreads / i;
|
||||
int group_size_in_bytes = group_size * sizeof(int);
|
||||
|
||||
int* hPtr = NULL;
|
||||
int* dPtr = NULL;
|
||||
int* dResults = NULL;
|
||||
int lane_delta = (rand() % group_size);
|
||||
|
||||
std::cout << "Testing coalesced_groups shfl_up with lane_delta " << lane_delta
|
||||
<< " and group size " << WAVE_SIZE << '\n' << std::endl;
|
||||
|
||||
int arrSize = blockSize * threadsPerBlock * sizeof(int);
|
||||
|
||||
hipHostMalloc(&hPtr, arrSize);
|
||||
// Fill up the array
|
||||
for (int i = 0; i < WAVE_SIZE; i++) {
|
||||
hPtr[i] = rand() % 1000;
|
||||
}
|
||||
//printResults(hPtr, WAVE_SIZE);
|
||||
|
||||
int* cpuResultsArr = (int*)malloc(group_size_in_bytes);
|
||||
for (int i = 0; i < group_size; i++) {
|
||||
cpuResultsArr[i] = (i <= (lane_delta - 1)) ? hPtr[i] : hPtr[i - lane_delta];
|
||||
}
|
||||
|
||||
//printf("Printing cpu results arr\n");
|
||||
//printResults(cpuResultsArr, WAVE_SIZE);
|
||||
|
||||
hipMalloc(&dPtr, group_size_in_bytes);
|
||||
hipMalloc(&dResults, group_size_in_bytes);
|
||||
|
||||
hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice);
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_shfl_up, blockSize, threadsPerBlock,
|
||||
threadsPerBlock * sizeof(int), 0, dPtr, dResults, lane_delta, i);
|
||||
hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
//printf("GPU computation array :\n");
|
||||
//printResults(hPtr, WAVE_SIZE);
|
||||
|
||||
verifyResults(hPtr, cpuResultsArr, group_size_in_bytes);
|
||||
std::cout << "Results verified!\n";
|
||||
|
||||
hipFree(hPtr);
|
||||
hipFree(dPtr);
|
||||
free(cpuResultsArr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_coalesced_groups_shfl_down") {
|
||||
// Use default device for validating the test
|
||||
int deviceId;
|
||||
ASSERT_EQUAL(hipGetDevice(&deviceId), hipSuccess);
|
||||
hipDeviceProp_t deviceProperties;
|
||||
ASSERT_EQUAL(hipGetDeviceProperties(&deviceProperties, deviceId), hipSuccess);
|
||||
int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock;
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
test_shfl_up();
|
||||
}
|
||||
|
||||
std::cout << "Testing coalesced_groups partitioning and shfl_up" << '\n' << std::endl;
|
||||
|
||||
int testNo = 1;
|
||||
std::vector<unsigned int> tileSizes = {2, 4, 8, 16, 32};
|
||||
for (auto i : tileSizes) {
|
||||
std::cout << "TEST " << testNo << ":" << '\n' << std::endl;
|
||||
test_group_partition(i);
|
||||
testNo++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Kogge-Stone algorithm */
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
|
||||
/* HIT_START
|
||||
* BUILD: %t %s ../../test_common.cpp NVCC_OPTIONS --std=c++11 -rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80
|
||||
* TEST: %t
|
||||
* HIT_END
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_cooperative_groups.h>
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) HIPASSERT(lhs == rhs)
|
||||
#define ASSERT_LE(lhs, rhs) HIPASSERT(lhs <= rhs)
|
||||
#define ASSERT_GE(lhs, rhs) HIPASSERT(lhs >= rhs)
|
||||
|
||||
using namespace cooperative_groups;
|
||||
constexpr int MaxGPUs = 8;
|
||||
|
||||
static __global__
|
||||
void kernel_cg_multi_grid_group_type(int* numGridsTestD,
|
||||
int* gridRankTestD,
|
||||
int *sizeTestD,
|
||||
int *thdRankTestD,
|
||||
int *isValidTestD,
|
||||
int *syncTestD,
|
||||
int *syncResultD)
|
||||
{
|
||||
multi_grid_group mg = this_multi_grid();
|
||||
int gIdx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
|
||||
// Test num_grids
|
||||
numGridsTestD[gIdx] = mg.num_grids();
|
||||
|
||||
// Test grid_rank
|
||||
gridRankTestD[gIdx] = mg.grid_rank();
|
||||
|
||||
// Test size
|
||||
sizeTestD[gIdx] = mg.size();
|
||||
|
||||
// Test thread_rank
|
||||
thdRankTestD[gIdx] = mg.thread_rank();
|
||||
|
||||
// Test is_valid
|
||||
isValidTestD[gIdx] = mg.is_valid();
|
||||
|
||||
// Test sync
|
||||
//
|
||||
// Eech thread assign 1 to their respective location
|
||||
syncTestD[gIdx] = 1;
|
||||
// Grid level sync
|
||||
this_grid().sync();
|
||||
// Thread 0 from work-group 0 of current grid (gpu) does grid level reduction
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (uint i = 1; i < gridDim.x * blockDim.x; ++i) {
|
||||
syncTestD[0] += syncTestD[i];
|
||||
}
|
||||
syncResultD[mg.grid_rank() + 1] = syncTestD[0];
|
||||
}
|
||||
// multi-grid level sync
|
||||
mg.sync();
|
||||
// grid (gpu) 0 does final reduction across all grids (gpus)
|
||||
if (mg.grid_rank() == 0 && blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
syncResultD[0] = 0;
|
||||
for (uint i = 1; i <= mg.num_grids(); ++i) {
|
||||
syncResultD[0] += syncResultD[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_cg_multi_grid_group_type(int blockSize, int nGpu)
|
||||
{
|
||||
// Create a stream each device
|
||||
hipStream_t stream[MaxGPUs];
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
HIPCHECK(hipDeviceSynchronize()); // Make sure work is done on this device
|
||||
HIPCHECK(hipStreamCreate(&stream[i]));
|
||||
}
|
||||
|
||||
// Allocate host and device memory
|
||||
int nBytes = sizeof(int) * 2 * blockSize;
|
||||
int *numGridsTestD[MaxGPUs], *numGridsTestH[MaxGPUs];
|
||||
int *gridRankTestD[MaxGPUs], *gridRankTestH[MaxGPUs];
|
||||
int *sizeTestD[MaxGPUs], *sizeTestH[MaxGPUs];
|
||||
int *thdRankTestD[MaxGPUs], *thdRankTestH[MaxGPUs];
|
||||
int *isValidTestD[MaxGPUs], *isValidTestH[MaxGPUs];
|
||||
int *syncTestD[MaxGPUs], *syncResultD;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
HIPCHECK(hipMalloc(&numGridsTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&gridRankTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&sizeTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&thdRankTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&isValidTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&syncTestD[i], nBytes));
|
||||
|
||||
HIPCHECK(hipHostMalloc(&numGridsTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&gridRankTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&sizeTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&thdRankTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&isValidTestH[i], nBytes));
|
||||
|
||||
if (i == 0) {
|
||||
HIPCHECK(hipHostMalloc(&syncResultD, sizeof(int) * (nGpu + 1), hipHostMallocCoherent));
|
||||
}
|
||||
}
|
||||
|
||||
// Launch Kernel
|
||||
constexpr int NumKernelArgs = 7;
|
||||
hipLaunchParams* launchParamsList = new hipLaunchParams[nGpu];
|
||||
void* args[MaxGPUs * NumKernelArgs];
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
args[i * NumKernelArgs] = &numGridsTestD[i];
|
||||
args[i * NumKernelArgs + 1] = &gridRankTestD[i];
|
||||
args[i * NumKernelArgs + 2] = &sizeTestD[i];
|
||||
args[i * NumKernelArgs + 3] = &thdRankTestD[i];
|
||||
args[i * NumKernelArgs + 4] = &isValidTestD[i];
|
||||
args[i * NumKernelArgs + 5] = &syncTestD[i];
|
||||
args[i * NumKernelArgs + 6] = &syncResultD;
|
||||
|
||||
launchParamsList[i].func = reinterpret_cast<void*>(kernel_cg_multi_grid_group_type);
|
||||
launchParamsList[i].gridDim = 2;
|
||||
launchParamsList[i].blockDim = blockSize;
|
||||
launchParamsList[i].sharedMem = 0;
|
||||
launchParamsList[i].stream = stream[i];
|
||||
launchParamsList[i].args = &args[i * NumKernelArgs];
|
||||
}
|
||||
HIPCHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList, nGpu, 0));
|
||||
|
||||
// Copy result from device to host
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
HIPCHECK(hipMemcpy(numGridsTestH[i], numGridsTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(gridRankTestH[i], gridRankTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(sizeTestH[i], sizeTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(thdRankTestH[i], thdRankTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(isValidTestH[i], isValidTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
}
|
||||
|
||||
// Validate results
|
||||
int gridsSeen[MaxGPUs];
|
||||
for (int i = 0; i < nGpu; ++i) {
|
||||
for (int j = 0; j < 2 * blockSize; ++j) {
|
||||
ASSERT_EQUAL(numGridsTestH[i][j], nGpu);
|
||||
ASSERT_GE(gridRankTestH[i][j], 0);
|
||||
ASSERT_LE(gridRankTestH[i][j], nGpu-1);
|
||||
ASSERT_EQUAL(gridRankTestH[i][j], gridRankTestH[i][0]);
|
||||
ASSERT_EQUAL(sizeTestH[i][j], nGpu * 2 * blockSize);
|
||||
int gridRank = gridRankTestH[i][j];
|
||||
ASSERT_EQUAL(thdRankTestH[i][j], (gridRank * 2 * blockSize) + j);
|
||||
ASSERT_EQUAL(isValidTestH[i][j], 1);
|
||||
}
|
||||
ASSERT_EQUAL(syncResultD[i+1], 2 * blockSize);
|
||||
|
||||
// Validate uniqueness property of grid rank
|
||||
gridsSeen[i] = gridRankTestH[i][0];
|
||||
for (int k = 0; k < i; ++k) {
|
||||
if (gridsSeen[k] == gridsSeen[i]) {
|
||||
assert(false && "Grid rank in multi-gpu setup should be unique");
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_EQUAL(syncResultD[0], nGpu * 2 * blockSize);
|
||||
|
||||
// Free host and device memory
|
||||
delete [] launchParamsList;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
HIPCHECK(hipFree(numGridsTestD[i]));
|
||||
HIPCHECK(hipFree(gridRankTestD[i]));
|
||||
HIPCHECK(hipFree(sizeTestD[i]));
|
||||
HIPCHECK(hipFree(thdRankTestD[i]));
|
||||
HIPCHECK(hipFree(isValidTestD[i]));
|
||||
HIPCHECK(hipFree(syncTestD[i]));
|
||||
|
||||
if (i == 0) {
|
||||
HIPCHECK(hipHostFree(syncResultD));
|
||||
}
|
||||
HIPCHECK(hipHostFree(numGridsTestH[i]));
|
||||
HIPCHECK(hipHostFree(gridRankTestH[i]));
|
||||
HIPCHECK(hipHostFree(sizeTestH[i]));
|
||||
HIPCHECK(hipHostFree(thdRankTestH[i]));
|
||||
HIPCHECK(hipHostFree(isValidTestH[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipCGMultiGridGroupType") {
|
||||
int nGpu = 0;
|
||||
HIPCHECK(hipGetDeviceCount(&nGpu));
|
||||
nGpu = min(nGpu, MaxGPUs);
|
||||
|
||||
// Set `maxThreadsPerBlock` by taking minimum among all available devices
|
||||
int maxThreadsPerBlock = INT_MAX;
|
||||
hipDeviceProp_t deviceProperties;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipGetDeviceProperties(&deviceProperties, i));
|
||||
if (!deviceProperties.cooperativeMultiDeviceLaunch) {
|
||||
HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!");
|
||||
return;
|
||||
}
|
||||
maxThreadsPerBlock = min(maxThreadsPerBlock, deviceProperties.maxThreadsPerBlock);
|
||||
}
|
||||
|
||||
// Test for blockSizes in powers of 2
|
||||
for (int blockSize = 2; blockSize <= maxThreadsPerBlock; blockSize = blockSize*2) {
|
||||
test_cg_multi_grid_group_type(blockSize, nGpu);
|
||||
}
|
||||
|
||||
// Test for random blockSizes, but the sequence is the same every execution
|
||||
srand(0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Test fails for 0 thread per block
|
||||
test_cg_multi_grid_group_type(max(2, rand() % maxThreadsPerBlock), nGpu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
|
||||
/* HIT_START
|
||||
* BUILD: %t %s ../../test_common.cpp NVCC_OPTIONS --std=c++11 -D_CG_ABI_EXPERIMENTAL -rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80
|
||||
* TEST: %t
|
||||
* HIT_END
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_cooperative_groups.h>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <climits>
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) HIPASSERT(lhs == rhs)
|
||||
#define ASSERT_LE(lhs, rhs) HIPASSERT(lhs <= rhs)
|
||||
#define ASSERT_GE(lhs, rhs) HIPASSERT(lhs >= rhs)
|
||||
|
||||
using namespace cooperative_groups;
|
||||
constexpr int MaxGPUs = 8;
|
||||
|
||||
static __global__
|
||||
void kernel_cg_multi_grid_group_type_via_base_type(int *sizeTestD,
|
||||
int* gridRankTestD,
|
||||
int *thdRankTestD,
|
||||
int *isValidTestD,
|
||||
int *syncTestD,
|
||||
int *syncResultD)
|
||||
{
|
||||
thread_group tg = this_multi_grid(); // This can work if _CG_ABI_EXPERIMENTAL defined on Cuda
|
||||
|
||||
int gIdx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
|
||||
// Test size
|
||||
sizeTestD[gIdx] = tg.size();
|
||||
|
||||
// Test thread_rank
|
||||
gridRankTestD[gIdx] = this_multi_grid().grid_rank();
|
||||
thdRankTestD[gIdx] = tg.thread_rank();
|
||||
|
||||
// Test is_valid
|
||||
#ifdef __HIP_PLATFORM_AMD__
|
||||
isValidTestD[gIdx] = tg.is_valid();
|
||||
#else
|
||||
// Cuda has no thread_group.is_valid()
|
||||
isValidTestD[gIdx] = true;
|
||||
#endif
|
||||
// Test sync
|
||||
//
|
||||
// Eech thread assign 1 to their respective location
|
||||
syncTestD[gIdx] = 1;
|
||||
// Grid level sync
|
||||
this_grid().sync();
|
||||
// Thread 0 from work-group 0 of current grid (gpu) does grid level reduction
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (uint i = 1; i < gridDim.x * blockDim.x; ++i) {
|
||||
syncTestD[0] += syncTestD[i];
|
||||
}
|
||||
syncResultD[this_multi_grid().grid_rank() + 1] = syncTestD[0];
|
||||
}
|
||||
// multi-grid level sync
|
||||
tg.sync();
|
||||
// grid (gpu) 0 does final reduction across all grids (gpus)
|
||||
if (this_multi_grid().grid_rank() == 0 && blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
syncResultD[0] = 0;
|
||||
for (uint i = 1; i <= this_multi_grid().num_grids(); ++i) {
|
||||
syncResultD[0] += syncResultD[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_cg_multi_grid_group_type_via_base_type(int blockSize, int nGpu)
|
||||
{
|
||||
// Create a stream each device
|
||||
hipStream_t stream[MaxGPUs];
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
HIPCHECK(hipDeviceSynchronize()); // Make sure work is done on this device
|
||||
HIPCHECK(hipStreamCreate(&stream[i]));
|
||||
}
|
||||
|
||||
// Allocate host and device memory
|
||||
int nBytes = sizeof(int) * 2 * blockSize;
|
||||
int *sizeTestD[MaxGPUs], *sizeTestH[MaxGPUs];
|
||||
int *gridRankTestD[MaxGPUs], *gridRankTestH[MaxGPUs];
|
||||
int *thdRankTestD[MaxGPUs], *thdRankTestH[MaxGPUs];
|
||||
int *isValidTestD[MaxGPUs], *isValidTestH[MaxGPUs];
|
||||
int *syncTestD[MaxGPUs], *syncResultD;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
HIPCHECK(hipMalloc(&sizeTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&gridRankTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&thdRankTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&isValidTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&syncTestD[i], nBytes));
|
||||
|
||||
HIPCHECK(hipHostMalloc(&sizeTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&gridRankTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&thdRankTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&isValidTestH[i], nBytes));
|
||||
|
||||
if (i == 0) {
|
||||
HIPCHECK(hipHostMalloc(&syncResultD, sizeof(int) * (nGpu + 1), hipHostMallocCoherent));
|
||||
}
|
||||
}
|
||||
|
||||
// Launch Kernel
|
||||
constexpr int NumKernelArgs = 6;
|
||||
hipLaunchParams* launchParamsList = new hipLaunchParams[nGpu];
|
||||
void* args[MaxGPUs * NumKernelArgs];
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
args[i * NumKernelArgs ] = &sizeTestD[i];
|
||||
args[i * NumKernelArgs + 1] = &gridRankTestD[i];
|
||||
args[i * NumKernelArgs + 2] = &thdRankTestD[i];
|
||||
args[i * NumKernelArgs + 3] = &isValidTestD[i];
|
||||
args[i * NumKernelArgs + 4] = &syncTestD[i];
|
||||
args[i * NumKernelArgs + 5] = &syncResultD;
|
||||
|
||||
launchParamsList[i].func = reinterpret_cast<void*>(kernel_cg_multi_grid_group_type_via_base_type);
|
||||
launchParamsList[i].gridDim = 2;
|
||||
launchParamsList[i].blockDim = blockSize;
|
||||
launchParamsList[i].sharedMem = 0;
|
||||
launchParamsList[i].stream = stream[i];
|
||||
launchParamsList[i].args = &args[i * NumKernelArgs];
|
||||
}
|
||||
HIPCHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList, nGpu, 0));
|
||||
|
||||
// Copy result from device to host
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
HIPCHECK(hipMemcpy(sizeTestH[i], sizeTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(gridRankTestH[i], gridRankTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(thdRankTestH[i], thdRankTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(isValidTestH[i], isValidTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
}
|
||||
|
||||
// Validate results
|
||||
int gridsSeen[MaxGPUs];
|
||||
for (int i = 0; i < nGpu; ++i) {
|
||||
for (int j = 0; j < 2 * blockSize; ++j) {
|
||||
ASSERT_EQUAL(sizeTestH[i][j], nGpu * 2 * blockSize);
|
||||
ASSERT_GE(gridRankTestH[i][j], 0);
|
||||
ASSERT_LE(gridRankTestH[i][j], nGpu-1);
|
||||
ASSERT_EQUAL(gridRankTestH[i][j], gridRankTestH[i][0]);
|
||||
int gridRank = gridRankTestH[i][j];
|
||||
ASSERT_EQUAL(thdRankTestH[i][j], (gridRank * 2 * blockSize) + j);
|
||||
ASSERT_EQUAL(isValidTestH[i][j], 1);
|
||||
}
|
||||
ASSERT_EQUAL(syncResultD[i+1], 2 * blockSize);
|
||||
|
||||
// Validate uniqueness property of grid rank
|
||||
gridsSeen[i] = gridRankTestH[i][0];
|
||||
for (int k = 0; k < i; ++k) {
|
||||
if (gridsSeen[k] == gridsSeen[i]) {
|
||||
assert (false && "Grid rank in multi-gpu setup should be unique");
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_EQUAL(syncResultD[0], nGpu * 2 * blockSize);
|
||||
|
||||
// Free host and device memory
|
||||
delete [] launchParamsList;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
HIPCHECK(hipFree(sizeTestD[i]));
|
||||
HIPCHECK(hipFree(gridRankTestD[i]));
|
||||
HIPCHECK(hipFree(thdRankTestD[i]));
|
||||
HIPCHECK(hipFree(isValidTestD[i]));
|
||||
HIPCHECK(hipFree(syncTestD[i]));
|
||||
|
||||
if (i == 0)
|
||||
HIPCHECK(hipHostFree(syncResultD));
|
||||
|
||||
HIPCHECK(hipHostFree(sizeTestH[i]));
|
||||
HIPCHECK(hipHostFree(gridRankTestH[i]));
|
||||
HIPCHECK(hipHostFree(thdRankTestH[i]));
|
||||
HIPCHECK(hipHostFree(isValidTestH[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipCGMultiGridGroupType_BaseType") {
|
||||
// Set `maxThreadsPerBlock` by taking minimum among all available devices
|
||||
int nGpu = 0;
|
||||
HIPCHECK(hipGetDeviceCount(&nGpu));
|
||||
nGpu = min(nGpu, MaxGPUs);
|
||||
|
||||
int maxThreadsPerBlock = INT_MAX;
|
||||
hipDeviceProp_t deviceProperties;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipGetDeviceProperties(&deviceProperties, i));
|
||||
if (!deviceProperties.cooperativeMultiDeviceLaunch) {
|
||||
HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!");
|
||||
return;
|
||||
}
|
||||
maxThreadsPerBlock = min(maxThreadsPerBlock, deviceProperties.maxThreadsPerBlock);
|
||||
}
|
||||
|
||||
// Test for blockSizes in powers of 2
|
||||
for (int blockSize = 2; blockSize <= maxThreadsPerBlock; blockSize = blockSize*2) {
|
||||
test_cg_multi_grid_group_type_via_base_type(blockSize, nGpu);
|
||||
}
|
||||
|
||||
// Test for random blockSizes, but the sequence is the same every execution
|
||||
srand(0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Test fails for 0 thread per block
|
||||
test_cg_multi_grid_group_type_via_base_type(max(2, rand() % maxThreadsPerBlock), nGpu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
|
||||
/* HIT_START
|
||||
* BUILD: %t %s ../../test_common.cpp NVCC_OPTIONS --std=c++11 -rdc=true -gencode arch=compute_60,code=sm_60 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_80,code=sm_80
|
||||
* TEST: %t
|
||||
* HIT_END
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_cooperative_groups.h>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <climits>
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) HIPASSERT(lhs == rhs)
|
||||
#define ASSERT_LE(lhs, rhs) HIPASSERT(lhs <= rhs)
|
||||
#define ASSERT_GE(lhs, rhs) HIPASSERT(lhs >= rhs)
|
||||
|
||||
using namespace cooperative_groups;
|
||||
constexpr int MaxGPUs = 8;
|
||||
|
||||
static __global__
|
||||
void kernel_cg_multi_grid_group_type_via_public_api(int *sizeTestD,
|
||||
int* gridRankTestD,
|
||||
int *thdRankTestD,
|
||||
int *isValidTestD,
|
||||
int *syncTestD,
|
||||
int *syncResultD)
|
||||
{
|
||||
multi_grid_group mg = this_multi_grid();
|
||||
int gIdx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
|
||||
// Test group_size api
|
||||
sizeTestD[gIdx] = group_size(mg);
|
||||
|
||||
// Test thread_rank api
|
||||
gridRankTestD[gIdx] = this_multi_grid().grid_rank();
|
||||
thdRankTestD[gIdx] = thread_rank(mg);
|
||||
|
||||
// Test is_valid api
|
||||
isValidTestD[gIdx] = mg.is_valid();
|
||||
|
||||
// Test sync api
|
||||
//
|
||||
// Eech thread assign 1 to their respective location
|
||||
syncTestD[gIdx] = 1;
|
||||
// Grid level sync
|
||||
sync(this_grid());
|
||||
// Thread 0 from work-group 0 of current grid (gpu) does grid level reduction
|
||||
if (blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
for (uint i = 1; i < gridDim.x * blockDim.x; ++i) {
|
||||
syncTestD[0] += syncTestD[i];
|
||||
}
|
||||
syncResultD[this_multi_grid().grid_rank() + 1] = syncTestD[0];
|
||||
}
|
||||
// multi-grid level sync via public api
|
||||
sync(mg);
|
||||
// grid (gpu) 0 does final reduction across all grids (gpus)
|
||||
if (this_multi_grid().grid_rank() == 0 && blockIdx.x == 0 && threadIdx.x == 0) {
|
||||
syncResultD[0] = 0;
|
||||
for (uint i = 1; i <= this_multi_grid().num_grids(); ++i) {
|
||||
syncResultD[0] += syncResultD[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_cg_multi_grid_group_type_via_public_api(int blockSize, int nGpu)
|
||||
{
|
||||
// Create a stream each device
|
||||
hipStream_t stream[MaxGPUs];
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
HIPCHECK(hipDeviceSynchronize()); // Make sure work is done on this device
|
||||
HIPCHECK(hipStreamCreate(&stream[i]));
|
||||
}
|
||||
|
||||
// Allocate host and device memory
|
||||
int nBytes = sizeof(int) * 2 * blockSize;
|
||||
int *sizeTestD[MaxGPUs], *sizeTestH[MaxGPUs];
|
||||
int *gridRankTestD[MaxGPUs], *gridRankTestH[MaxGPUs];
|
||||
int *thdRankTestD[MaxGPUs], *thdRankTestH[MaxGPUs];
|
||||
int *isValidTestD[MaxGPUs], *isValidTestH[MaxGPUs];
|
||||
int *syncTestD[MaxGPUs], *syncResultD;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
HIPCHECK(hipMalloc(&sizeTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&gridRankTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&thdRankTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&isValidTestD[i], nBytes));
|
||||
HIPCHECK(hipMalloc(&syncTestD[i], nBytes));
|
||||
|
||||
HIPCHECK(hipHostMalloc(&sizeTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&gridRankTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&thdRankTestH[i], nBytes));
|
||||
HIPCHECK(hipHostMalloc(&isValidTestH[i], nBytes));
|
||||
|
||||
if (i == 0) {
|
||||
HIPCHECK(hipHostMalloc(&syncResultD, sizeof(int) * (nGpu + 1), hipHostMallocCoherent));
|
||||
}
|
||||
}
|
||||
|
||||
// Launch Kernel
|
||||
constexpr int NumKernelArgs = 6;
|
||||
hipLaunchParams* launchParamsList = new hipLaunchParams[nGpu];
|
||||
void* args[MaxGPUs * NumKernelArgs];
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
args[i * NumKernelArgs ] = &sizeTestD[i];
|
||||
args[i * NumKernelArgs + 1] = &gridRankTestD[i];
|
||||
args[i * NumKernelArgs + 2] = &thdRankTestD[i];
|
||||
args[i * NumKernelArgs + 3] = &isValidTestD[i];
|
||||
args[i * NumKernelArgs + 4] = &syncTestD[i];
|
||||
args[i * NumKernelArgs + 5] = &syncResultD;
|
||||
|
||||
launchParamsList[i].func = reinterpret_cast<void*>(kernel_cg_multi_grid_group_type_via_public_api);
|
||||
launchParamsList[i].gridDim = 2;
|
||||
launchParamsList[i].blockDim = blockSize;
|
||||
launchParamsList[i].sharedMem = 0;
|
||||
launchParamsList[i].stream = stream[i];
|
||||
launchParamsList[i].args = &args[i * NumKernelArgs];
|
||||
}
|
||||
HIPCHECK(hipLaunchCooperativeKernelMultiDevice(launchParamsList, nGpu, 0));
|
||||
|
||||
// Copy result from device to host
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
HIPCHECK(hipMemcpy(sizeTestH[i], sizeTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(gridRankTestH[i], gridRankTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(thdRankTestH[i], thdRankTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(isValidTestH[i], isValidTestD[i], nBytes, hipMemcpyDeviceToHost));
|
||||
}
|
||||
|
||||
// Validate results
|
||||
int gridsSeen[MaxGPUs];
|
||||
for (int i = 0; i < nGpu; ++i) {
|
||||
for (int j = 0; j < 2 * blockSize; ++j) {
|
||||
ASSERT_EQUAL(sizeTestH[i][j], nGpu * 2 * blockSize);
|
||||
ASSERT_GE(gridRankTestH[i][j], 0);
|
||||
ASSERT_LE(gridRankTestH[i][j], nGpu-1);
|
||||
ASSERT_EQUAL(gridRankTestH[i][j], gridRankTestH[i][0]);
|
||||
int gridRank = gridRankTestH[i][j];
|
||||
ASSERT_EQUAL(thdRankTestH[i][j], (gridRank * 2 * blockSize) + j);
|
||||
ASSERT_EQUAL(isValidTestH[i][j], 1);
|
||||
}
|
||||
ASSERT_EQUAL(syncResultD[i+1], 2 * blockSize);
|
||||
|
||||
// Validate uniqueness property of grid rank
|
||||
gridsSeen[i] = gridRankTestH[i][0];
|
||||
for (int k = 0; k < i; ++k) {
|
||||
if (gridsSeen[k] == gridsSeen[i]) {
|
||||
assert (false && "Grid rank in multi-gpu setup should be unique");
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_EQUAL(syncResultD[0], nGpu * 2 * blockSize);
|
||||
|
||||
// Free host and device memory
|
||||
delete [] launchParamsList;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipSetDevice(i));
|
||||
|
||||
HIPCHECK(hipFree(sizeTestD[i]));
|
||||
HIPCHECK(hipFree(gridRankTestD[i]));
|
||||
HIPCHECK(hipFree(thdRankTestD[i]));
|
||||
HIPCHECK(hipFree(isValidTestD[i]));
|
||||
HIPCHECK(hipFree(syncTestD[i]));
|
||||
|
||||
if (i == 0)
|
||||
HIPCHECK(hipHostFree(syncResultD));
|
||||
|
||||
HIPCHECK(hipHostFree(sizeTestH[i]));
|
||||
HIPCHECK(hipHostFree(gridRankTestH[i]));
|
||||
HIPCHECK(hipHostFree(thdRankTestH[i]));
|
||||
HIPCHECK(hipHostFree(isValidTestH[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipCGMultiGridGroupType_PublicApi") {
|
||||
// Set `maxThreadsPerBlock` by taking minimum among all available devices
|
||||
int nGpu = 0;
|
||||
HIPCHECK(hipGetDeviceCount(&nGpu));
|
||||
nGpu = min(nGpu, MaxGPUs);
|
||||
|
||||
int maxThreadsPerBlock = INT_MAX;
|
||||
hipDeviceProp_t deviceProperties;
|
||||
for (int i = 0; i < nGpu; i++) {
|
||||
HIPCHECK(hipGetDeviceProperties(&deviceProperties, i));
|
||||
if (!deviceProperties.cooperativeMultiDeviceLaunch) {
|
||||
HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!");
|
||||
return;
|
||||
}
|
||||
maxThreadsPerBlock = min(maxThreadsPerBlock, deviceProperties.maxThreadsPerBlock);
|
||||
}
|
||||
|
||||
// Test for blockSizes in powers of 2
|
||||
for (int blockSize = 2; blockSize <= maxThreadsPerBlock; blockSize = blockSize*2) {
|
||||
test_cg_multi_grid_group_type_via_public_api(blockSize, nGpu);
|
||||
}
|
||||
|
||||
// Test for random blockSizes, but the sequence is the same every execution
|
||||
srand(0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Test fails for 0 thread per block
|
||||
test_cg_multi_grid_group_type_via_public_api(max(2, rand() % maxThreadsPerBlock), nGpu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
|
||||
/* HIT_START
|
||||
* BUILD: %t %s ../../test_common.cpp
|
||||
* TEST: %t
|
||||
* HIT_END
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_cooperative_groups.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) HIPASSERT(lhs == rhs)
|
||||
|
||||
using namespace cooperative_groups;
|
||||
|
||||
static __global__
|
||||
void kernel_cg_thread_block_type(int *sizeTestD,
|
||||
int *thdRankTestD,
|
||||
int *syncTestD,
|
||||
dim3 *groupIndexTestD,
|
||||
dim3 *thdIndexTestD)
|
||||
{
|
||||
thread_block tb = this_thread_block();
|
||||
int gIdx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
// Test size
|
||||
sizeTestD[gIdx] = tb.size();
|
||||
|
||||
// Test thread_rank
|
||||
thdRankTestD[gIdx] = tb.thread_rank();
|
||||
|
||||
// Test sync
|
||||
__shared__ int sm[2];
|
||||
if (threadIdx.x == 0)
|
||||
sm[0] = 10;
|
||||
else if (threadIdx.x == 1)
|
||||
sm[1] = 20;
|
||||
tb.sync();
|
||||
syncTestD[gIdx] = sm[1] * sm[0];
|
||||
|
||||
// Test group_index
|
||||
groupIndexTestD[gIdx] = tb.group_index();
|
||||
|
||||
// Test thread_index
|
||||
thdIndexTestD[gIdx] = tb.thread_index();
|
||||
}
|
||||
|
||||
static void test_cg_thread_block_type(int blockSize)
|
||||
{
|
||||
int nBytes = sizeof(int) * 2 * blockSize;
|
||||
int nDim3Bytes = sizeof(dim3) * 2 * blockSize;
|
||||
int *sizeTestD, *sizeTestH;
|
||||
int *thdRankTestD, *thdRankTestH;
|
||||
int *syncTestD, *syncTestH;
|
||||
dim3 *groupIndexTestD, *groupIndexTestH;
|
||||
dim3 *thdIndexTestD, *thdIndexTestH;
|
||||
|
||||
// Allocate device memory
|
||||
HIPCHECK(hipMalloc(&sizeTestD, nBytes));
|
||||
HIPCHECK(hipMalloc(&thdRankTestD, nBytes));
|
||||
HIPCHECK(hipMalloc(&syncTestD, nBytes));
|
||||
HIPCHECK(hipMalloc(&groupIndexTestD, nDim3Bytes));
|
||||
HIPCHECK(hipMalloc(&thdIndexTestD, nDim3Bytes));
|
||||
|
||||
// Allocate host memory
|
||||
HIPCHECK(hipHostMalloc(&sizeTestH, nBytes));
|
||||
HIPCHECK(hipHostMalloc(&thdRankTestH, nBytes));
|
||||
HIPCHECK(hipHostMalloc(&syncTestH, nBytes));
|
||||
HIPCHECK(hipHostMalloc(&groupIndexTestH, nDim3Bytes));
|
||||
HIPCHECK(hipHostMalloc(&thdIndexTestH, nDim3Bytes));
|
||||
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_cg_thread_block_type,
|
||||
2,
|
||||
blockSize,
|
||||
0,
|
||||
0,
|
||||
sizeTestD,
|
||||
thdRankTestD,
|
||||
syncTestD,
|
||||
groupIndexTestD,
|
||||
thdIndexTestD);
|
||||
|
||||
// Copy result from device to host
|
||||
HIPCHECK(hipMemcpy(sizeTestH, sizeTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(thdRankTestH, thdRankTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(syncTestH, syncTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(groupIndexTestH, groupIndexTestD, nDim3Bytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(thdIndexTestH, thdIndexTestD, nDim3Bytes, hipMemcpyDeviceToHost));
|
||||
|
||||
// Validate results for both blocks together
|
||||
for (int i = 0; i < 2 * blockSize; ++i) {
|
||||
ASSERT_EQUAL(sizeTestH[i], blockSize);
|
||||
ASSERT_EQUAL(thdRankTestH[i], i % blockSize);
|
||||
ASSERT_EQUAL(syncTestH[i], 200);
|
||||
ASSERT_EQUAL(groupIndexTestH[i].x, (uint) i / blockSize);
|
||||
ASSERT_EQUAL(groupIndexTestH[i].y, 0);
|
||||
ASSERT_EQUAL(groupIndexTestH[i].z, 0);
|
||||
ASSERT_EQUAL(thdIndexTestH[i].x, (uint) i % blockSize);
|
||||
ASSERT_EQUAL(thdIndexTestH[i].y, 0);
|
||||
ASSERT_EQUAL(thdIndexTestH[i].z, 0);
|
||||
}
|
||||
|
||||
// Free device memory
|
||||
HIPCHECK(hipFree(sizeTestD));
|
||||
HIPCHECK(hipFree(thdRankTestD));
|
||||
HIPCHECK(hipFree(syncTestD));
|
||||
HIPCHECK(hipFree(groupIndexTestD));
|
||||
HIPCHECK(hipFree(thdIndexTestD));
|
||||
|
||||
//Free host memory
|
||||
HIPCHECK(hipHostFree(sizeTestH));
|
||||
HIPCHECK(hipHostFree(thdRankTestH));
|
||||
HIPCHECK(hipHostFree(syncTestH));
|
||||
HIPCHECK(hipHostFree(groupIndexTestH));
|
||||
HIPCHECK(hipHostFree(thdIndexTestH));
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipCGThreadBlockType") {
|
||||
// Use default device for validating the test
|
||||
int deviceId;
|
||||
hipDeviceProp_t deviceProperties;
|
||||
HIPCHECK(hipGetDevice(&deviceId));
|
||||
HIPCHECK(hipGetDeviceProperties(&deviceProperties, deviceId));
|
||||
|
||||
if (!deviceProperties.cooperativeLaunch) {
|
||||
HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test for blockSizes in powers of 2
|
||||
int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock;
|
||||
for (int blockSize = 2; blockSize <= maxThreadsPerBlock; blockSize = blockSize*2) {
|
||||
test_cg_thread_block_type(blockSize);
|
||||
}
|
||||
|
||||
// Test for random blockSizes, but the sequence is the same every execution
|
||||
srand(0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Test fails for only 1 thread per block
|
||||
test_cg_thread_block_type(max(2, rand() % maxThreadsPerBlock));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
|
||||
/* HIT_START
|
||||
* BUILD: %t %s ../../test_common.cpp
|
||||
* TEST: %t
|
||||
* HIT_END
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include "hip/hip_cooperative_groups.h"
|
||||
#include <cstdlib>
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) assert(lhs == rhs)
|
||||
|
||||
using namespace cooperative_groups;
|
||||
|
||||
static __global__
|
||||
void kernel_cg_thread_block_type_via_base_type(int *sizeTestD,
|
||||
int *thdRankTestD,
|
||||
int *syncTestD)
|
||||
{
|
||||
thread_group tg = this_thread_block();
|
||||
int gIdx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
|
||||
// Test size
|
||||
sizeTestD[gIdx] = tg.size();
|
||||
|
||||
// Test thread_rank
|
||||
thdRankTestD[gIdx] = tg.thread_rank();
|
||||
|
||||
// Test sync
|
||||
__shared__ int sm[2];
|
||||
if (threadIdx.x == 0)
|
||||
sm[0] = 10;
|
||||
else if (threadIdx.x == 1)
|
||||
sm[1] = 20;
|
||||
tg.sync();
|
||||
syncTestD[gIdx] = sm[1] * sm[0];
|
||||
}
|
||||
|
||||
static void test_cg_thread_block_type_via_base_type(int blockSize)
|
||||
{
|
||||
int nBytes = sizeof(int) * 2 * blockSize;
|
||||
int *sizeTestD, *sizeTestH;
|
||||
int *thdRankTestD, *thdRankTestH;
|
||||
int *syncTestD, *syncTestH;
|
||||
|
||||
// Allocate device memory
|
||||
HIPCHECK(hipMalloc(&sizeTestD, nBytes));
|
||||
HIPCHECK(hipMalloc(&thdRankTestD, nBytes));
|
||||
HIPCHECK(hipMalloc(&syncTestD, nBytes));
|
||||
|
||||
// Allocate host memory
|
||||
HIPCHECK(hipHostMalloc(&sizeTestH, nBytes));
|
||||
HIPCHECK(hipHostMalloc(&thdRankTestH, nBytes));
|
||||
HIPCHECK(hipHostMalloc(&syncTestH, nBytes));
|
||||
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_cg_thread_block_type_via_base_type,
|
||||
2,
|
||||
blockSize,
|
||||
0,
|
||||
0,
|
||||
sizeTestD,
|
||||
thdRankTestD,
|
||||
syncTestD);
|
||||
|
||||
// Copy result from device to host
|
||||
HIPCHECK(hipMemcpy(sizeTestH, sizeTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(thdRankTestH, thdRankTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(syncTestH, syncTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
|
||||
// Validate results for both blocks together
|
||||
for (int i = 0; i < 2 * blockSize; ++i) {
|
||||
ASSERT_EQUAL(sizeTestH[i], blockSize);
|
||||
ASSERT_EQUAL(thdRankTestH[i], i % blockSize);
|
||||
ASSERT_EQUAL(syncTestH[i], 200);
|
||||
}
|
||||
|
||||
// Free device memory
|
||||
HIPCHECK(hipFree(sizeTestD));
|
||||
HIPCHECK(hipFree(thdRankTestD));
|
||||
HIPCHECK(hipFree(syncTestD));
|
||||
|
||||
//Free host memory
|
||||
HIPCHECK(hipHostFree(sizeTestH));
|
||||
HIPCHECK(hipHostFree(thdRankTestH));
|
||||
HIPCHECK(hipHostFree(syncTestH));
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipCGThreadBlockType_BaseType") {
|
||||
// Use default device for validating the test
|
||||
int deviceId;
|
||||
hipDeviceProp_t deviceProperties;
|
||||
HIPCHECK(hipGetDevice(&deviceId));
|
||||
HIPCHECK(hipGetDeviceProperties(&deviceProperties, deviceId));
|
||||
|
||||
if (!deviceProperties.cooperativeLaunch) {
|
||||
HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test for blockSizes in powers of 2
|
||||
int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock;
|
||||
for (int blockSize = 2; blockSize <= maxThreadsPerBlock; blockSize = blockSize*2) {
|
||||
test_cg_thread_block_type_via_base_type(blockSize);
|
||||
}
|
||||
|
||||
// Test for random blockSizes, but the sequence is the same every execution
|
||||
srand(0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Test fails for only 1 thread per block
|
||||
test_cg_thread_block_type_via_base_type(max(2, rand() % maxThreadsPerBlock));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
|
||||
/* HIT_START
|
||||
* BUILD: %t %s ../../test_common.cpp
|
||||
* TEST: %t
|
||||
* HIT_END
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include "hip/hip_cooperative_groups.h"
|
||||
#include <cstdlib>
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) assert(lhs == rhs)
|
||||
|
||||
using namespace cooperative_groups;
|
||||
|
||||
static __global__
|
||||
void kernel_cg_thread_block_type_via_public_api(int *sizeTestD,
|
||||
int *thdRankTestD,
|
||||
int *syncTestD)
|
||||
{
|
||||
thread_block tb = this_thread_block();
|
||||
int gIdx = (blockIdx.x * blockDim.x) + threadIdx.x;
|
||||
|
||||
// Test group_size api
|
||||
sizeTestD[gIdx] = group_size(tb);
|
||||
|
||||
// Test thread_rank api
|
||||
thdRankTestD[gIdx] = thread_rank(tb);
|
||||
|
||||
// Test sync api
|
||||
__shared__ int sm[2];
|
||||
if (threadIdx.x == 0)
|
||||
sm[0] = 10;
|
||||
else if (threadIdx.x == 1)
|
||||
sm[1] = 20;
|
||||
sync(tb);
|
||||
syncTestD[gIdx] = sm[1] * sm[0];
|
||||
}
|
||||
|
||||
static void test_cg_thread_block_type_via_public_api(int blockSize)
|
||||
{
|
||||
int nBytes = sizeof(int) * 2 * blockSize;
|
||||
int *sizeTestD, *sizeTestH;
|
||||
int *thdRankTestD, *thdRankTestH;
|
||||
int *syncTestD, *syncTestH;
|
||||
|
||||
// Allocate device memory
|
||||
HIPCHECK(hipMalloc(&sizeTestD, nBytes));
|
||||
HIPCHECK(hipMalloc(&thdRankTestD, nBytes));
|
||||
HIPCHECK(hipMalloc(&syncTestD, nBytes));
|
||||
|
||||
// Allocate host memory
|
||||
HIPCHECK(hipHostMalloc(&sizeTestH, nBytes));
|
||||
HIPCHECK(hipHostMalloc(&thdRankTestH, nBytes));
|
||||
HIPCHECK(hipHostMalloc(&syncTestH, nBytes));
|
||||
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_cg_thread_block_type_via_public_api,
|
||||
2,
|
||||
blockSize,
|
||||
0,
|
||||
0,
|
||||
sizeTestD,
|
||||
thdRankTestD,
|
||||
syncTestD);
|
||||
|
||||
// Copy result from device to host
|
||||
HIPCHECK(hipMemcpy(sizeTestH, sizeTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(thdRankTestH, thdRankTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
HIPCHECK(hipMemcpy(syncTestH, syncTestD, nBytes, hipMemcpyDeviceToHost));
|
||||
|
||||
// Validate results for both blocks together
|
||||
for (int i = 0; i < 2 * blockSize; ++i) {
|
||||
ASSERT_EQUAL(sizeTestH[i], blockSize);
|
||||
ASSERT_EQUAL(thdRankTestH[i], i % blockSize);
|
||||
ASSERT_EQUAL(syncTestH[i], 200);
|
||||
}
|
||||
|
||||
// Free device memory
|
||||
HIPCHECK(hipFree(sizeTestD));
|
||||
HIPCHECK(hipFree(thdRankTestD));
|
||||
HIPCHECK(hipFree(syncTestD));
|
||||
|
||||
//Free host memory
|
||||
HIPCHECK(hipHostFree(sizeTestH));
|
||||
HIPCHECK(hipHostFree(thdRankTestH));
|
||||
HIPCHECK(hipHostFree(syncTestH));
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_hipCGThreadBlockType_PublicApi") {
|
||||
// Use default device for validating the test
|
||||
int deviceId;
|
||||
hipDeviceProp_t deviceProperties;
|
||||
HIPCHECK(hipGetDevice(&deviceId));
|
||||
HIPCHECK(hipGetDeviceProperties(&deviceProperties, deviceId));
|
||||
|
||||
if (!deviceProperties.cooperativeLaunch) {
|
||||
HipTest::HIP_SKIP_TEST("Device doesn't support cooperative launch!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test for blockSizes in powers of 2
|
||||
int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock;
|
||||
for (int blockSize = 2; blockSize <= maxThreadsPerBlock; blockSize = blockSize*2) {
|
||||
test_cg_thread_block_type_via_public_api(blockSize);
|
||||
}
|
||||
|
||||
// Test for random blockSizes, but the sequence is the same every execution
|
||||
srand(0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Test fails for only 1 thread per block
|
||||
test_cg_thread_block_type_via_public_api(max(2, rand() % maxThreadsPerBlock));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,576 @@
|
||||
/*
|
||||
Copyright (c) 2020 - 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 Description:
|
||||
/* This test implements sum reduction kernel, first with each threads own rank
|
||||
as input and comparing the sum with expected sum output derieved from n(n-1)/2
|
||||
formula. The second part, partitions this parent group into child subgroups
|
||||
a.k.a tiles using using tiled_partition() collective operation. This can be called
|
||||
with a static tile size, passed in templated non-type variable-tiled_partition<tileSz>,
|
||||
or in runtime as tiled_partition(thread_group parent, tileSz). This test covers both these
|
||||
cases.
|
||||
This test tests functionality of cg group partitioning, (static and dynamic) and its respective
|
||||
API's size(), thread_rank(), and sync().
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
#include <hip/hip_cooperative_groups.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace cooperative_groups;
|
||||
|
||||
#define ASSERT_EQUAL(lhs, rhs) assert(lhs == rhs)
|
||||
|
||||
#define NUM_ELEMS 10000000
|
||||
#define NUM_THREADS_PER_BLOCK 512
|
||||
#define WAVE_SIZE 32
|
||||
|
||||
/* Test coalesced group's functionality.
|
||||
*
|
||||
*/
|
||||
|
||||
__device__ int atomicAggInc(int *ptr) {
|
||||
coalesced_group g = coalesced_threads();
|
||||
int prev;
|
||||
// elect the first active thread to perform atomic add
|
||||
if (g.thread_rank() == 0) {
|
||||
prev = atomicAdd(ptr, g.size());
|
||||
}
|
||||
// broadcast previous value within the warp
|
||||
// and add each active thread’s rank to it
|
||||
prev = g.thread_rank() + g.shfl(prev, 0);
|
||||
return prev;
|
||||
}
|
||||
|
||||
__global__ void kernel_shfl (int * dPtr, int *dResults, int srcLane, int cg_sizes) {
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
if (id % cg_sizes == 0) {
|
||||
coalesced_group const& g = coalesced_threads();
|
||||
int rank = g.thread_rank();
|
||||
int val = dPtr[rank];
|
||||
dResults[rank] = g.shfl(val, srcLane);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void kernel_shfl_any_to_any (int *randVal, int *dsrcArr, int *dResults, int cg_sizes) {
|
||||
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
if (id % cg_sizes == 0) {
|
||||
coalesced_group const& g = coalesced_threads();
|
||||
int rank = g.thread_rank();
|
||||
int val = randVal[rank];
|
||||
dResults[rank] = g.shfl(val, dsrcArr[rank]);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__global__ void filter_arr(int *dst, int *nres, const int *src, int n) {
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
|
||||
for (int i = id; i < n; i += gridDim.x * blockDim.x) {
|
||||
if (src[i] > 0) dst[atomicAggInc(nres)] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Parallel reduce kernel.
|
||||
*
|
||||
* Step complexity: O(log n)
|
||||
* Work complexity: O(n)
|
||||
*
|
||||
* Note: This kernel works only with power of 2 input arrays.
|
||||
*/
|
||||
__device__ int reduction_kernel(coalesced_group g, int* x, int val) {
|
||||
int lane = g.thread_rank();
|
||||
int sz = g.size();
|
||||
|
||||
for (int i = g.size() / 2; i > 0; i /= 2) {
|
||||
// use lds to store the temporary result
|
||||
x[lane] = val;
|
||||
// Ensure all the stores are completed.
|
||||
g.sync();
|
||||
|
||||
if (lane < i) {
|
||||
val += x[lane + i];
|
||||
}
|
||||
// It must work on one tiled thread group at a time,
|
||||
// and it must make sure all memory operations are
|
||||
// completed before moving to the next stride.
|
||||
// sync() here just does that.
|
||||
g.sync();
|
||||
}
|
||||
|
||||
// Choose the 0'th indexed thread that holds the reduction value to return
|
||||
if (g.thread_rank() == 0) {
|
||||
return val;
|
||||
}
|
||||
// Rest of the threads return no useful values
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void kernel_cg_coalesced_group_partition(unsigned int tileSz, int* result,
|
||||
bool isGlobalMem, int* globalMem, int cg_sizes) {
|
||||
|
||||
int id = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
if (id % cg_sizes == 0) {
|
||||
coalesced_group threadBlockCGTy = coalesced_threads();
|
||||
int threadBlockGroupSize = threadBlockCGTy.size();
|
||||
|
||||
int* workspace = NULL;
|
||||
|
||||
if (isGlobalMem) {
|
||||
workspace = globalMem;
|
||||
} else {
|
||||
// Declare a shared memory
|
||||
extern __shared__ int sharedMem[];
|
||||
workspace = sharedMem;
|
||||
}
|
||||
|
||||
int input, outputSum, expectedOutput;
|
||||
|
||||
// input to reduction, for each thread, is its' rank in the group
|
||||
input = threadBlockCGTy.thread_rank();
|
||||
|
||||
expectedOutput = (threadBlockGroupSize - 1) * threadBlockGroupSize / 2;
|
||||
|
||||
outputSum = reduction_kernel(threadBlockCGTy, workspace, input);
|
||||
|
||||
if (threadBlockCGTy.thread_rank() == 0) {
|
||||
printf(" Sum of all ranks 0..%d in coalesced_group is %d\n\n",
|
||||
(int)threadBlockCGTy.size() - 1, outputSum);
|
||||
printf(" Creating %d groups, of tile size %d threads:\n\n",
|
||||
(int)threadBlockCGTy.size() / tileSz, tileSz);
|
||||
}
|
||||
|
||||
threadBlockCGTy.sync();
|
||||
|
||||
coalesced_group tiledPartition = tiled_partition(threadBlockCGTy, tileSz);
|
||||
|
||||
// This offset allows each group to have its own unique area in the workspace array
|
||||
int workspaceOffset = threadBlockCGTy.thread_rank() - tiledPartition.thread_rank();
|
||||
|
||||
outputSum = reduction_kernel(tiledPartition, workspace + workspaceOffset, input);
|
||||
|
||||
if (tiledPartition.thread_rank() == 0) {
|
||||
printf(
|
||||
" Sum of all ranks 0..%d in this tiledPartition group is %d. Corresponding parent thread "
|
||||
"rank: %d\n",
|
||||
tiledPartition.size() - 1, outputSum, input);
|
||||
|
||||
result[input / (tileSz)] = outputSum;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void kernel_coalesced_active_groups() {
|
||||
thread_block threadBlockCGTy = this_thread_block();
|
||||
int threadBlockGroupSize = threadBlockCGTy.size();
|
||||
|
||||
// input to reduction, for each thread, is its' rank in the group
|
||||
int input = threadBlockCGTy.thread_rank();
|
||||
|
||||
if (threadBlockCGTy.thread_rank() == 0) {
|
||||
printf(" Creating odd and even set of active thread groups based on branch divergence\n\n");
|
||||
}
|
||||
|
||||
threadBlockCGTy.sync();
|
||||
|
||||
// Group all active odd threads
|
||||
if (threadBlockCGTy.thread_rank() % 2) {
|
||||
coalesced_group activeOdd = coalesced_threads();
|
||||
|
||||
if (activeOdd.thread_rank() == 0) {
|
||||
printf(" ODD: Size of odd set of active threads is %d."
|
||||
" Corresponding parent thread_rank is %d.\n\n",
|
||||
activeOdd.size(), threadBlockCGTy.thread_rank());
|
||||
}
|
||||
}
|
||||
else { // Group all active even threads
|
||||
coalesced_group activeEven = coalesced_threads();
|
||||
|
||||
if (activeEven.thread_rank() == 0) {
|
||||
printf(" EVEN: Size of even set of active threads is %d."
|
||||
" Corresponding parent thread_rank is %d.",
|
||||
activeEven.size(), threadBlockCGTy.thread_rank());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void printResults(int* ptr, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
std::cout << ptr[i] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
void compareResults(int* cpu, int* gpu, int size) {
|
||||
for (unsigned int i = 0; i < size / sizeof(int); i++) {
|
||||
if (cpu[i] != gpu[i]) {
|
||||
INFO(" results do not match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_active_threads_grouping() {
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
int threadsPerBlock = WAVE_SIZE;
|
||||
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_coalesced_active_groups, blockSize, threadsPerBlock, 0, 0);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
printf("\n...PASSED.\n\n");
|
||||
}
|
||||
|
||||
// Search if the sum exists in the expected results array
|
||||
void verifyResults(int* hPtr, int* dPtr, int size) {
|
||||
int i = 0, j = 0;
|
||||
for (i = 0; i < size; i++) {
|
||||
for (j = 0; j < size; j++) {
|
||||
if (hPtr[i] == dPtr[j]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == size) {
|
||||
INFO(" Result verification failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void test_group_partition(unsigned int tileSz, bool useGlobalMem) {
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
int threadsPerBlock = WAVE_SIZE;
|
||||
|
||||
std::vector<unsigned int> cg_sizes = {1, 2, 3};
|
||||
for (auto i : cg_sizes) {
|
||||
|
||||
int numTiles = ((blockSize * threadsPerBlock) / i) / tileSz;
|
||||
|
||||
// numTiles = 0 when partitioning is possible. The below statement is to avoid
|
||||
// out-of-bounds error and still evaluate failure case.
|
||||
numTiles = (numTiles == 0) ? 1 : numTiles;
|
||||
|
||||
// Build an array of expected reduction sum output on the host
|
||||
// based on the sum of their respective thread ranks to use for verification
|
||||
int* expectedSum = new int[numTiles];
|
||||
int temp = 0, sum = 0;
|
||||
for (int i = 1; i <= numTiles; i++) {
|
||||
sum = temp;
|
||||
temp = (((tileSz * i) - 1) * (tileSz * i)) / 2;
|
||||
expectedSum[i-1] = temp - sum;
|
||||
}
|
||||
|
||||
int* dResult = NULL;
|
||||
hipMalloc(&dResult, sizeof(int) * numTiles);
|
||||
|
||||
int* globalMem = NULL;
|
||||
if (useGlobalMem) {
|
||||
hipMalloc((void**)&globalMem, threadsPerBlock * sizeof(int));
|
||||
}
|
||||
|
||||
int* hResult = NULL;
|
||||
hipHostMalloc(&hResult, numTiles * sizeof(int), hipHostMallocDefault);
|
||||
memset(hResult, 0, numTiles * sizeof(int));
|
||||
|
||||
// Launch Kernel
|
||||
if (useGlobalMem) {
|
||||
hipLaunchKernelGGL(kernel_cg_coalesced_group_partition, blockSize, threadsPerBlock, 0, 0, tileSz,
|
||||
dResult, useGlobalMem, globalMem, i);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
} else {
|
||||
hipLaunchKernelGGL(kernel_cg_coalesced_group_partition, blockSize, threadsPerBlock,
|
||||
threadsPerBlock * sizeof(int), 0, tileSz, dResult, useGlobalMem, globalMem, i);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
}
|
||||
|
||||
hipMemcpy(hResult, dResult, numTiles * sizeof(int), hipMemcpyDeviceToHost);
|
||||
verifyResults(expectedSum, hResult, numTiles);
|
||||
// Free all allocated memory on host and device
|
||||
hipFree(dResult);
|
||||
hipFree(hResult);
|
||||
if (useGlobalMem) {
|
||||
hipFree(globalMem);
|
||||
}
|
||||
delete[] expectedSum;
|
||||
|
||||
printf("\n...PASSED.\n\n");
|
||||
}
|
||||
}
|
||||
static void test_shfl_any_to_any() {
|
||||
|
||||
std::vector<unsigned int> cg_sizes = {1, 2, 3};
|
||||
for (auto i : cg_sizes) {
|
||||
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
int threadsPerBlock = WAVE_SIZE;
|
||||
|
||||
int totalThreads = blockSize * threadsPerBlock;
|
||||
int group_size = (totalThreads + i - 1) / i;
|
||||
int group_size_in_bytes = group_size * sizeof(int);
|
||||
|
||||
int* hPtr = NULL;
|
||||
int* dPtr = NULL;
|
||||
int* dsrcArr = NULL;
|
||||
int* dResults = NULL;
|
||||
int* srcArr = (int*)malloc(group_size_in_bytes);
|
||||
int* srcArrCpu = (int*)malloc(group_size_in_bytes);
|
||||
|
||||
std::cout << "Testing coalesced_groups shfl any-to-any\n" <<std::endl;
|
||||
|
||||
int arrSize = blockSize * threadsPerBlock * sizeof(int);
|
||||
|
||||
hipHostMalloc(&hPtr, arrSize);
|
||||
// Fill up the array
|
||||
for (int i = 0; i < WAVE_SIZE; i++) {
|
||||
hPtr[i] = rand() % 1000;
|
||||
}
|
||||
|
||||
// Fill up the random array
|
||||
for (int i = 0; i < group_size; i++) {
|
||||
srcArr[i] = rand() % 1000;
|
||||
srcArrCpu[i] = srcArr[i] % group_size;
|
||||
}
|
||||
|
||||
/* Fill cpu results array so that we can verify with gpu computation */
|
||||
int* cpuResultsArr = (int*)malloc(group_size_in_bytes);
|
||||
for(int i = 0; i < group_size; i++) {
|
||||
cpuResultsArr[i] = hPtr[srcArrCpu[i]];
|
||||
}
|
||||
|
||||
//printf("Array passed to GPU for computation\n");
|
||||
//printResults(hPtr, WAVE_SIZE);
|
||||
hipMalloc(&dPtr, group_size_in_bytes);
|
||||
hipMalloc(&dResults, group_size_in_bytes);
|
||||
|
||||
hipMalloc(&dsrcArr, group_size_in_bytes);
|
||||
hipMemcpy(dsrcArr, srcArr, group_size_in_bytes, hipMemcpyHostToDevice);
|
||||
|
||||
hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice);
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_shfl_any_to_any, blockSize, threadsPerBlock,
|
||||
threadsPerBlock * sizeof(int), 0 , dPtr, dsrcArr, dResults, i);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost);
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
//printf("GPU results: \n");
|
||||
//printResults(hPtr, group_size);
|
||||
//printf("Printing cpu to be verified array\n");
|
||||
//printResults(cpuResultsArr, group_size);
|
||||
//printf("Printing srcLane array that was passed\n");
|
||||
//printResults(srcArr, group_size);
|
||||
//printf("Printing srcLane array on the CPU\n");
|
||||
//printResults(srcArrCpu, group_size);
|
||||
compareResults(hPtr, cpuResultsArr, group_size_in_bytes);
|
||||
std::cout << "Results verified!\n";
|
||||
|
||||
hipFree(hPtr);
|
||||
hipFree(dPtr);
|
||||
free(srcArr);
|
||||
free(srcArrCpu);
|
||||
free(cpuResultsArr);
|
||||
}
|
||||
}
|
||||
static void test_shfl_broadcast() {
|
||||
|
||||
std::vector<unsigned int> cg_sizes = {1, 2, 3};
|
||||
for (auto i : cg_sizes) {
|
||||
|
||||
hipError_t err;
|
||||
int blockSize = 1;
|
||||
int threadsPerBlock = WAVE_SIZE;
|
||||
|
||||
int totalThreads = blockSize * threadsPerBlock;
|
||||
int group_size = (totalThreads + i - 1) / i;
|
||||
int group_size_in_bytes = group_size * sizeof(int);
|
||||
|
||||
int* hPtr = NULL;
|
||||
int* dPtr = NULL;
|
||||
int* dResults = NULL;
|
||||
int srcLane = rand() % 1000;
|
||||
int srcLaneCpu = 0;
|
||||
std::cout << "Testing coalesced_groups shfl with srcLane " << srcLane << '\n'
|
||||
<< " and group size " << i <<std::endl;
|
||||
|
||||
int arrSize = blockSize * threadsPerBlock * sizeof(int);
|
||||
|
||||
hipHostMalloc(&hPtr, arrSize);
|
||||
// Fill up the array
|
||||
for (int i = 0; i < WAVE_SIZE; i++) {
|
||||
hPtr[i] = rand() % 1000;
|
||||
}
|
||||
|
||||
|
||||
/* Fill cpu results array so that we can verify with gpu computation */
|
||||
srcLaneCpu = hPtr[srcLane % group_size];
|
||||
|
||||
int* cpuResultsArr = (int*)malloc(sizeof(int) * group_size);
|
||||
for (int i = 0; i < group_size; i++) {
|
||||
cpuResultsArr[i] = srcLaneCpu;
|
||||
}
|
||||
printf("Array passed to GPU for computation\n");
|
||||
printResults(hPtr, WAVE_SIZE);
|
||||
hipMalloc(&dPtr, group_size_in_bytes);
|
||||
hipMalloc(&dResults, group_size_in_bytes);
|
||||
|
||||
hipMemcpy(dPtr, hPtr, group_size_in_bytes, hipMemcpyHostToDevice);
|
||||
// Launch Kernel
|
||||
hipLaunchKernelGGL(kernel_shfl, blockSize, threadsPerBlock,
|
||||
threadsPerBlock * sizeof(int), 0, dPtr, dResults, srcLane, i);
|
||||
HIP_CHECK(hipGetLastError());
|
||||
hipMemcpy(hPtr, dResults, group_size_in_bytes, hipMemcpyDeviceToHost);
|
||||
err = hipDeviceSynchronize();
|
||||
if (err != hipSuccess) {
|
||||
fprintf(stderr, "Failed to launch kernel (error code %s)!\n", hipGetErrorString(err));
|
||||
}
|
||||
printf("GPU results: \n");
|
||||
printResults(hPtr, group_size);
|
||||
printf("Printing cpu to be verified array\n");
|
||||
printResults(cpuResultsArr, group_size);
|
||||
|
||||
compareResults(hPtr, cpuResultsArr, group_size_in_bytes);
|
||||
std::cout << "Results verified!\n";
|
||||
|
||||
hipFree(hPtr);
|
||||
hipFree(dPtr);
|
||||
free(cpuResultsArr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Unit_coalesced_groups") {
|
||||
// Use default device for validating the test
|
||||
int deviceId;
|
||||
HIP_CHECK(hipGetDevice(&deviceId));
|
||||
hipDeviceProp_t deviceProperties;
|
||||
HIP_CHECK(hipGetDeviceProperties(&deviceProperties, deviceId));
|
||||
int maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock;
|
||||
|
||||
std::cout << "Now testing coalesced_groups" << '\n' << std::endl;
|
||||
|
||||
int *data_to_filter, *filtered_data, nres = 0;
|
||||
int *d_data_to_filter, *d_filtered_data, *d_nres;
|
||||
|
||||
int numOfBuckets = 5;
|
||||
|
||||
data_to_filter = reinterpret_cast<int *>(malloc(sizeof(int) * NUM_ELEMS));
|
||||
|
||||
// Generate input data.
|
||||
for (int i = 0; i < NUM_ELEMS; i++) {
|
||||
data_to_filter[i] = rand() % numOfBuckets;
|
||||
}
|
||||
|
||||
|
||||
HIP_CHECK(hipMalloc(&d_data_to_filter, sizeof(int) * NUM_ELEMS));
|
||||
HIP_CHECK(hipMalloc(&d_filtered_data, sizeof(int) * NUM_ELEMS));
|
||||
HIP_CHECK(hipMalloc(&d_nres, sizeof(int)));
|
||||
|
||||
HIP_CHECK(hipMemcpy(d_data_to_filter, data_to_filter,
|
||||
sizeof(int) * NUM_ELEMS, hipMemcpyHostToDevice));
|
||||
hipMemset(d_nres, 0, sizeof(int));
|
||||
|
||||
dim3 dimBlock(NUM_THREADS_PER_BLOCK, 1, 1);
|
||||
dim3 dimGrid((NUM_ELEMS / NUM_THREADS_PER_BLOCK) + 1, 1, 1);
|
||||
|
||||
filter_arr<<<dimGrid, dimBlock>>>(d_filtered_data, d_nres, d_data_to_filter,
|
||||
NUM_ELEMS);
|
||||
|
||||
|
||||
HIP_CHECK(hipMemcpy(&nres, d_nres, sizeof(int), hipMemcpyDeviceToHost));
|
||||
|
||||
filtered_data = reinterpret_cast<int *>(malloc(sizeof(int) * nres));
|
||||
|
||||
HIP_CHECK(hipMemcpy(filtered_data, d_filtered_data, sizeof(int) * nres,
|
||||
hipMemcpyDeviceToHost));
|
||||
|
||||
int *host_filtered_data =
|
||||
reinterpret_cast<int *>(malloc(sizeof(int) * NUM_ELEMS));
|
||||
|
||||
// Generate host output with host filtering code.
|
||||
int host_flt_count = 0;
|
||||
for (int i = 0; i < NUM_ELEMS; i++) {
|
||||
if (data_to_filter[i] > 0) {
|
||||
host_filtered_data[host_flt_count++] = data_to_filter[i];
|
||||
}
|
||||
}
|
||||
|
||||
printf("\nWarp Aggregated Atomics %s \n",
|
||||
(host_flt_count == nres) ? "PASSED" : "FAILED");
|
||||
|
||||
// Now, testing shfl collective
|
||||
std::cout << "Now testing shfl collective as a broadcast" << '\n' << std::endl;
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
test_shfl_broadcast();
|
||||
}
|
||||
|
||||
|
||||
// Now, testing shfl collective
|
||||
std::cout << "Now testing shfl operations any-to-any member lanes" << '\n' << std::endl;
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
test_shfl_any_to_any();
|
||||
}
|
||||
|
||||
// Now, pass a already coalesced_group that was partitioned
|
||||
/* Test coalesced group partitioning */
|
||||
std::cout << "Now testing coalesced_groups partitioning" << '\n' << std::endl;
|
||||
|
||||
int testNo = 1;
|
||||
for (int memTy = 0; memTy < 2; memTy++) {
|
||||
std::vector<unsigned int> tileSizes = {2, 4, 8, 16, 32};
|
||||
for (auto i : tileSizes) {
|
||||
std::cout << "TEST " << testNo << ":" << '\n' << std::endl;
|
||||
test_group_partition(i, memTy);
|
||||
testNo++;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Now grouping active threads based on branch divergence" << '\n' << std::endl;
|
||||
test_active_threads_grouping();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
# Common Tests - Test independent of all platforms
|
||||
set(TEST_SRC
|
||||
hipChooseDevice.cc
|
||||
hipDeviceComputeCapability.cc
|
||||
hipDeviceGetByPCIBusId.cc
|
||||
hipDeviceGetLimit.cc
|
||||
hipDeviceGetName.cc
|
||||
hipDeviceGetPCIBusId.cc
|
||||
hipDeviceSetGetCacheConfig.cc
|
||||
hipDeviceSynchronize.cc
|
||||
hipDeviceTotalMem.cc
|
||||
hipGetDeviceAttribute.cc
|
||||
hipGetDeviceCount.cc
|
||||
hipGetDeviceProperties.cc
|
||||
hipRuntimeGetVersion.cc
|
||||
hipGetSetDeviceFlags.cc
|
||||
hipSetGetDevice.cc
|
||||
hipDeviceGetUuid.cc
|
||||
hipDeviceGetP2PAttribute.cc
|
||||
hipDeviceGetDefaultMemPool.cc
|
||||
hipDeviceCanAccessPeer.cc
|
||||
hipDeviceEnableDisablePeerAccess.cc
|
||||
hipExtGetLinkTypeAndHopCount.cc
|
||||
hipDeviceSetLimit.cc
|
||||
hipDeviceSetGetSharedMemConfig.cc
|
||||
hipDeviceReset.cc
|
||||
hipDeviceSetGetMemPool.cc
|
||||
hipInit.cc
|
||||
hipDriverGetVersion.cc
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
set(TEST_SRC ${TEST_SRC}
|
||||
hipIpcOpenMemHandle.cc
|
||||
hipIpcGetMemHandle.cc
|
||||
hipIpcCloseMemHandle.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
set_source_files_properties(hipGetDeviceCount.cc PROPERTIES COMPILE_FLAGS -std=c++17)
|
||||
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)
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <hip/hip_runtime.h>
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
bool UNSETENV(std::string var) {
|
||||
int result = -1;
|
||||
#ifdef __unix__
|
||||
result = unsetenv(var.c_str());
|
||||
#else
|
||||
result = _putenv((var + '=').c_str());
|
||||
#endif
|
||||
return (result == 0) ? true: false;
|
||||
}
|
||||
|
||||
bool SETENV(std::string var, std::string value, int overwrite) {
|
||||
int result = -1;
|
||||
#ifdef __unix__
|
||||
result = setenv(var.c_str(), value.c_str(), overwrite);
|
||||
#else
|
||||
result = _putenv((var + '=' + value).c_str());
|
||||
#endif
|
||||
return (result == 0) ? true: false;
|
||||
}
|
||||
|
||||
// Expects 1 command line arg, which is the Device Visible String
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Invalid number of args passed.\n"
|
||||
<< "argc : " << argc << std::endl;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
std::cerr << " argv[" << i << "] : " << argv[0] << std::endl;
|
||||
}
|
||||
std::cerr << "The program expects device visibility string i.e. 0,1,2" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// disable visible_devices env from shell
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
UNSETENV("CUDA_VISIBLE_DEVICES");
|
||||
SETENV("CUDA_VISIBLE_DEVICES", argv[1], 1);
|
||||
auto init_res = hipInit(0);
|
||||
if (hipSuccess != init_res) {
|
||||
std::cerr << "CUDA INIT API returned : " << hipGetErrorString(init_res) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
UNSETENV("ROCR_VISIBLE_DEVICES");
|
||||
UNSETENV("HIP_VISIBLE_DEVICES");
|
||||
SETENV("ROCR_VISIBLE_DEVICES", argv[1], 1);
|
||||
SETENV("HIP_VISIBLE_DEVICES", argv[1], 1);
|
||||
#endif
|
||||
|
||||
int count = 0;
|
||||
auto res = hipGetDeviceCount(&count);
|
||||
if (hipSuccess != res) {
|
||||
std::cerr << "HIP API returned : " << hipGetErrorString(res) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __HIP_PLATFORM_NVCC__
|
||||
UNSETENV("CUDA_VISIBLE_DEVICES");
|
||||
#else
|
||||
UNSETENV("ROCR_VISIBLE_DEVICES");
|
||||
UNSETENV("HIP_VISIBLE_DEVICES");
|
||||
#endif
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <hip_test_common.hh>
|
||||
|
||||
/**
|
||||
* hipChooseDevice tests
|
||||
* Scenario: Validates dev id value.
|
||||
*/
|
||||
TEST_CASE("Unit_hipChooseDevice_ValidateDevId") {
|
||||
hipDeviceProp_t prop;
|
||||
HIP_CHECK(hipGetDeviceProperties(&prop, 0));
|
||||
int numDevices = 0;
|
||||
HIP_CHECK(hipGetDeviceCount(&numDevices));
|
||||
int dev = -1;
|
||||
HIP_CHECK(hipChooseDevice(&dev, &prop));
|
||||
REQUIRE_FALSE(dev < 0);
|
||||
REQUIRE_FALSE(dev >= numDevices);
|
||||
}
|
||||
/**
|
||||
* hipChooseDevice tests
|
||||
* Scenario1: Validates if dev = nullptr returns error code
|
||||
* Scenario2: Validates if prop = nullptr returns error code
|
||||
*/
|
||||
TEST_CASE("Unit_hipChooseDevice_NegTst") {
|
||||
hipDeviceProp_t prop;
|
||||
int dev = -1;
|
||||
|
||||
// Scenario1
|
||||
SECTION("dev is nullptr") {
|
||||
REQUIRE_FALSE(hipSuccess == hipChooseDevice(nullptr, &prop));
|
||||
}
|
||||
|
||||
// Scenario2
|
||||
SECTION("prop is nullptr") {
|
||||
REQUIRE_FALSE(hipSuccess == hipChooseDevice(&dev, nullptr));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user