Skip to content

Instantly share code, notes, and snippets.

@Hermann-SW
Created August 12, 2026 09:53
Show Gist options
  • Select an option

  • Save Hermann-SW/595702a4e87f59408b534c004a1bb18e to your computer and use it in GitHub Desktop.

Select an option

Save Hermann-SW/595702a4e87f59408b534c004a1bb18e to your computer and use it in GitHub Desktop.
Multi GPU INT4/INT8/FP16 benchmark from long gemini session
#include <hip/hip_runtime.h>
#include <hip/hip_fp16.h>
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
#include <iomanip>
#include <atomic>
#include <string>
enum PrecisionMode {
PREC_INT4,
PREC_INT8,
PREC_FP16
};
// ---------------------------------------------------------------------------
// Compute Kernels
// ---------------------------------------------------------------------------
__global__ void __launch_bounds__(256, 2) mi50_int4_kernel(uint64_t iterations, int* dummy_out) {
int src0 = 0x12345678;
int src1 = 0x87654321;
int acc0 = 0, acc1 = 0, acc2 = 0, acc3 = 0;
int acc4 = 0, acc5 = 0, acc6 = 0, acc7 = 0;
#pragma unroll 1
for (uint64_t i = 0; i < iterations; ++i) {
#pragma unroll
for (int k = 0; k < 16; ++k) {
acc0 = __builtin_amdgcn_sdot8(src0, src1, acc0, false);
acc1 = __builtin_amdgcn_sdot8(src0, src1, acc1, false);
acc2 = __builtin_amdgcn_sdot8(src0, src1, acc2, false);
acc3 = __builtin_amdgcn_sdot8(src0, src1, acc3, false);
acc4 = __builtin_amdgcn_sdot8(src0, src1, acc4, false);
acc5 = __builtin_amdgcn_sdot8(src0, src1, acc5, false);
acc6 = __builtin_amdgcn_sdot8(src0, src1, acc6, false);
acc7 = __builtin_amdgcn_sdot8(src0, src1, acc7, false);
}
}
if (threadIdx.x == 0 && blockIdx.x == 0) {
*dummy_out = acc0 + acc1 + acc2 + acc3 + acc4 + acc5 + acc6 + acc7;
}
}
__global__ void __launch_bounds__(256, 2) mi50_int8_kernel(uint64_t iterations, int* dummy_out) {
int src0 = 0x12345678;
int src1 = 0x87654321;
int acc0 = 0, acc1 = 0, acc2 = 0, acc3 = 0;
int acc4 = 0, acc5 = 0, acc6 = 0, acc7 = 0;
#pragma unroll 1
for (uint64_t i = 0; i < iterations; ++i) {
#pragma unroll
for (int k = 0; k < 16; ++k) {
acc0 = __builtin_amdgcn_sdot4(src0, src1, acc0, false);
acc1 = __builtin_amdgcn_sdot4(src0, src1, acc1, false);
acc2 = __builtin_amdgcn_sdot4(src0, src1, acc2, false);
acc3 = __builtin_amdgcn_sdot4(src0, src1, acc3, false);
acc4 = __builtin_amdgcn_sdot4(src0, src1, acc4, false);
acc5 = __builtin_amdgcn_sdot4(src0, src1, acc5, false);
acc6 = __builtin_amdgcn_sdot4(src0, src1, acc6, false);
acc7 = __builtin_amdgcn_sdot4(src0, src1, acc7, false);
}
}
if (threadIdx.x == 0 && blockIdx.x == 0) {
*dummy_out = acc0 + acc1 + acc2 + acc3 + acc4 + acc5 + acc6 + acc7;
}
}
__global__ void __launch_bounds__(256, 2) mi50_fp16_kernel(uint64_t iterations, half2* dummy_out) {
half2 src0 = __float2half2_rn(1.001f);
half2 src1 = __float2half2_rn(0.999f);
half2 acc0 = __float2half2_rn(0.1f);
half2 acc1 = __float2half2_rn(0.2f);
half2 acc2 = __float2half2_rn(0.3f);
half2 acc3 = __float2half2_rn(0.4f);
half2 acc4 = __float2half2_rn(0.5f);
half2 acc5 = __float2half2_rn(0.6f);
half2 acc6 = __float2half2_rn(0.7f);
half2 acc7 = __float2half2_rn(0.8f);
#pragma unroll 1
for (uint64_t i = 0; i < iterations; ++i) {
#pragma unroll
for (int k = 0; k < 16; ++k) {
acc0 = __hfma2(src0, src1, acc0);
acc1 = __hfma2(src0, src1, acc1);
acc2 = __hfma2(src0, src1, acc2);
acc3 = __hfma2(src0, src1, acc3);
acc4 = __hfma2(src0, src1, acc4);
acc5 = __hfma2(src0, src1, acc5);
acc6 = __hfma2(src0, src1, acc6);
acc7 = __hfma2(src0, src1, acc7);
}
}
if (threadIdx.x == 0 && blockIdx.x == 0) {
*dummy_out = acc0 + acc1 + acc2 + acc3 + acc4 + acc5 + acc6 + acc7;
}
}
// ---------------------------------------------------------------------------
// Worker Infrastructure
// ---------------------------------------------------------------------------
struct GpuResult {
int dev_id;
std::string name;
float kernel_ms;
double tops;
double total_ops;
bool success;
};
void worker_thread(int dev_id, PrecisionMode mode, uint64_t iterations, GpuResult* result,
std::atomic<int>& ready_count, std::atomic<bool>& start_flag) {
result->dev_id = dev_id;
result->success = false;
if (hipSetDevice(dev_id) != hipSuccess) return;
hipDeviceProp_t prop;
if (hipGetDeviceProperties(&prop, dev_id) != hipSuccess) return;
result->name = prop.name;
const int num_blocks = 240; // 60 CUs * 4 blocks
const int threads_per_block = 256;
const double total_threads = (double)num_blocks * threads_per_block;
double ops_per_thread_per_iter = 0.0;
if (mode == PREC_INT4) ops_per_thread_per_iter = 128.0 * 16.0; // 2048 ops
else if (mode == PREC_INT8) ops_per_thread_per_iter = 128.0 * 8.0; // 1024 ops
else if (mode == PREC_FP16) ops_per_thread_per_iter = 128.0 * 4.0; // 512 FLOPs
result->total_ops = ops_per_thread_per_iter * (double)iterations * total_threads;
void* d_out = nullptr;
if (hipMalloc(&d_out, sizeof(double)) != hipSuccess) return;
// Warmup pass
if (mode == PREC_INT4) {
mi50_int4_kernel<<<num_blocks, threads_per_block>>>(10000ULL, (int*)d_out);
} else if (mode == PREC_INT8) {
mi50_int8_kernel<<<num_blocks, threads_per_block>>>(10000ULL, (int*)d_out);
} else if (mode == PREC_FP16) {
mi50_fp16_kernel<<<num_blocks, threads_per_block>>>(10000ULL, (half2*)d_out);
}
if (hipDeviceSynchronize() != hipSuccess) {
hipFree(d_out);
return;
}
hipEvent_t start, stop;
hipEventCreate(&start);
hipEventCreate(&stop);
ready_count++;
while (!start_flag.load(std::memory_order_acquire)) {
std::this_thread::yield();
}
// Synchronized launch
hipEventRecord(start);
if (mode == PREC_INT4) {
mi50_int4_kernel<<<num_blocks, threads_per_block>>>(iterations, (int*)d_out);
} else if (mode == PREC_INT8) {
mi50_int8_kernel<<<num_blocks, threads_per_block>>>(iterations, (int*)d_out);
} else if (mode == PREC_FP16) {
mi50_fp16_kernel<<<num_blocks, threads_per_block>>>(iterations, (half2*)d_out);
}
hipEventRecord(stop);
hipDeviceSynchronize();
float milliseconds = 0;
hipEventElapsedTime(&milliseconds, start, stop);
double seconds = milliseconds / 1000.0;
result->kernel_ms = milliseconds;
result->tops = (result->total_ops / seconds) / 1e12;
result->success = true;
hipEventDestroy(start);
hipEventDestroy(stop);
hipFree(d_out);
}
double run_benchmark_pass(int device_count, PrecisionMode mode, const std::string& label, uint64_t iterations) {
std::cout << "=============================================================================\n";
std::cout << " RUNNING " << label << " BENCHMARK PASS (" << iterations << " iterations)\n";
std::cout << "=============================================================================\n";
std::vector<GpuResult> results(device_count);
std::vector<std::thread> threads;
std::atomic<int> ready_count(0);
std::atomic<bool> start_flag(false);
for (int i = 0; i < device_count; ++i) {
threads.emplace_back(worker_thread, i, mode, iterations, &results[i],
std::ref(ready_count), std::ref(start_flag));
}
while (ready_count.load(std::memory_order_relaxed) < device_count) {
std::this_thread::yield();
}
auto wall_start = std::chrono::high_resolution_clock::now();
start_flag.store(true, std::memory_order_release);
for (auto& t : threads) {
t.join();
}
auto wall_stop = std::chrono::high_resolution_clock::now();
double wall_seconds = std::chrono::duration<double>(wall_stop - wall_start).count();
std::cout << std::left << std::setw(10) << "Logical ID"
<< std::setw(30) << "Device Name"
<< std::setw(16) << "Time (ms)"
<< std::setw(16) << (mode == PREC_FP16 ? "Per-GPU TFLOPS" : "Per-GPU TOPS") << "\n";
std::cout << std::string(72, '-') << "\n";
double sum_ops = 0.0;
int active_gpus = 0;
for (const auto& r : results) {
if (r.success) {
std::cout << std::left << std::setw(10) << r.dev_id
<< std::setw(30) << r.name
<< std::setw(16) << std::fixed << std::setprecision(2) << r.kernel_ms
<< std::setw(16) << std::setprecision(2) << r.tops << "\n";
sum_ops += r.total_ops;
active_gpus++;
}
}
std::cout << std::string(72, '-') << "\n";
double aggregate_throughput = (sum_ops / wall_seconds) / 1e12;
std::cout << "Active GPUs: " << active_gpus << " / " << device_count << "\n";
std::cout << "Concurrent Wall Time: " << std::fixed << std::setprecision(3) << wall_seconds * 1000.0 << " ms\n";
std::cout << "AGGREGATE SYSTEM " << label << ": " << std::setprecision(2) << aggregate_throughput
<< (mode == PREC_FP16 ? " TFLOPS" : " TOPS") << "\n\n";
return aggregate_throughput;
}
int main() {
int device_count = 0;
if (hipGetDeviceCount(&device_count) != hipSuccess || device_count == 0) {
std::cerr << "No valid HIP devices found." << std::endl;
return 1;
}
std::cout << "Dispatched multi-precision benchmark across " << device_count << " visible GPU device(s)...\n\n";
const uint64_t iterations = 1000000ULL;
double agg_int4 = run_benchmark_pass(device_count, PREC_INT4, "INT4", iterations);
double agg_int8 = run_benchmark_pass(device_count, PREC_INT8, "INT8", iterations);
double agg_fp16 = run_benchmark_pass(device_count, PREC_FP16, "FP16", iterations);
std::cout << "=============================================================================\n";
std::cout << " SYSTEM SUMMARY PERFORMANCE \n";
std::cout << "=============================================================================\n";
std::cout << " INT4 Aggregate Compute: " << std::fixed << std::setprecision(2) << agg_int4 << " TOPS\n";
std::cout << " INT8 Aggregate Compute: " << std::fixed << std::setprecision(2) << agg_int8 << " TOPS\n";
std::cout << " FP16 Aggregate Compute: " << std::fixed << std::setprecision(2) << agg_fp16 << " TFLOPS\n";
std::cout << "=============================================================================\n";
return 0;
}
@Hermann-SW

