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 levelLevel 1 Core — OaContext + OaFnMatrix
User-facing opOaFnMatrix::MatMul(a, b) — shape convention: A[M,K] · B[N,K]ᵀ → C[M,N]
GPU pathBF16 CoopMat (tensor cores) on NVIDIA/AMD, FP32 tiled on Intel iGPU
Peak throughput19.2 TFLOP/s at 2048³ — 30.0% of RTX 5090 BF16 theoretical peak
Batch speedup4–8× vs single-dispatch for small ops (amortized submit overhead)
Cross-vendorSame binary: RTX 5090, Intel ARL iGPU — OA_DEVICE=integrated
SourceTutorial/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)

ShapeMNKnorm_errResult
tiny-square8885.46e-03
square1281281286.93e-04
tall-skinny512641286.84e-04
short-wide645121286.56e-04
irregular100130775.54e-03
gemv-decode12562566.12e-07
mnist-hidden641287846.52e-04
mnist-logits64101285.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)

ShapeMNKWall msGFLOP/s% of Peak BF16
square-5125125125120.0823,2805.1%
square-10241024102410240.13815,54824.3%
square-20482048204820480.89719,15930.0%
tall-skinny409612810240.1537,00410.9%
short-wide128409610240.1357,95812.4%
gemv-decode1409640960.903370.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 submit
OaVec<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 submission
ctx.Sync();
ShapeMNKOA 5090 BF16OA Intel FP32Speedup vs Single
square-51251251251221,7171,1198.0× / 5.7×
square-102410241024102430,4831,1765.7× / 3.8×
tall-skinny2048128102418,3831,0044.3× / 4.1×

5. Cross-Stack Comparison

All measurements are wall-clock time including full public API path (submit + sync overhead).

ShapeMNKOA 5090 BF16OA Intel FP32Reference TF32OA / Reference
square-5125125125123,28019632,02710.2%
square-102410241024102415,54831242,14236.9%
square-204820482048204819,15927038,65049.6%
tall-skinny409612810247,00424437,37718.7%
short-wide128409610247,95828239,72120.0%
gemv-decode140964096374.54208.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 iGPU
OA_DEVICE=integrated ./Bin/Release/Tutorial/Core/TutorialCoreMatMulIntro
RTX 5090 LaptopIntel ARL iGPU
Precision pathBF16 CoopMat (tensor cores)FP32 tiled/naive
norm_err (2048³)~5e-30.00 (bit-exact)
Throughput (2048³)19.2 TFLOP/s259 GFLOP/s
Theoretical peak64 TFLOPS BF162.5 TFLOPS FP32
% of peak (2048³)30.0%10.4%

See also: detailed benchmark results with cross-stack comparison and batch-dispatch tables.