Files
TheRock/rocm-systems/projects/hip-tests/catch/stress/stream/streamEnqueue.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

220 lines
6.9 KiB
C++

/*
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
*
* SPDX-License-Identifier: MIT
*/
#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
HIP_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
HIP_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) {
HIP_SKIP_TEST(HipTest::SkipReason::kFewerThanTwoGpus);
}
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; }));
}
}