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
293 lines
9.8 KiB
C++
293 lines
9.8 KiB
C++
/*
|
|
* Copyright (c) Advanced Micro Devices, Inc.
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* SDMA Test - exercises memory copy operations that use SDMA engines
|
|
*
|
|
* This example performs Host-to-Device, Device-to-Device, and Device-to-Host
|
|
* memory transfers to exercise the SDMA (System DMA) engines on AMD GPUs.
|
|
*
|
|
* Usage: sdma_test [OPTIONS]
|
|
* -s, --size <MB> Transfer size in MB (default: 512)
|
|
* -n, --iterations <N> Number of iterations (default: 10)
|
|
* -c, --copies <N> Number of copies per iteration (default: 10)
|
|
* -d, --device <ID> GPU device ID (default: 0)
|
|
* -h, --help Show this help message
|
|
*
|
|
* Run with profiler:
|
|
* rocprof-sys-run -e ROCPROFSYS_AMD_SMI_METRICS=sdma_usage -- ./sdma_test
|
|
*/
|
|
|
|
#include <chrono>
|
|
#include <csignal>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <hip/hip_runtime.h>
|
|
|
|
#define HIP_CHECK(cmd) \
|
|
do \
|
|
{ \
|
|
hipError_t error = cmd; \
|
|
if(error != hipSuccess) \
|
|
{ \
|
|
fprintf(stderr, "HIP error %s at %s:%d\n", hipGetErrorString(error), \
|
|
__FILE__, __LINE__); \
|
|
exit(1); \
|
|
} \
|
|
} while(0)
|
|
|
|
// Default configuration
|
|
static constexpr size_t DEFAULT_SIZE_MB = 512;
|
|
static constexpr int DEFAULT_ITERATIONS = 10;
|
|
static constexpr int DEFAULT_COPIES = 10;
|
|
static constexpr int DEFAULT_DEVICE_ID = 0;
|
|
|
|
volatile bool g_running = true;
|
|
|
|
void
|
|
signal_handler(int signum)
|
|
{
|
|
(void) signum;
|
|
g_running = false;
|
|
printf("\nStopping...\n");
|
|
}
|
|
|
|
void
|
|
print_usage(const char* prog_name)
|
|
{
|
|
printf("SDMA Test - exercises memory copy operations using SDMA engines\n\n");
|
|
printf("Usage: %s [OPTIONS]\n\n", prog_name);
|
|
printf("Options:\n");
|
|
printf(" -s, --size <MB> Transfer size in MB (default: %zu)\n",
|
|
DEFAULT_SIZE_MB);
|
|
printf(" -n, --iterations <N> Number of iterations, 0=infinite (default: %d)\n",
|
|
DEFAULT_ITERATIONS);
|
|
printf(" -c, --copies <N> Number of copies per iteration (default: %d)\n",
|
|
DEFAULT_COPIES);
|
|
printf(" -d, --device <ID> GPU device ID (default: %d)\n", DEFAULT_DEVICE_ID);
|
|
printf(" -h, --help Show this help message\n\n");
|
|
printf("Examples:\n");
|
|
printf(" %s # Run with defaults (512MB, 10 iterations)\n",
|
|
prog_name);
|
|
printf(" %s -s 1024 -n 5 # 1GB transfers, 5 iterations\n", prog_name);
|
|
printf(" %s -s 256 -n 0 # 256MB transfers, run until Ctrl+C\n", prog_name);
|
|
printf("\nRun with profiler:\n");
|
|
printf(" rocprof-sys-run -e ROCPROFSYS_AMD_SMI_METRICS=sdma_usage -- %s\n",
|
|
prog_name);
|
|
}
|
|
|
|
size_t
|
|
parse_size(const char* str)
|
|
{
|
|
size_t value = std::strtoull(str, nullptr, 10);
|
|
size_t len = std::strlen(str);
|
|
if(len > 0)
|
|
{
|
|
char suffix = str[len - 1];
|
|
switch(suffix)
|
|
{
|
|
case 'G':
|
|
case 'g': value *= 1024; // fall through
|
|
case 'M':
|
|
case 'm': break; // already in MB
|
|
case 'K':
|
|
case 'k': value /= 1024; break;
|
|
default: break;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
// Configuration with defaults
|
|
size_t size_mb = DEFAULT_SIZE_MB;
|
|
int iterations = DEFAULT_ITERATIONS;
|
|
int num_copies = DEFAULT_COPIES;
|
|
int device_id = DEFAULT_DEVICE_ID;
|
|
|
|
// Parse command line arguments
|
|
for(int i = 1; i < argc; i++)
|
|
{
|
|
if(std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0)
|
|
{
|
|
print_usage(argv[0]);
|
|
return 0;
|
|
}
|
|
else if((std::strcmp(argv[i], "-s") == 0 ||
|
|
std::strcmp(argv[i], "--size") == 0) &&
|
|
i + 1 < argc)
|
|
{
|
|
size_mb = parse_size(argv[++i]);
|
|
}
|
|
else if((std::strcmp(argv[i], "-n") == 0 ||
|
|
std::strcmp(argv[i], "--iterations") == 0) &&
|
|
i + 1 < argc)
|
|
{
|
|
iterations = std::atoi(argv[++i]);
|
|
}
|
|
else if((std::strcmp(argv[i], "-c") == 0 ||
|
|
std::strcmp(argv[i], "--copies") == 0) &&
|
|
i + 1 < argc)
|
|
{
|
|
num_copies = std::atoi(argv[++i]);
|
|
}
|
|
else if((std::strcmp(argv[i], "-d") == 0 ||
|
|
std::strcmp(argv[i], "--device") == 0) &&
|
|
i + 1 < argc)
|
|
{
|
|
device_id = std::atoi(argv[++i]);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
|
print_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
size_t size_bytes = size_mb * 1024ULL * 1024ULL;
|
|
|
|
// Setup signal handler for Ctrl+C
|
|
signal(SIGINT, signal_handler);
|
|
|
|
printf("SDMA Test Configuration:\n");
|
|
printf(" Transfer size: %zu MB (%zu bytes)\n", size_mb, size_bytes);
|
|
printf(" Iterations: %d%s\n", iterations, iterations == 0 ? " (infinite)" : "");
|
|
printf(" Copies per iteration: %d\n", num_copies);
|
|
printf(" Device ID: %d\n", device_id);
|
|
if(iterations == 0)
|
|
{
|
|
printf(" Press Ctrl+C to stop\n");
|
|
}
|
|
printf("\n");
|
|
|
|
// Set device
|
|
HIP_CHECK(hipSetDevice(device_id));
|
|
|
|
// Get device properties
|
|
hipDeviceProp_t props;
|
|
HIP_CHECK(hipGetDeviceProperties(&props, device_id));
|
|
printf("Using GPU: %s\n\n", props.name);
|
|
|
|
// Allocate host memory (pinned for better DMA performance)
|
|
void* h_src = nullptr;
|
|
void* h_dst = nullptr;
|
|
HIP_CHECK(hipHostMalloc(&h_src, size_bytes, hipHostMallocDefault));
|
|
HIP_CHECK(hipHostMalloc(&h_dst, size_bytes, hipHostMallocDefault));
|
|
|
|
// Initialize source data
|
|
memset(h_src, 0xAB, size_bytes);
|
|
memset(h_dst, 0x00, size_bytes);
|
|
|
|
// Allocate device memory
|
|
void* d_buf1 = nullptr;
|
|
void* d_buf2 = nullptr;
|
|
HIP_CHECK(hipMalloc(&d_buf1, size_bytes));
|
|
HIP_CHECK(hipMalloc(&d_buf2, size_bytes));
|
|
|
|
// Create stream for async operations
|
|
hipStream_t stream;
|
|
HIP_CHECK(hipStreamCreate(&stream));
|
|
|
|
int round = 0;
|
|
double total_h2d_bw = 0.0;
|
|
double total_d2d_bw = 0.0;
|
|
double total_d2h_bw = 0.0;
|
|
bool infinite_mode = (iterations == 0);
|
|
int target_rounds = infinite_mode ? INT32_MAX : iterations;
|
|
|
|
while(g_running && round < target_rounds)
|
|
{
|
|
round++;
|
|
printf("=== Iteration %d", round);
|
|
if(!infinite_mode)
|
|
{
|
|
printf("/%d", iterations);
|
|
}
|
|
printf(" ===\n");
|
|
|
|
// H2D
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
for(int i = 0; i < num_copies && g_running; i++)
|
|
{
|
|
HIP_CHECK(
|
|
hipMemcpyAsync(d_buf1, h_src, size_bytes, hipMemcpyHostToDevice, stream));
|
|
}
|
|
HIP_CHECK(hipStreamSynchronize(stream));
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
double elapsed = std::chrono::duration<double>(end - start).count();
|
|
double bw = (double) (size_bytes * num_copies) / elapsed / 1e9;
|
|
total_h2d_bw += bw;
|
|
printf(" H2D: %d x %zu MB in %.3f s (%.2f GB/s)\n", num_copies, size_mb, elapsed,
|
|
bw);
|
|
|
|
if(!g_running) break;
|
|
|
|
// D2D
|
|
start = std::chrono::high_resolution_clock::now();
|
|
for(int i = 0; i < num_copies && g_running; i++)
|
|
{
|
|
HIP_CHECK(hipMemcpyAsync(d_buf2, d_buf1, size_bytes, hipMemcpyDeviceToDevice,
|
|
stream));
|
|
}
|
|
HIP_CHECK(hipStreamSynchronize(stream));
|
|
end = std::chrono::high_resolution_clock::now();
|
|
elapsed = std::chrono::duration<double>(end - start).count();
|
|
bw = (double) (size_bytes * num_copies) / elapsed / 1e9;
|
|
total_d2d_bw += bw;
|
|
printf(" D2D: %d x %zu MB in %.3f s (%.2f GB/s)\n", num_copies, size_mb, elapsed,
|
|
bw);
|
|
|
|
if(!g_running) break;
|
|
|
|
// D2H
|
|
start = std::chrono::high_resolution_clock::now();
|
|
for(int i = 0; i < num_copies && g_running; i++)
|
|
{
|
|
HIP_CHECK(
|
|
hipMemcpyAsync(h_dst, d_buf2, size_bytes, hipMemcpyDeviceToHost, stream));
|
|
}
|
|
HIP_CHECK(hipStreamSynchronize(stream));
|
|
end = std::chrono::high_resolution_clock::now();
|
|
elapsed = std::chrono::duration<double>(end - start).count();
|
|
bw = (double) (size_bytes * num_copies) / elapsed / 1e9;
|
|
total_d2h_bw += bw;
|
|
printf(" D2H: %d x %zu MB in %.3f s (%.2f GB/s)\n\n", num_copies, size_mb,
|
|
elapsed, bw);
|
|
}
|
|
|
|
// Print summary
|
|
printf("=== Summary ===\n");
|
|
printf("Completed %d iteration(s)\n", round);
|
|
if(round > 0)
|
|
{
|
|
printf("Average bandwidth:\n");
|
|
printf(" H2D: %.2f GB/s\n", total_h2d_bw / round);
|
|
printf(" D2D: %.2f GB/s\n", total_d2d_bw / round);
|
|
printf(" D2H: %.2f GB/s\n", total_d2h_bw / round);
|
|
}
|
|
|
|
// Verify data integrity
|
|
printf("\nData verification: ");
|
|
if(memcmp(h_src, h_dst, size_bytes) == 0)
|
|
{
|
|
printf("PASSED\n");
|
|
}
|
|
else
|
|
{
|
|
printf("FAILED\n");
|
|
}
|
|
|
|
// Cleanup
|
|
HIP_CHECK(hipStreamDestroy(stream));
|
|
HIP_CHECK(hipFree(d_buf1));
|
|
HIP_CHECK(hipFree(d_buf2));
|
|
HIP_CHECK(hipHostFree(h_src));
|
|
HIP_CHECK(hipHostFree(h_dst));
|
|
|
|
return 0;
|
|
}
|