Not every routine has every sweep companion — they exist only where that
parameter was actually worth isolating (e.g. trans.strmv and lda.strmv
exist; ldb.<routine> currently exists only for sgemm, where transB/
ldb turned out to matter but transA/lda didn't).
Benchmarking Pattern
A wgblas benchmark and its cuBLAS counterpart always share the same
WARMUP_ITERS/BENCH_ITERS/SIZES for that one routine, so the two are
directly comparable — but those numbers scale down by BLAS level, since
work grows from O(n) to O(n²) to O(n³) and a Level 3 sweep at Level 1's
iteration count would take far too long to run:
That's the default per level, not a hard rule — a routine whose
performance profile doesn't fit gets its own numbers. strsm, for
instance, is dispatch-count-dominated (sequential block dependency in the
triangular solve) rather than compute-bound like sgemm, so it uses
WARMUP_ITERS=5, BENCH_ITERS=30, and its own SIZES — the same reasoning
strsv applied at Level 2 to decide which parameters get a dedicated sweep,
even though strsv itself keeps Level 2's standard iteration counts. It
also reports GB/s only, no GFLOP/s, unlike every other Level 3 routine —
see below. Check the routine's own wgblas/<routine>.js for its actual
constants rather than assuming the table above.
What's common across every routine at every level, regardless of that
table:
Data stays on GPU — vectors/matrices are allocated with
GpuVector.from()/GpuMatrix.from() (JS) or cudaMalloc (C) before the
timed loop. The measured time is pure compute; no host↔device transfer
is included.
Timed iterations are individually timed; the median is recorded.
Median suppresses outliers from OS scheduler preemption better than mean.
Throughput — bytes / 1e9 / (median_ms / 1e3) where bytes is the
total memory traffic for the routine (e.g. saxpy reads x, reads and
writes y → 3 × n × 4 bytes). Level 3 routines are compute-bound, not
just memory-bound, so they additionally report GFLOP/s: sgemm's is
(2×m×n×k + 2×m×n) / 1e9 / (median_ms / 1e3).
init({ benchmark: true }) — enables WebGPU timestamp queries so the
JS benchmark measures actual GPU execution time, not the CPU-side await
overhead.
Make Commands
Command
What it does
make bench
Runs every wgblas/*.js benchmark (base + all sweep companions) in sequence
make bench-<routine>
Runs one routine's base wgblas benchmark, e.g. make bench-saxpy
make bench-stride-<routine>
Runs that routine's stride-sweep companion
make bench-trans-<routine>
Runs that routine's trans-sweep companion
make bench-lda-<routine>
Runs that routine's lda-sweep companion
make bench-ldb-<routine>
Runs that routine's ldb-sweep companion
make bench-uplo-<routine>
Runs that routine's uplo-sweep companion
make cuda
Compiles and runs every cuBLAS benchmark (NVIDIA only)
make cuda-<routine>
Compiles and runs one routine's base cuBLAS benchmark
make cuda-stride-<routine> / -trans- / -lda- / -ldb- / -uplo-
Same sweep companions, cuBLAS side
Pass ARGS=low-power to prefer the integrated GPU on dual-GPU machines:
make bench-saxpy ARGS=low-power.
Results are saved automatically under benchmarks/results/<gpu-model>/,
following the same wgblas/ vs cuda/, flat-vs-nested layout described
above.
Contributing Results
Run make bench (and make cuda on NVIDIA).
Commit the generated JSON files under benchmarks/results/.
Open a pull request — a maintainer will run scripts/gen-bench-tables.py
and commit the generated .mjs/.svg files before merging. See the
scripts module for what that script does and its arguments.
Per-routine GPU benchmarks for wgblas — a WebGPU (JS) benchmark and a cuBLAS (C/CUDA) reference benchmark for BLAS Level 1, 2, and 3 routines.
Folder Structure
Not every routine has every sweep companion — they exist only where that parameter was actually worth isolating (e.g.
trans.strmvandlda.strmvexist;ldb.<routine>currently exists only forsgemm, wheretransB/ldbturned out to matter buttransA/ldadidn't).Benchmarking Pattern
A wgblas benchmark and its cuBLAS counterpart always share the same
WARMUP_ITERS/BENCH_ITERS/SIZESfor that one routine, so the two are directly comparable — but those numbers scale down by BLAS level, since work grows from O(n) to O(n²) to O(n³) and a Level 3 sweep at Level 1's iteration count would take far too long to run:n— 12 element counts,32to16777216m=n— 9 square dimensions,32to4096(4096² ≈ 16.8M elements, matching Level 1's max)m=n=k— 6 square dimensions,32to1024That's the default per level, not a hard rule — a routine whose performance profile doesn't fit gets its own numbers.
strsm, for instance, is dispatch-count-dominated (sequential block dependency in the triangular solve) rather than compute-bound likesgemm, so it usesWARMUP_ITERS=5,BENCH_ITERS=30, and its ownSIZES— the same reasoningstrsvapplied at Level 2 to decide which parameters get a dedicated sweep, even thoughstrsvitself keeps Level 2's standard iteration counts. It also reports GB/s only, no GFLOP/s, unlike every other Level 3 routine — see below. Check the routine's ownwgblas/<routine>.jsfor its actual constants rather than assuming the table above.What's common across every routine at every level, regardless of that table:
GpuVector.from()/GpuMatrix.from()(JS) orcudaMalloc(C) before the timed loop. The measured time is pure compute; no host↔device transfer is included.bytes / 1e9 / (median_ms / 1e3)wherebytesis the total memory traffic for the routine (e.g. saxpy readsx, reads and writesy→3 × n × 4 bytes). Level 3 routines are compute-bound, not just memory-bound, so they additionally report GFLOP/s: sgemm's is(2×m×n×k + 2×m×n) / 1e9 / (median_ms / 1e3).init({ benchmark: true })— enables WebGPU timestamp queries so the JS benchmark measures actual GPU execution time, not the CPU-side await overhead.Make Commands
make benchwgblas/*.jsbenchmark (base + all sweep companions) in sequencemake bench-<routine>make bench-saxpymake bench-stride-<routine>make bench-trans-<routine>make bench-lda-<routine>make bench-ldb-<routine>make bench-uplo-<routine>make cudamake cuda-<routine>make cuda-stride-<routine>/-trans-/-lda-/-ldb-/-uplo-Pass
ARGS=low-powerto prefer the integrated GPU on dual-GPU machines:make bench-saxpy ARGS=low-power.Results are saved automatically under
benchmarks/results/<gpu-model>/, following the samewgblas/vscuda/, flat-vs-nested layout described above.Contributing Results
make bench(andmake cudaon NVIDIA).benchmarks/results/.scripts/gen-bench-tables.pyand commit the generated.mjs/.svgfiles before merging. See thescriptsmodule for what that script does and its arguments.