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:
Rahul Garg
2022-12-12 19:51:29 +00:00
647 changed files with 121950 additions and 0 deletions
@@ -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)
+252
View File
@@ -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()
+204
View File
@@ -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
- Dont 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 Catchs 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 */
+23
View File
@@ -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.
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()
@@ -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}")
@@ -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()
+25
View File
@@ -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.
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;
};
+145
View File
@@ -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()
+6
View File
@@ -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 threads 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