Tutorial
Matrix Multiplication
Context-recorded matrix multiplication in OA. Allocate matrices on the GPU, record OaFnMatrix::MatMul, execute through OaContext, validate correctness across shape archetypes, and measure throughput against a reference implementation.
| API level | Level 1 Core — OaContext + OaFnMatrix |
| User-facing op | OaFnMatrix::MatMul(a, b) — shape convention: A[M,K] · B[N,K]ᵀ → C[M,N] |
| GPU path | BF16 CoopMat (tensor cores) on NVIDIA/AMD, FP32 tiled on Intel iGPU |
| Peak throughput | 19.2 TFLOP/s at 2048³ — 30.0% of RTX 5090 BF16 theoretical peak |
| Batch speedup | 4–8× vs single-dispatch for small ops (amortized submit overhead) |
| Cross-vendor | Same binary: RTX 5090, Intel ARL iGPU — OA_DEVICE=integrated |
| Source | Tutorial/Core/TutorialCoreMatMulIntro.cpp |
1. Basic Syntax
Tensors are created and filled on the GPU (Rand, RandN, Full, Zeros). Host memory is touched only to read results back — for validation or inspection — never to upload input data. Execution happens at the OaContext::Scope boundary.
Tutorialcorematmulintro.cpp
// Inputs generated ON THE GPU — no host upload.OaMatrix a = OaFnMatrix::Rand(OaShape2D(M, K));OaMatrix b = OaFnMatrix::Rand(OaShape2D(N, K));OaMatrix c;{OaContext::Scope scope(OaContext::GetDefault());c = OaFnMatrix::MatMul(a, b); // C = A @ B^T, shape [M, N]} // scope exit: Execute() + Sync()// Read result back to host (validation only).std::vector<float> host(M * N);OaFnMatrix::CopyToHost(c, host.data(), host.size() * sizeof(float));
The shape convention matches current OA GEMM: A is [M, K],B is stored as [N, K], result is [M, N]. The GEMM router, tuner, and cache are internal — the user never calls them directly.
2. Correctness Pass
Start with small deterministic shapes and compare every output element against a CPU reference. Use normalized error to avoid false failures near zero:
Tutorialcorematmulintro.cpp
for (OaU32 m = 0; m < M; ++m) {for (OaU32 n = 0; n < N; ++n) {float sum = 0.0f;for (OaU32 k = 0; k < K; ++k) {sum += aHost[m * K + k] * bHost[n * K + k];}cRef[m * N + n] = sum;}}// Normalized error (handles near-zero output entries)float norm_err = max_i|ref[i] - gpu[i]| / max(max_i|ref[i]|, 1e-6f);
Measured Correctness — RTX 5090 Laptop (BF16 CoopMat, tol = 3e-2)
| Shape | M | N | K | norm_err | Result |
|---|---|---|---|---|---|
| tiny-square | 8 | 8 | 8 | 5.46e-03 | ✓ |
| square | 128 | 128 | 128 | 6.93e-04 | ✓ |
| tall-skinny | 512 | 64 | 128 | 6.84e-04 | ✓ |
| short-wide | 64 | 512 | 128 | 6.56e-04 | ✓ |
| irregular | 100 | 130 | 77 | 5.54e-03 | ✓ |
| gemv-decode | 1 | 256 | 256 | 6.12e-07 | ✓ |
| mnist-hidden | 64 | 128 | 784 | 6.52e-04 | ✓ |
| mnist-logits | 64 | 10 | 128 | 5.36e-03 | ✓ |
All shapes land at norm_err ≈ 5e-3, consistent with BF16's 7-bit mantissa — well inside the 3e-2 tolerance. On FP32-only devices the router picks tiled/naive kernels and norm_err drops below 1e-4.
3. Performance Pass
After correctness, run timed shapes that exercise the router. Each shape runs 50 iterations after warmup. GFLOP/s = 2·M·N·K / wall_time.
Single-Dispatch Throughput — RTX 5090 Laptop (BF16 CoopMat, 50 iters)
| Shape | M | N | K | Wall ms | GFLOP/s | % of Peak BF16 |
|---|---|---|---|---|---|---|
| square-512 | 512 | 512 | 512 | 0.082 | 3,280 | 5.1% |
| square-1024 | 1024 | 1024 | 1024 | 0.138 | 15,548 | 24.3% |
| square-2048 | 2048 | 2048 | 2048 | 0.897 | 19,159 | 30.0% |
| tall-skinny | 4096 | 128 | 1024 | 0.153 | 7,004 | 10.9% |
| short-wide | 128 | 4096 | 1024 | 0.135 | 7,958 | 12.4% |
| gemv-decode | 1 | 4096 | 4096 | 0.903 | 37 | 0.1% |
Device theoretical peak: 64 TFLOPS (BF16 tensor cores). Throughput climbs with problem size as fixed per-dispatch overhead amortizes, peaking near 19.2 TFLOP/s at 2048³. The gemv-decode row is memory-bound — a single output row can't saturate tensor cores. This is the autoregressive single-token path.
4. Batch Dispatch
Recording multiple operations into a single OaContext scope before executing reduces CPU→GPU submission overhead. For small operations where GPU time ≈ submit latency, batching provides 4–8× speedup.
Tutorialcorematmulintro.cpp
// Batch dispatch: 4 operations, single submitOaVec<OaMatrix> results;for (int i = 0; i < 4; ++i) {results.PushBack(OaFnMatrix::MatMul(aVec[i], bVec[i]));}ctx.Execute(); // All 4 matmuls execute in one submissionctx.Sync();
| Shape | M | N | K | OA 5090 BF16 | OA Intel FP32 | Speedup vs Single |
|---|---|---|---|---|---|---|
| square-512 | 512 | 512 | 512 | 21,717 | 1,119 | 8.0× / 5.7× |
| square-1024 | 1024 | 1024 | 1024 | 30,483 | 1,176 | 5.7× / 3.8× |
| tall-skinny | 2048 | 128 | 1024 | 18,383 | 1,004 | 4.3× / 4.1× |
5. Cross-Stack Comparison
All measurements are wall-clock time including full public API path (submit + sync overhead).
| Shape | M | N | K | OA 5090 BF16 | OA Intel FP32 | Reference TF32 | OA / Reference |
|---|---|---|---|---|---|---|---|
| square-512 | 512 | 512 | 512 | 3,280 | 196 | 32,027 | 10.2% |
| square-1024 | 1024 | 1024 | 1024 | 15,548 | 312 | 42,142 | 36.9% |
| square-2048 | 2048 | 2048 | 2048 | 19,159 | 270 | 38,650 | 49.6% |
| tall-skinny | 4096 | 128 | 1024 | 7,004 | 244 | 37,377 | 18.7% |
| short-wide | 128 | 4096 | 1024 | 7,958 | 282 | 39,721 | 20.0% |
| gemv-decode | 1 | 4096 | 4096 | 37 | 4.5 | 420 | 8.8% |
OA BF16 CoopMat achieves 19.2 TFLOP/s at 2048³ (30.0% of 64 TFLOP theoretical peak). The reference TF32 implementation achieves 38.7 TFLOP/s (60% of peak). There is room for improvement through tile tuning and shared memory optimization — the kernel path uses the same tensor core hardware. Intel iGPU runs the same binary at 259 GFLOP/s (FP32 fallback, 10.4% of 2.5 TFLOP theoretical peak).
6. Cross-Device Portability
Same binary, same source — runtime device selection:
Run.sh
cmake --build Build/Release --target TutorialCoreMatMulIntro -j./Bin/Release/Tutorial/Core/TutorialCoreMatMulIntro# Intel iGPUOA_DEVICE=integrated ./Bin/Release/Tutorial/Core/TutorialCoreMatMulIntro
| RTX 5090 Laptop | Intel ARL iGPU | |
|---|---|---|
| Precision path | BF16 CoopMat (tensor cores) | FP32 tiled/naive |
| norm_err (2048³) | ~5e-3 | 0.00 (bit-exact) |
| Throughput (2048³) | 19.2 TFLOP/s | 259 GFLOP/s |
| Theoretical peak | 64 TFLOPS BF16 | 2.5 TFLOPS FP32 |
| % of peak (2048³) | 30.0% | 10.4% |
See also: detailed benchmark results with cross-stack comparison and batch-dispatch tables.