Every kernel in the case studies uses the same measurement pipeline. This page shows the code once so the walkthroughs can focus on what changes between kernels.
choreo::timing wraps the kernel in a warmup/repeat loop, excludes warmup from the average, and returns the mean elapsed time in milliseconds:
int warmup = 10;
int repeat = 500;
const char* warmup_env = std::getenv("CROQTILE_TIMING_WARMUP");
const char* repeat_env = std::getenv("CROQTILE_TIMING_REPEAT");
if (warmup_env) { int value = std::atoi(warmup_env); if (value >= 0) warmup = value; }
if (repeat_env) { int value = std::atoi(repeat_env); if (value > 0) repeat = value; }
choreo::TimerOption topt;
topt.warmup = warmup;
topt.repeat = repeat;
auto avg_ms = choreo::timing([&]() {
matmul(lhs_d, rhs_d, res_d);
cudaDeviceSynchronize();
}, topt);
std::cout << "Timing avg ms: " << avg_ms << "\n";The lambda includes cudaDeviceSynchronize() so the GPU finishes before the timer stops. Warmup lets caches, TLBs, and steady-state behavior settle.
For dense C = A × B with shapes (M, K) and (K, N), each of the M × N output elements requires K multiply-add pairs. A multiply-add counts as two FLOPs (one multiply, one add — often a single FMA instruction):
FLOPs = 2 × M × N × K
For an 8192³ GEMM: 2 × 8192³ = 1,099,511,627,776 ≈ 1.1 TFLOP of work per kernel call.
With the measured average time:
double flops = 2.0 * double(M) * double(N) * double(K);
double tflops = (flops / (avg_ms / 1000.0)) / 1e12;
std::cout << "TFLOPS: " << tflops << "\n";Sparse variants count effective multiply-adds for nonzeros. If a benchmark documents FLOPs differently (e.g. MACs as one op), align with that host program's formula.
TFLOPS alone is meaningless without a ceiling. The benchmarks print efficiency as a fraction of the documented GPU peak:
double eff = (tflops / H800_PCIE_PEAK_F16_TFLOPS) * 100.0;
std::cout << "HW efficiency: " << eff << "%\n";Reference peaks for H800 PCIe:
| Constant | Value | Use |
|---|---|---|
H800_PCIE_PEAK_F16_TFLOPS |
1513 TFLOPS | FP16 dense |
H800_PCIE_PEAK_F8_TFLOPS |
3026 TFLOPS | FP8 dense |
These are theoretical peaks; real kernels rarely hit 100%. In the case studies, we compare against cuBLAS on the same hardware (~380 TFLOPS for FP16 dense at 8192³) as a practical ceiling. Use the same peak constant before and after a change — you are comparing deltas, not final grades.
| Variable | Default | Effect |
|---|---|---|
CROQTILE_TIMING_WARMUP |
10 |
Warmup iterations (0 disables) |
CROQTILE_TIMING_REPEAT |
500 |
Timed iterations (must be > 0) |
CROQTILE_DISABLE_TIMING |
unset | Set to 1 to skip timing entirely |
CROQTILE_SKIP_VERIFY |
unset | Set to 1 to skip numerical verification |
Use CROQTILE_SKIP_VERIFY=1 only when you trust correctness. A fast wrong kernel sends the optimization search backward — always re-enable verification after changing data layout, precision, or tiling.
Performance .co files are built through the Croqtile driver. A typical invocation:
./croqtile -gs -t cute -arch=sm_90a --use-warpspec --stmatrix \
benchmark/performance/matmul/matmul_f16_dyn_sm90.co \
-o /tmp/matmul.cute.result && bash /tmp/matmul.cute.result --executeCommon SM90 flags: --use-warpspec, --stmatrix, --hoist-offset, --hoist-scale, --ptx-barrier, --tma-cluster-aware, --wgmma-wait-depth=N. Exact semantics live in Croqtile's CLI help. Copy the recipe from the benchmark you are reproducing, then vary one flag at a time.