Copy link
Copy Markdown
Author

It is important that iGPU is disabled either in bios, or through udev rules or other means.
On my system only the 8 MI50s are reported.
The one without MI50 in name (later) and with 225W power cap is my only 32GB MI50, the others are 16GB:

hermann@7600x:~$ rocm-smi 


============================================ ROCm System Management Interface ============================================
====================================================== Concise Info ======================================================
Device  Node  IDs              Temp    Power     Partitions          SCLK     MCLK     Fan     Perf  PwrCap  VRAM%  GPU%  
              (DID,     GUID)  (Edge)  (Socket)  (Mem, Compute, ID)                                                       
==========================================================================================================================
0       1     0x66a1,   45820  32.0°C  30.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  250.0W  0%     0%    
1       2     0x66a1,   29947  34.0°C  99.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  250.0W  0%     0%    
2       3     0x66a1,   10040  35.0°C  40.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  250.0W  0%     0%    
3       4     0x66a1,   8048   36.0°C  40.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  250.0W  0%     0%    
4       5     0x66a1,   19635  36.0°C  42.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  250.0W  0%     0%    
5       6     0x66a1,   48627  36.0°C  35.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  250.0W  0%     0%    
6       7     0x66a1,   10295  36.0°C  32.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  250.0W  0%     0%    
7       8     0x66a1,   59764  34.0°C  89.0W     N/A, N/A, 0         1725Mhz  1000Mhz  14.51%  high  225.0W  0%     0%    
==========================================================================================================================
================================================== End of ROCm SMI Log ===================================================
hermann@7600x:~$ 

