advanced24 min studyLesson 472 in Engineering

Tiled matrix multiplication: assign outputs and count useful work

Derive matrix tiles, accumulation, edge masks, and conventional FLOP counts before comparing a kernel with a transfer-bound performance ceiling.

On this page

What you’ll understand

  • Map an output tile to the reduction dimension of a matrix product.
  • Count conventional multiply-add work and define the transferred-byte boundary.
  • Handle incomplete tiles without reading or writing outside valid tensors.

Matrix multiplication is a collection of dot products, but its performance depends on sharing their inputs and assigning their outputs well. A tile groups several outputs so nearby work can cooperate. Start with the mathematical product, valid index ranges, and an accumulation policy. Only then compare the implementation's work and data movement with the hardware resources it actually uses.

A tile owns an output region

For A with shape M by K and B with shape K by N, output C has shape M by N. One block or program instance can own a rectangular output tile and accumulate its partial sums across chunks of K. Assign each valid output exactly once unless the algorithm explicitly combines partial outputs through a sound protocol. A split-K implementation is a different reduction design, with extra combination work and numerical-order consequences. Do not add overlapping output stores merely to make the grid dimensions convenient.

When M, N, or K is not divisible by the chosen tile sizes, partial tiles require masks or a separately correct edge path. Invalid input lanes can contribute zeros to the dot product if that matches the operation, while invalid output coordinates must not be stored. The loaded layout still follows the tensor's actual strides. Bounds checks cannot be removed merely because a benchmark used square aligned dimensions. Also state whether the operation computes C=AB or a more general scaled combination involving previous C, because that changes both semantics and memory traffic.

Cij=∑k=0K−1AikBkj,Fconventional=2MNKC_{ij}=\sum_{k=0}^{K-1}A_{ik}B_{kj},\qquad F_{\rm conventional}=2MNK
The product uses valid indices 0≤i<M and 0≤j<N. F_conventional counts each multiply-add contribution as two floating-point operations, including the usual zero-initialized accumulation convention. It excludes scaling, bias, activation, and any previous-C read.↔ Scroll wide equations horizontally.

Define which bytes cross which boundary

An ideal single-read model for C=AB counts each input once and each output once. It is a lower traffic model at a named external boundary, not the total number of operand accesses made by all threads. Different tiles can reload the same input, caches can satisfy some loads, and shared-memory staging adds another transfer layer. Use an instruction count, a logical tensor-byte count, and measured device-memory bytes as separate metrics. A conventional FLOP numerator divided by inconsistent byte counters can produce a misleading intensity.

Arithmetic intensity is work divided by transferred bytes at the chosen boundary. A roofline model compares that intensity with a memory-bandwidth ceiling and a compute ceiling using matching units. It does not predict an exact kernel time: launch overhead, insufficient parallelism, instruction dependencies, conversion, and synchronization can reduce achieved throughput. A tiny matrix can be latency-bound even with favorable intensity. Compare against a tuned library under the same dtype, shape, layout, and precision contract before interpreting a custom tile as an improvement.

Qideal=sAMK+sBKN+sCMN,I=2MNKQidealQ_{\rm ideal}=s_A MK+s_B KN+s_C MN,\qquad I=\frac{2MNK}{Q_{\rm ideal}}
s_A, s_B, and s_C are bytes per stored element, giving Q_ideal in bytes and I in FLOP/byte. The model assumes C=AB, one external read of each input and one output write, no read of old C, and no extra traffic; actual boundary bytes must be measured separately.↔ Scroll wide equations horizontally.

Tile size is a resource tradeoff

Larger output tiles can reuse more input values, but they require more accumulators and can reduce the number of independent blocks available for a small problem. They also change register use, shared-memory needs, edge waste, and scheduling. The best shape need not be square, especially when M and N differ greatly. A blocked algorithm's mathematical correctness is independent of whether its tile saturates a particular GPU. Separate a valid tile design from a demonstrated performance result, and record the architecture and compiler when tuning.

