The benchmark suite under bench/ compares GLASS variants against block-level
CUDA library baselines and against each other, so you can see which path
wins for a given shape on your hardware.
| File | Comparison |
|---|---|
bench_reduce.cu |
glass::*::reduce/dot/nrm2 (plain, low_memory, high_speed,
compile-time) vs CUB BlockReduce vs glass::nvidia::block::reduce |
bench_gemv.cu |
glass::gemv (runtime + compile-time) vs raw cuBLASDx vs
glass::nvidia::block::gemv (default + caller-pinned BlockDim<256>) |
bench_gemm.cu |
glass::gemm (plain, tiled, compile-time) vs raw cuBLASDx vs
glass::nvidia::block::gemm (default + caller-pinned) |
bench_blockdim.cu |
glass::nvidia::block::gemm cuBLASDx-chosen block_dim vs caller-pinned
BlockDim<128> vs BlockDim<352> |
bench_gemm_batched.cu |
glass::nvidia::block::gemm_batched<...,BATCH> vs a naive for(b) loop, for
BATCH ∈ {4, 8, 16, 32} |
bench_gemm_batched_1d.cu |
1D-launch gemm_batched_1d (SIMT vs cuBLASDx) — feeds the autotune table |
bench_lapack.cu (needs cuSOLVERDx) |
pure-SIMT glass::potrf / trsm vs
glass::nvidia::block::potrf / trsm / posv (fused) |
CUB ships with CUDA 11+. cuBLASDx and cuSOLVERDx ship together in NVIDIA MathDx — see :doc:`../getting_started/installation`.
Set MATHDX_ROOT to your MathDx install, then:
cd /path/to/GLASS
python3 bench/run_bench.py
# Custom iteration count (default: 10000)
python3 bench/run_bench.py --iters 50000
# Skip cuBLASDx (only bench_reduce will run)
python3 bench/run_bench.py --no-cublasdxThe driver auto-detects dependencies and prints what it found at startup:
=== GLASS Benchmark Suite ===
GPU arch: sm_120 (SM1200)
cuBLASDx: enabled (/opt/nvidia/mathdx/25.12)
cuSOLVERDx: enabled
Iterations: 10000
bench_lapack is skipped automatically if cusolverdx.hpp is not present
under $MATHDX_ROOT/include/. When cuSOLVERDx is enabled, the driver adds the
-rdc=true -dlto -lcusolverdx ... device-link flags for you.
Results are printed as a Markdown table and saved to
bench/results/bench_<hostname>.json. Timing uses the GRiD pattern — the
iteration loop runs inside the kernel to amortize launch overhead.
Note
Anti-optimization safeguards are baked into every bench loop:
per-iteration writes to a volatile sink defeat dead-store elimination;
destructive inputs (Cholesky, LU, QR overwrite their input) are reloaded from
a master copy each iteration; nvcc -Xptxas -O1 is enforced. Numbers below
~0.1 µs/op for a non-trivial kernel almost always mean the bench was elided —
recheck the safeguards.
The reduction rows are end-to-end harness comparisons, not isolated primitive instruction counts. The raw CUB path reads its global input directly, while the GLASS paths include their harness staging and synchronization. Use those rows to compare the tested call paths; do not attribute the whole difference to the reduction primitive alone.
The same harness backs bench/autotune.py, which measures SIMT vs cuBLASDx
per shape and writes a per-host override table so the glass::nvidia::*
compile-time auto-dispatch picks the measured winner instead of the static
heuristic (the table is consulted at compile time — rebuild to apply). See
:doc:`../concepts/tuning` for that workflow.