@Hermann-SW

Hermann-SW commented Aug 12, 2026

Copy link
Copy Markdown
Author

Nice INT4+INT8 TOPS, and nice FP16 TFLOPS.
It started with gemini working on INT4 benchmark demonstrating 100 TOPS INT4.
Because I had problem with one MI50 I showed gemini rocm-smi output.
After the problem was resolved, gemini asked whether I wanted to extend to 800 TOPS multi-GPU benchmark.
Later it asked me whether I wanted to see benchmark for INT8 or FP16 as well.
I said that I wanted a combined benchmark for INT4/INT8/FP16, and the result is this gist.
Really nice summary from below:

=============================================================================
                       SYSTEM SUMMARY PERFORMANCE                            
=============================================================================
 INT4 Aggregate Compute:  799.58 TOPS
 INT8 Aggregate Compute:  399.87 TOPS
 FP16 Aggregate Compute:  207.80 TFLOPS
=============================================================================
hermann@7600x:~$ sudo rocm-smi --setperflevel high
...
GPU[6]		: Performance level set to high
GPU[7]		: Performance level set to high
==========================================================================================
================================== End of ROCm SMI Log ===================================
hermann@7600x:~$ HSA_OVERRIDE_GFX_VERSION=9.0.6 ./mi50_multi_precision_bench
Dispatched multi-precision benchmark across 8 visible GPU device(s)...

