Matrix multiplication

Build one GPU matrix product, validate it against an independent CPU reference, then measure representative shapes without confusing peak samples, replay throughput, or synchronized application latency.

TuCurrent contractC++ source-ownedCore
ContractValue
Operationoa::FnMatrix::matMulNt(a, b)
ShapeA[M,K] · B[N,K]ᵀ → C[M,N]
Work2 · M · N · K floating-point operations
RoutingInternal selection from shape, precision, and live device capabilities
Current sourcesdk/cpp/tutorials/core/tuCoreMatMulIntro.cpp
SDK filename targettuMatmul.cpp / tuMatmul.py

1. Record one operation

OA stores B as [N,K] and computes A @ Bᵀ. The public call records the semantic operation; kernel selection remains an internal lowering decision. C++ submits explicitly. Python synchronizes when the result crosses the host boundary.

1#include <oa/oa.h>
2
3OA_MAIN("TutorialCoreMatmul") {
4 auto a = oa::FnMatrix::full({2, 3}, 1.0F);
5 auto b = oa::FnMatrix::full({2, 3}, 2.0F);
6 auto c = oa::FnMatrix::matMulNt(a, b);
7
8 auto submitted = engine.submit();
9 if (not submitted.isOk()) return 1;
10 if (not engine.wait(submitted.getValue()).isOk()) return 1;
11
12 oa::Array<oa::F32, 4> values{};
13 if (not oa::FnMatrix::copyToHost(c, values.data(), sizeof(values)).isOk()) return 1;
14 return 0;
15}
16

2. Prove correctness first

The tutorial checks tiny and regular squares, tall and wide projections, irregular dimensions, single-row decode, MNIST-like layers, and fused Linear bias. GPU output is compared with a separate FP32 CPU implementation before any performance row is valid.

1double maxError = 0.0;
2double maxReference = 1e-6;
3for (oa::Usize index = 0; index < reference.size(); ++index) {
4 maxError = oa::max(maxError,
5 oa::abs(static_cast<double>(reference[index]) - gpu[index]));
6 maxReference = oa::max(maxReference,
7 oa::abs(static_cast<double>(reference[index])));
8}
9const double normalizedError = maxError / maxReference;
10

The accepted normalized error is 1e-4 for FP32 and 3e-2 only on devices where the BF16 cooperative-matrix route is actually selected and verified.

3. Measure the route you mean

The primary result is median synchronized wall time. The 95th percentile exposes jitter and throttling. A fastest-of-N sample may describe an optimistic peak, but it is not representative throughput.

SectionWhat the number includes
Public operationAllocation, recording, submission, GPU work, and synchronization
Pre-allocated outputThe same route with output storage reused
Replay / pipeline ×8An already captured graph submitted repeatedly before one wait
Batch ×4 / ×8Independent GEMMs recorded into one submission, reported as batch and per-op time

For a batch of B independent products, aggregate throughput isB · (2·M·N·K) / batch wall time. The previous public page multiplied byB twice; those inflated batch figures are intentionally gone.

1oa::Matrix output;
2auto captured = engine.capture([&]() {
3 output = oa::FnMatrix::matMulNt(a, b);
4});
5if (not captured.isOk()) return 1;
6auto plan = oa::move(captured).getValue();
7
8auto event = engine.submit(plan);
9if (not event.isOk()) return 1;
10if (not engine.wait(event.getValue()).isOk()) return 1;
11

4. Current checked baseline

These are Release results recorded on 11 July 2026 with an Intel Iris Xe Graphics Tiger Lake GT2 device, Vulkan 1.4, and the verified FP32 fallback. They are historical evidence for this exact method—not a current cross-device performance claim.

ShapeMNKp50 msp95 msGFLOP/s
square-5125125125120.7090.877378.8
square-10241024102410244.9015.967438.2
square-204820482048204841.61547.522412.8
tall-skinny409612810242.6964.354398.3
short-wide128409610242.6902.778399.1
gemv-decode1409640965.0515.8796.6

5. Read model shapes, not just squares

Large Alm projections approach the large-GEMM regime, while small tutorial-scale NLP projections remain dominated by dispatch and fixed costs. Single-token decode is GEMV-like and requires a dedicated route rather than another square-GEMM tile.

ShapeMNKp50 msp95 msGFLOP/s
nlp-qkv102432320.0760.25327.4
nlp-ffn-up102464320.0700.09259.7
nlp-ffn-down102432640.0780.08853.8
alm-qkv40963843842.9113.789414.9
alm-ffn-up4096153638412.31013.751392.5
alm-ffn-down4096384153613.34618.112362.0

6. Run and refresh the evidence

Build Release once, close competing GPU workloads, warm the process, then retain the complete recorded run with device, driver, precision, correctness, p50, and p95 output. When stronger hardware is available, replace this baseline from one controlled run rather than mixing new rows with the unavailable 5090 measurements.

cmake --build build/release --target TutorialCoreMatMulIntro -j
./bin/release/sdk/tutorials/core/tuCoreMatMulIntro
# Optional exhaustive CSV grid
OA_AUTOTUNE_BENCH=1 ./bin/release/sdk/tutorials/core/tuCoreMatMulIntro