Files
TheRock/rocm-systems/projects/hip-tests/catch/unit/cooperativeGrps/grid_group.cc
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

324 lines
13 KiB
C++

/*
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
*
* SPDX-License-Identifier: MIT
*/
#include "cooperative_groups_common.hh"
#include <cpu_grid.h>
#include <resource_guards.hh>
#include <utils.hh>
/**
* @addtogroup grid_group grid_group
* @{
* @ingroup DeviceLanguageTest
* Contains unit tests for all grid_group APIs
*/
namespace cg = cooperative_groups;
static __global__ void grid_group_size_getter(unsigned int* sizes) {
sizes[thread_rank_in_grid()] = cg::this_grid().size();
}
static __global__ void grid_group_thread_rank_getter(unsigned int* thread_ranks) {
thread_ranks[thread_rank_in_grid()] = cg::this_grid().thread_rank();
}
static __global__ void grid_group_block_rank_getter(unsigned int* block_ranks) {
block_ranks[thread_rank_in_grid()] = cg::this_grid().block_rank();
}
static __global__ void grid_group_is_valid_getter(unsigned int* is_valid_flags) {
is_valid_flags[thread_rank_in_grid()] = cg::this_grid().is_valid();
}
static __global__ void grid_group_non_member_size_getter(unsigned int* sizes) {
sizes[thread_rank_in_grid()] = cg::group_size(cg::this_grid());
}
static __global__ void grid_group_non_member_thread_rank_getter(unsigned int* thread_ranks) {
thread_ranks[thread_rank_in_grid()] = cg::thread_rank(cg::this_grid());
}
static __global__ void sync_kernel(unsigned int* atomic_val, unsigned int* per_loop_atomic,
unsigned int* array, unsigned int loops) {
cg::grid_group grid = cg::this_grid();
unsigned rank = grid.thread_rank();
int blocks_seen = 0;
int grid_blocks = gridDim.x * gridDim.y * gridDim.z;
for (int iter = 0; iter < loops; iter++) {
// Make the last thread run way behind everyone else.
// If the sync below fails, then the other threads may hit the
// atomicInc instruction many times before the last thread ever gets to it.
// If the sync works, then it will likely contain "total number of blocks"*iter
if (rank == (grid.size() - 1)) {
// The last wavefront should spin on this loop's atomic value
// until all of the other wavefronts have incremented the
// per-loop atomic and hit the grid.sync()
#if HT_AMD
while (__hip_atomic_load(&per_loop_atomic[iter], __ATOMIC_RELAXED, __HIP_MEMORY_SCOPE_AGENT) <
(grid_blocks - 1)) {
__builtin_amdgcn_s_sleep(127);
}
// Give the other waves time to maybe go around the loop again
// if the barrier has failed
__builtin_amdgcn_s_sleep(127);
#else // CUDA does not seem to need an ordered atomic load
while (per_loop_atomic[iter] < (grid_blocks - 1)) {
}
#endif
}
if (threadIdx.x == blockDim.x - 1 && threadIdx.y == blockDim.y - 1 &&
threadIdx.z == blockDim.z - 1) {
atomicInc(&per_loop_atomic[iter], UINT_MAX);
array[((blockIdx.z * gridDim.y + blockIdx.y) * gridDim.x + blockIdx.x) + blocks_seen] =
atomicInc(&atomic_val[0], UINT_MAX);
}
grid.sync();
blocks_seen += grid_blocks;
}
}
/**
* Test Description
* ------------------------
* - Launches kernels that write the return values of size, thread_rank and is_valid member
* functions to an output array that is validated on the host side. The kernels are run
* sequentially, reusing the output array, to avoid running out of device memory for large kernel
* launches.
* Test source
* ------------------------
* - unit/cooperativeGrps/grid_group.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 5.2
* - Device supports cooperative launch
*/
HIP_TEST_CASE(Unit_Grid_Group_Getters_Positive_Basic) {
int device;
hipDeviceProp_t device_properties;
HIP_CHECK(hipGetDevice(&device));
HIP_CHECK(hipGetDeviceProperties(&device_properties, device));
if (!device_properties.cooperativeLaunch) {
HIP_SKIP_TEST(HipTest::SkipReason::kCooperativeLaunchUnsupported);
}
const auto blocks = GenerateBlockDimensions();
const auto threads = GenerateThreadDimensions();
if (!CheckDimensions(device, grid_group_size_getter, blocks, threads)) return;
INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z);
INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z);
const CPUGrid grid(blocks, threads);
LinearAllocGuard<unsigned int> uint_arr_dev(LinearAllocs::hipMalloc,
grid.thread_count_ * sizeof(unsigned int));
LinearAllocGuard<unsigned int> uint_arr(LinearAllocs::hipHostMalloc,
grid.thread_count_ * sizeof(unsigned int));
// Launch Kernel
unsigned int* uint_arr_dev_ptr = uint_arr_dev.ptr();
void* params[1];
params[0] = &uint_arr_dev_ptr;
HIP_CHECK(hipLaunchCooperativeKernel(grid_group_size_getter, blocks, threads, params, 0, 0));
HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(),
grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
HIP_CHECK(
hipLaunchCooperativeKernel(grid_group_thread_rank_getter, blocks, threads, params, 0, 0));
// Verify grid_group.size() values
ArrayAllOf(uint_arr.ptr(), grid.thread_count_,
[size = grid.thread_count_](uint32_t) { return size; });
HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(),
grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
HIP_CHECK(hipLaunchCooperativeKernel(grid_group_is_valid_getter, blocks, threads, params, 0, 0));
// Verify grid_group.thread_rank() values
ArrayAllOf(uint_arr.ptr(), grid.thread_count_, [](uint32_t i) { return i; });
HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(),
grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
HIP_CHECK(hipLaunchCooperativeKernel(grid_group_block_rank_getter, blocks, threads, params, 0, 0));
// Verify grid_group.is_valid() values
ArrayAllOf(uint_arr.ptr(), grid.thread_count_, [](uint32_t) { return 1; });
HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(),
grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
// Verify grid_group.block_rank() values
ArrayAllOf(uint_arr.ptr(), grid.thread_count_, [threads](uint32_t i) {
return i/(threads.x * threads.y * threads.z); });
}
/**
* Test Description
* ------------------------
* - Launches kernels that write the return values of size and thread_rank non-member functions
* to an output array that is validated on the host side. The kernels are run sequentially, reusing
* the output array, to avoid running out of device memory for large kernel launches.
* Test source
* ------------------------
* - unit/cooperativeGrps/grid_group.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 5.2
* - Device supports cooperative launch
*/
HIP_TEST_CASE(Unit_Grid_Group_Getters_Via_Non_Member_Functions_Positive_Basic) {
int device;
hipDeviceProp_t device_properties;
HIP_CHECK(hipGetDevice(&device));
HIP_CHECK(hipGetDeviceProperties(&device_properties, device));
if (!device_properties.cooperativeLaunch) {
HIP_SKIP_TEST(HipTest::SkipReason::kCooperativeLaunchUnsupported);
}
const auto blocks = GenerateBlockDimensions();
const auto threads = GenerateThreadDimensions();
if (!CheckDimensions(device, grid_group_non_member_size_getter, blocks, threads)) return;
INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z);
INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z);
const CPUGrid grid(blocks, threads);
LinearAllocGuard<unsigned int> uint_arr_dev(LinearAllocs::hipMalloc,
grid.thread_count_ * sizeof(unsigned int));
LinearAllocGuard<unsigned int> uint_arr(LinearAllocs::hipHostMalloc,
grid.thread_count_ * sizeof(unsigned int));
// Launch Kernel
unsigned int* uint_arr_dev_ptr = uint_arr_dev.ptr();
void* params[1];
params[0] = &uint_arr_dev_ptr;
HIP_CHECK(
hipLaunchCooperativeKernel(grid_group_non_member_size_getter, blocks, threads, params, 0, 0));
HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(),
grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
HIP_CHECK(hipLaunchCooperativeKernel(grid_group_non_member_thread_rank_getter, blocks, threads,
params, 0, 0));
// Verify grid_group.size() values
ArrayAllOf(uint_arr.ptr(), grid.thread_count_,
[size = grid.thread_count_](uint32_t) { return size; });
HIP_CHECK(hipMemcpy(uint_arr.ptr(), uint_arr_dev.ptr(),
grid.thread_count_ * sizeof(*uint_arr.ptr()), hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
// Verify grid_group.thread_rank() values
ArrayAllOf(uint_arr.ptr(), grid.thread_count_, [](uint32_t i) { return i; });
}
/**
* Test Description
* ------------------------
* - Launches a kernel where the last thread in a block atomically increments a global variable
* within a work loop. The value returned from this atomic increment entirely depends on the order
* the threads arrive at the atomic instruction. Each thread then stores the result in the global
* array based on its block id. A wait loop is inserted into the last thread so that it runs behind
* all other threads. If the sync doesn't work, the other threads will increment the atomic variable
* many times before the last thread gets to it and it will read a very large value. If the sync
* works, each thread will increment the variable once per loop iteration and the last thread will
* contain total number of blocks * loop iteration.
* Test source
* ------------------------
* - unit/cooperativeGrps/grid_group.cc
* Test requirements
* ------------------------
* - HIP_VERSION >= 5.2
* - Device supports cooperative launch
*/
HIP_TEST_CASE(Unit_Grid_Group_Sync_Positive_Basic) {
int device;
hipDeviceProp_t device_properties;
HIP_CHECK(hipGetDevice(&device));
HIP_CHECK(hipGetDeviceProperties(&device_properties, device));
if (!device_properties.cooperativeLaunch) {
HIP_SKIP_TEST(HipTest::SkipReason::kCooperativeLaunchUnsupported);
}
auto loops = GENERATE(2, 4, 8, 16);
dim3 blocks;
dim3 threads;
if (IsStrixHalo()) {
// Launch params for this test are hardcoded as a workaround for an issue reported
// ROCM-2957. When fixed, please enable calls to GenerateBlock/ThreadDimensions()
blocks = GENERATE_COPY(dim3(5,5,5), dim3(40,1,1), dim3(1, 40, 1), dim3(1, 1, 40));
threads = GENERATE_COPY(dim3(64,1,1), dim3(33,3,3), dim3(64, 8, 2), dim3(16, 16, 3));
} else {
blocks = GenerateBlockDimensions();
threads = GenerateThreadDimensions();
}
if (!CheckDimensions(device, sync_kernel, blocks, threads)) return;
INFO("Grid dimensions: x " << blocks.x << ", y " << blocks.y << ", z " << blocks.z);
INFO("Block dimensions: x " << threads.x << ", y " << threads.y << ", z " << threads.z);
const CPUGrid grid(blocks, threads);
unsigned int array_len = grid.block_count_ * loops;
unsigned int *uint_arr_dev{}, *uint_arr{}, *atomic_val{}, *per_loop_atomic_val{};
HIP_CHECK(hipMalloc(&uint_arr_dev, array_len * sizeof(unsigned int)));
HIP_CHECK(hipHostMalloc(&uint_arr, array_len * sizeof(unsigned int)));
HIP_CHECK(hipMalloc(&atomic_val, sizeof(unsigned int)));
HIP_CHECK(hipMalloc(&per_loop_atomic_val, loops * sizeof(unsigned int)));
HIP_CHECK(hipMemset(atomic_val, 0, sizeof(unsigned int)));
HIP_CHECK(hipMemset(per_loop_atomic_val, 0, loops * sizeof(unsigned int)));
// Launch Kernel
void* params[4];
params[0] = reinterpret_cast<void*>(&atomic_val);
params[1] = reinterpret_cast<void*>(&per_loop_atomic_val);
params[2] = reinterpret_cast<void*>(&uint_arr_dev);
params[3] = reinterpret_cast<void*>(&loops);
HIP_CHECK(hipLaunchCooperativeKernel(sync_kernel, blocks, threads, params, 0, 0));
HIP_CHECK(hipMemcpy(uint_arr, uint_arr_dev, array_len * sizeof(*uint_arr),
hipMemcpyDeviceToHost));
HIP_CHECK(hipDeviceSynchronize());
// Verify host buffer values
unsigned int max_in_this_loop = 0;
for (unsigned int i = 0; i < loops; i++) {
max_in_this_loop += grid.block_count_;
unsigned int j = 0;
for (j = 0; j < grid.block_count_ - 1; j++) {
REQUIRE(uint_arr[i * grid.block_count_ + j] < max_in_this_loop);
}
// TODO: Fix this
// REQUIRE(uint_arr[i * grid.block_count_ + j] == max_in_this_loop - 1);
}
HIP_CHECK(hipFree(uint_arr_dev));
HIP_CHECK(hipHostFree(uint_arr));
HIP_CHECK(hipFree(atomic_val));
HIP_CHECK(hipFree(per_loop_atomic_val));
}
/**
* End doxygen group DeviceLanguageTest.
* @}
*/