The roofline lab has a fixed synthetic compute ceiling of 1,000 GFLOP/s. Reset, set I=8 FLOP/byte and B=80 GB/s, predict ceiling 640 GFLOP/s, then Play or Step. Separately Reset, set I=16 and B=80, predict the compute-capped ceiling 1,000, then Play or Step. These reachable settings do not change an actual matrix tile or GPU. They illustrate why improving reuse can help only until another ceiling dominates, while the displayed ridge intensity remains 12.5 FLOP/byte for the selected bandwidth.

Count a small dense product

Compute a 16-by-16 product with K=16 using four-byte inputs and output, C=AB, and the ideal one-read input/one-write output boundary.

  1. Conventional work is 2×16×16×16=8,192 FLOPs. This is a counting convention for multiply-add contributions, not an instruction count.

  2. Each matrix contains 256 elements. Reading A and B and writing C gives 3×256×4=3,072 ideal boundary bytes, with no previous-C read.

  3. The ideal intensity is 8,192/3,072=8/3≈2.667 FLOP/byte. A real tiled implementation can move more bytes and take additional overhead, so this is not a throughput measurement.

The result

The operation has 8,192 conventional FLOPs and 3,072 ideal boundary bytes under explicit assumptions. Tile tuning must preserve those semantics and validate actual traffic.

Explore the model

Interactive lab

Interactive lab · roofline

Reuse can move the bottleneck

Reset, set I=8 and B=80, predict 640 GFLOP/s, then Play or Step. Separately Reset, set I=16 and B=80, predict the fixed 1,000 GFLOP/s compute ceiling, then Play or Step. The synthetic roofline has no matrix tiles, launch overhead, or measured GPU counters.

Reuse can move the bottleneckSynthetic roofline ceiling min(1000, B·I), in GFLOP/s, with B in decimal GB/s and I in FLOP/byte. Playback traces the roof without changing the selected kernel. This upper-bound model is not a measured throughput prediction, hardware comparison or price model. Current readings: Attainable ceiling: 640 GFLOP/s; Ridge intensity: 12.5 FLOP/byte; Ceiling regime: Memory-bound ceiling. All parameters are adjustable with the labeled controls below.Hypothetical roofline upper bound: 640 GFLOP per second at operational intensity 8 FLOP per byte and bandwidth 80 gigabytes per second.0.1110641101001000Operational intensity · FLOP/byte · log scaleCeiling · GFLOP/s · log scalePeak: 1000
Attainable ceiling / kernelBandwidth roof / traceCompute ceiling
Attainable ceiling
640 GFLOP/s
Ridge intensity
12.5 FLOP/byte
Ceiling regime
Memory-bound ceiling
t = 0 s

Synthetic roofline ceiling min(1000, B·I), in GFLOP/s, with B in decimal GB/s and I in FLOP/byte. Playback traces the roof without changing the selected kernel. This upper-bound model is not a measured throughput prediction, hardware comparison or price model. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forArithmetic intensity belongs to a defined transfer boundary and only bounds attainable throughput.

Check your understanding

Why must a tiled kernel handle a reduction dimension not divisible by its tile width?

Try it yourself

For M=8,N=4,K=16 with two-byte A and B and four-byte C, find conventional FLOPs and ideal boundary bytes for C=AB.

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Use each tensor's own element width and omit old-C traffic only because the operation overwrites C.

Compare with the solution

Work is 2×8×4×16=1,024 FLOPs. Input A occupies 8×16×2=256 bytes, B occupies 16×4×2=128 bytes, and output C occupies 8×4×4=128 bytes. The ideal total is 512 bytes, giving intensity 2 FLOP/byte before any actual rereads, staging, or overhead.

Take it with you

  • Output ownership and edge handling are correctness requirements before tuning.
  • Conventional FLOPs, instructions, and transferred bytes are different counters.
  • Tile size trades input reuse against accumulators, occupancy, and problem parallelism.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.