=============================================================================
 RUNNING INT4 BENCHMARK PASS (1000000 iterations)
=============================================================================
Logical IDDevice Name                   Time (ms)       Per-GPU TOPS    
------------------------------------------------------------------------
0         AMD Instinct MI50/MI60        1258.34         100.00          
1         AMD Instinct MI50/MI60        1258.34         100.00          
2         AMD Instinct MI50/MI60        1258.34         100.00          
3         AMD Instinct MI50/MI60        1258.34         100.00          
4         AMD Instinct MI50/MI60        1258.39         99.99           
5         AMD Instinct MI50/MI60        1258.34         100.00          
6         AMD Instinct MI50/MI60        1258.33         100.00          
7         AMD Radeon Graphics           1258.33         100.00          
------------------------------------------------------------------------
Active GPUs:            8 / 8
Concurrent Wall Time:   1258.953 ms
AGGREGATE SYSTEM INT4: 799.58 TOPS

=============================================================================
 RUNNING INT8 BENCHMARK PASS (1000000 iterations)
=============================================================================
Logical IDDevice Name                   Time (ms)       Per-GPU TOPS    
------------------------------------------------------------------------
0         AMD Instinct MI50/MI60        1258.45         49.99           
1         AMD Instinct MI50/MI60        1258.34         50.00           
2         AMD Instinct MI50/MI60        1258.34         50.00           
3         AMD Instinct MI50/MI60        1258.34         50.00           
4         AMD Instinct MI50/MI60        1258.57         49.99           
5         AMD Instinct MI50/MI60        1258.35         50.00           
6         AMD Instinct MI50/MI60        1258.33         50.00           
7         AMD Radeon Graphics           1258.33         50.00           
------------------------------------------------------------------------
Active GPUs:            8 / 8
Concurrent Wall Time:   1258.709 ms
AGGREGATE SYSTEM INT8: 399.87 TOPS

=============================================================================
 RUNNING FP16 BENCHMARK PASS (1000000 iterations)
=============================================================================
Logical IDDevice Name                   Time (ms)       Per-GPU TFLOPS  
------------------------------------------------------------------------
0         AMD Instinct MI50/MI60        1210.49         25.99           
1         AMD Instinct MI50/MI60        1210.50         25.99           
2         AMD Instinct MI50/MI60        1210.50         25.99           
3         AMD Instinct MI50/MI60        1210.50         25.99           
4         AMD Instinct MI50/MI60        1210.51         25.99           
5         AMD Instinct MI50/MI60        1210.51         25.99           
6         AMD Instinct MI50/MI60        1210.49         25.99           
7         AMD Radeon Graphics           1210.49         25.99           
------------------------------------------------------------------------
Active GPUs:            8 / 8
Concurrent Wall Time:   1211.065 ms
AGGREGATE SYSTEM FP16: 207.80 TFLOPS

=============================================================================
                       SYSTEM SUMMARY PERFORMANCE                            
=============================================================================
 INT4 Aggregate Compute:  799.58 TOPS
 INT8 Aggregate Compute:  399.87 TOPS
 FP16 Aggregate Compute:  207.80 TFLOPS
=============================================================================
hermann@7600x:~$ 

@Hermann-SW

Hermann-SW commented Aug 15, 2026

Copy link
Copy Markdown
Author

Including two more gfx906 type GPUs (MI50 is gfx906 as well) via OpenMP did show 1.0003 POPS (peta operations per second)!
https://github.com/Hermann-SW/1.0003-POPS

=============================================================================
                   TOTAL 10-GPU CLUSTER PERFORMANCE                          
=============================================================================
 Combined INT4 Compute:  1000.37 TOPS
 Combined INT8 Compute:  500.05 TOPS
 Combined FP16 Compute:  260.13 TFLOPS
=============================================================================

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment