A systolic array is a 2D grid of MAC units where operands are pumped between nearest neighbors, so each value fetched from memory is reused across an entire row or column of PEs.

Mechanism

An grid of processing elements, each holding a register and doing one MAC per cycle. Operands enter at the edges and shift one hop per cycle; no PE addresses memory directly.

  • Peak = FLOP/cycle. A 256×256 array at 1 GHz = 131 TFLOP/s from one block.
  • Memory ports required: per cycle, not . That vs gap is the entire architectural argument: bandwidth grows linearly while compute grows quadratically with array size.
  • Fill/drain latency cycles; a 256×256 array needs ~512 cycles of pipeline before the first result and after the last.

Dataflow variants (what stays put)

VariantStationary in PEStreams throughBest when
Weight-stationaryone weightactivations in, partial sums accumulate downweights reused across many activations (large batch/large )
Output-stationaryone accumulatorweights and activations both streamlong reduction dimension ; avoids partial-sum movement
Row-stationarya row of weights + reuse of activation rowssliding-window overlapsconvolutions, where the same activation feeds many outputs

Choosing between them is choosing which operand’s movement energy you eliminate. Off-chip DRAM access costs on the order of 100–1000x the energy of a MAC, and even a local register read is ~1–2x a MAC, so the dataflow choice is primarily an energy argument, then a bandwidth one.

Weight-stationary is the common choice for large matmul engines; output-stationary appears where is large and precision of accumulation matters.

The utilization cliff

Utilization for a GEMM mapped to an array is roughly

three multiplicative penalties:

  1. Quantization of M and N to the array dimensions. on a 128-row array uses 2 passes at 50%, a step function, not a gradient.
  2. Fill/drain amortization: small never amortizes the pipeline depth. on a 256×256 array is under 15% utilization before anything else.
  3. Batch-1 decode is the worst case. A GEMV has : one row of a 256-row array is active → 0.4% utilization. This is the single most important fact about systolic arrays for LLM inference, and it is the same phenomenon as low tensor-core efficiency at small M (Tensor Cores).

Compare to a GPU’s SIMT array, which degrades more gracefully because its granularity is a 32-lane warp, not a 128–256-wide array edge.

Mitigations

  • Smaller or partitionable arrays: many independent 32×32 or 64×64 tiles instead of one 256×256, trading peak density for shape flexibility.
  • Batching / continuous batching to raise above the array width.
  • Speculative decoding: turns into verification, directly buying utilization.
  • Fold other dimensions into M: multiple sequences, multiple heads, beam candidates.
  • Split-K to keep the array fed when is the only large dimension, at the cost of a reduction.
  • Padding is correct but wasteful: measure achieved MACs, not padded ones.

Tip

Always ask for utilization at your actual shapes, not peak TOPS. Array-width quantization plus fill/drain means a spec-sheet-fast part can deliver single-digit percent on decode-shaped GEMVs.

Dataflow Architectures · Tensor Cores · GEMM Tiling · Performance Modeling · Accelerator Compilers