Running an AI model is mostly one small job done billions of times: multiply two numbers, then add the answer to a running total. The workload chapter showed why.
The math itself is cheap. The costly part is fetching the numbers from memory. One fetch can use hundreds of times more energy than the math done with it.3
A is built to fetch less. It is a grid of tiny calculators. A chip has a clock that ticks billions of times a second to keep every part in step. On each tick, every calculator does one multiply-and-add and passes its numbers to the next one. So a number fetched once gets used by every calculator it passes.2
The name comes from the heart. “Systole” is the squeeze that pumps your blood, and data pulses through the grid in the same steady beat.1 The idea is from 1978. It became famous again when Google built its AI chip, the TPU, around a giant one.7
Neural networks spend most of their time in : , where every output is a sum of products. Each product-and-add is one (MAC). Done naively, a MAC needs three values read from memory (a weight, an input and the running sum) and one written back. Reading from off-chip DRAM costs about 200 times the energy of the MAC itself, so memory traffic, not arithmetic, sets the cost.3
A is a grid of identical (PEs), each a MAC unit with a few registers, wired only to their nearest neighbors. Every clock cycle, each PE does one MAC and passes its values one step along. Data that enters at the edge is reused by every PE it passes, so the array does many operations per memory access.2 The term and the first designs come from H. T. Kung and Charles Leiserson at Carnegie-Mellon in 1978.1 The first TPU used one for exactly this reason: “reading a large SRAM uses much more power than arithmetic”.7
This chapter covers:
- How the bucket brigade computes a matrix product, and why the inputs enter in a staircase.
- The main design choice: which data stays put in each PE (weights, outputs, inputs or rows).
- How large matrices and convolutions are cut up and fed to a fixed-size array.
- Utilization: why arrays are often far less busy than their peak numbers suggest.
- Published designs as case studies: Google’s TPUs, Tesla’s self-driving chip, MIT’s Eyeriss and the open-source Gemmini.
For the other big answer to the same problem, many small cores running the same instructions, see SIMT and GPUs; for grids of whole cores rather than single MACs, see Dataflow and spatial meshes.
You know a neural network is mostly . A systolic array is the most direct hardware for it: an mesh of MAC with registered nearest-neighbor links, fed from on-chip buffers at two edges. Kung’s 1982 argument still frames the design: a GEMM is compute-bound ( operations on data), so a structure that uses each fetched value many times can raise throughput without raising memory bandwidth.2
The details that decide whether a given array is any good are:
- Dataflow: which operand is stationary in the PE (weights, outputs, inputs, or Eyeriss’s row-stationary mix), which sets what each level of memory is read for.4
- Mapping: how a GEMM’s , and , or a convolution lowered to a GEMM, are assigned to rows, columns and time, and how many folds that takes.6
- Utilization: fill and drain (about cycles per fold), fragmentation, weight-load stalls and memory-bound layers. TPU v1 averaged 23% of peak MACs doing useful work across six production workloads.7
This chapter puts numbers on each, using published TPU, Tesla FSD, Eyeriss and Gemmini data, and derives the runtime, utilization, traffic and energy models in “Under the hood”.
No reuse: every MAC reads two operands from memory, 32 reads per cycle for 16 MACs. Memory bandwidth, not the multipliers, sets the speed.
Start with one calculator and one memory. For every multiplication, the calculator has to fetch two numbers. Memory can only hand them over so fast, so the calculator spends most of its time waiting.
Now line up six calculators and pass each number down the line. Memory works just as hard, but six times as much math gets done. That is the whole trick.2
A real systolic array is a square grid. Each cell holds one weight, a number the model learned in training. Other numbers flow in from the left, one cell per tick. Each cell multiplies, adds the result to a running total coming from the cell above, and passes the total down. Finished answers drop out the bottom.
There is one catch. A total takes a tick to move down one row. So each row starts one tick later than the row above it, and the busy cells form a slanted wave across the grid.7
One PE, then many
Kung’s 1982 paper opens with a back-of-envelope case that still explains the design:
- Memory can move 10 million bytes per second to and from the processor.
- Each operation needs at least two bytes read or written.
- So one PE that fetches its operands from memory can do at most 5 million operations per second, however fast it is.
- Replace it with a chain of six PEs that pass each value along and use it at every stop: the same memory now supports 30 million operations per second.2
This only works when the job uses each value many times. Kung called those jobs compute-bound: matrix multiplication qualifies, because every entry of one matrix meets every entry in a row or column of the other. Adding two matrices doesn’t, since each value is used once.2 In today’s terms, matrix multiplication has high .
A 2 × 2 example
Take (two rows of ) and (the weights). In a 2 × 2 array, PE (row , column ) stores weight . Row of the array receives column of from the left. Each PE multiplies the arriving activation by its weight, adds the arriving from above, and passes the activation right and the new sum down.
| Cycle | What happens | Leaves the bottom |
|---|---|---|
| 0 | PE(0,0): , sum sent down | — |
| 1 | PE(1,0): . PE(0,1): . PE(0,0): | |
| 2 | PE(1,1): . PE(1,0): . PE(0,1): | , |
| 3 | PE(1,1): |
Two details make it work. First, the value 2 () enters row 1 at cycle 1, one cycle after the 1 entered row 0, so it reaches PE(1,0) at the same moment as the partial sum 5. That deliberate delay is the . Second, every value moves through a register (a ) between PEs, so no wire is longer than one PE and the clock can be fast. Apart from power, the clock is the only signal that reaches every PE.2
Each weight here was read once and used twice; each activation was read once and used twice. In a 256 × 256 array the same structure reuses each activation 256 times as it crosses a row. The first TPU’s matrix unit works this way: data flows in from the left, weights are preloaded from the top, and each multiply moves through the array “as a diagonal wavefront”. Software never sees the systolic timing; it just sees a unit that multiplies a block of inputs by a block of weights.7
The PE and its timing
In the weight-stationary form used by TPU matrix units, PE() holds and on every cycle computes
with registered to the right neighbor and to the one below. Activation enters row at cycle (the ) and reaches column at . The partial sum for output is at row at the same cycle , so the operands always coincide, and leaves the bottom of column at cycle . The outputs leave skewed and are de-skewed into .67
Properties that follow, and that Kung listed as the criteria for a good systolic design:
- Each input is used many times ( times per activation, times per weight).
- Extensive concurrency: all PEs can work at once in steady state.
- Only a few kinds of simple cells.
- Simple, regular data and control flow: every wire is nearest-neighbor, so wire length doesn’t grow with array size; the only global signal is the clock.2
Kung contrasted these “pure” systolic arrays with semi-systolic ones that broadcast an input to all cells or sum results through a fan-in adder tree. Broadcast and fan-in also achieve reuse, but their wires grow with the array and are hard to scale without slowing the clock.2 The original Kung–Leiserson matrix multiplier was a hexagonally connected array in which all three matrices moved; today’s accelerators use the rectangular, one-operand-stationary form.1
TPU v1 as the reference implementation
TPU v1’s matrix unit is 256 × 256 8-bit MACs. Products (16-bit) go to 4 MiB of 32-bit accumulators below the unit, organized as 4096 vectors of 256; the unit produces one 256-element partial sum per cycle. Activations come from a 24 MiB on-chip Unified Buffer; weights come from off-chip DRAM through a Weight FIFO four tiles deep. The unit holds one 64 KiB weight tile plus a second for double buffering, “to hide the 256 cycles it takes to shift a tile in”.7 Mixed 8/16-bit operands run at half speed and 16/16 at a quarter.7
One PE: 10 MB/s ÷ 2 bytes per operation caps it at 5 million operations per second, however fast it is.
Before cycle 0: weights are loaded; row 1’s activations are held back one cycle (the skew).
Every multiply-and-add uses three kinds of numbers. There are the weights, which never change. There are the inputs, which are new every time. And there are the running totals, which grow until each answer is done.
In a grid, one kind can sit still in each cell while the others flow past. In a grid, each cell keeps a weight. Google’s TPU works this way. In a grid, each cell keeps its own running total.3
Neither one always wins. It depends on the shape of the job. You can try both in the simulation below.
The pattern of which data stays and which moves is called the dataflow. MIT’s Eyeriss team classified published accelerators by it, and the names stuck.4
| Dataflow | Stays in each PE | Moves | Saves |
|---|---|---|---|
| (WS) | a weight | activations in, partial sums out | weight reads |
| (OS) | one output’s running sum | activations and weights in | partial-sum reads and writes |
| Input-stationary (IS) | an activation | weights in, partial sums out | activation reads |
| (RS) | a filter row, part of an input row and a partial sum | a mix of all three | all three together |
| No local reuse (NLR) | nothing | everything, via a large shared buffer | PE area, spent on buffer instead |
Why the choice matters: on-chip memory comes in levels, and smaller levels are cheaper to read. Eyeriss measured the energy of one access, relative to one MAC, in a 65 nm process:4
| Where the value comes from | Typical size | Energy per access (MAC = 1) |
|---|---|---|
| Off-chip DRAM | gigabytes | 200× |
| On-chip global buffer | more than 100 kB | 6× |
| A neighboring PE | — | 2× |
| The PE’s own small register file | 0.5 kB | 1× |
A dataflow decides which data gets read from which level, and how often. In a WS array each weight is read from the buffer once per tile and then used from the PE’s own register for every activation that passes. In an OS array the running sum lives in the PE, so no partial sum is ever written out and read back.
The idea predates neural networks. Kung’s 1982 paper laid out a whole family of systolic designs for one convolution problem: some where the weights stay, some where the results stay, with inputs and results moving in the same or opposite directions at the same or different speeds.2 Eyeriss itself introduced row-stationary, which tries to minimize the energy of all three data types at once, and reported it 1.4 to 2.5 times more energy-efficient than the others on the convolution layers of AlexNet.4
Definitions, precisely
- WS: each weight stays in a PE’s register file to maximize convolutional and filter reuse; the PE runs every MAC that uses that weight before it is replaced. Partial sums are reduced across PEs and, if not finished, spilled to the global buffer, so buffer capacity can limit how many filters are in flight.4
- OS: each output accumulates in one PE. Eyeriss distinguishes variants by which outputs are computed together: one output channel with many pixels (for convolution layers), many channels with many pixels, or many channels with one pixel (for fully connected layers).4
- IS: the mirror of WS for the activation operand. SCALE-Sim models OS, WS and IS on a systolic array and finds WS beats IS when a layer has more output pixels than weights, and the reverse otherwise, because loading the stationary matrix costs cycles with no compute; the less often it must be reloaded, the better.5
- NLR: no register file in the PE; all area goes to a larger global buffer. It has the lowest DRAM energy but pays buffer-level energy for nearly every access.3
- RS: each PE performs a 1-D convolution of a filter row with an input row, keeping the row of weights, a window of activations and one partial sum in its 0.5 kB register file; rows of PEs then sum their outputs vertically for the 2-D convolution.4
Under the same area and 256 PEs, Eyeriss’s analysis found WS lowest for weight energy and OS lowest for partial-sum energy, but RS lowest in total, because it optimizes all three data types. In fully connected layers there is little reuse and every dataflow spends a significant amount of its energy reading weights.3
Two observations from the original systolic work still guide the choice. When there are more weights than cells, a design where partial results stay generally needs less I/O than one where they move. And moving partial results needs wider paths than moving weights, because results carry more bits for numerical accuracy.2 In modern terms: WS ships 32-bit sums of 8-bit products between PEs; OS ships only the 8-bit operands.
SCALE-Sim’s authors conclude that while hyper-parameters and array size affect which dataflow is best, fixing one usually costs little, so the cost of supporting several should be weighed carefully.5 Gemmini, an open-source generator, makes it a configuration option: its PEs run either WS or OS.11 At the extreme, analog crossbars that store weights as conductances are “the ultimate form of a weight stationary dataflow”, covered in Sparsity, in-memory and analog compute.3
Weight-stationary: each weight is read once per tile and reused for every activation that passes. Partial sums move down to the accumulators.
Below is a small grid of 16 cells doing a multiplication job. Press Play, or Step to go one tick at a time. Watch the numbers march in from the left in a staircase and the answers drop out the bottom.
Then switch between “Weights stay” and “Sums stay”. What stays put in each cell, and when do the answers start to come out?
The array is 4 × 4 and computes with of size and of size , using small whole numbers so you can check the sums. It starts weight-stationary. Things to try:
- Step through the first cycles and find the diagonal wavefront. Why does row 3 start three cycles late?
- With 4 rows streamed, what is the peak number of busy PEs? Raise the rows streamed to 7 and look again.
- Raise the rows streamed from 4 to 16. How do the total cycles and the utilization change in each dataflow, and why does output-stationary not improve?
- Compare buffer reads with the “no reuse” count of two reads per MAC.
The Expert view adds the array size (2–8), the reduction depth (1–24) and up to 24 rows of ; is . The cycle count follows the SCALE-Sim model exactly: , with for WS (folds over ) and for OS (folds over ), and no overlap between folds.6 Try:
- , versus , in each dataflow. Which dimension does each one amortize fill and drain over?
- Read the bar chart: where are the load, fill and drain ramps, and how wide is the flat top?
- Compare “buffer reads” with “minimum reads”. When does each dataflow re-read an operand, and which one?
- At , how large must be before WS utilization passes 50%?
Buffer reads count every operand entering the array edge; results written count every vector leaving it, including WS partial sums sent to the accumulators between -folds. Memory never stalls in this model.
A real AI model multiplies tables of numbers with thousands of rows and columns. That is far bigger than any grid. So the job is cut into grid-sized pieces, like covering a big floor with square tiles. The pieces run one after another, and their results are added up at the end.7
The grid also needs a steady supply of numbers. So chips like this put a lot of fast memory right beside it, often more than the grid itself.7
Tiling
A matrix larger than the array is split into array-sized blocks, a step called (or folding). For a weight-stationary array:
- Load one array-sized tile of weights into the PEs.
- Stream every row of activations through it. The array produces partial sums.
- Add those partial sums into next to the array. TPU v1 has 4 MiB of 32-bit accumulators for this.7
- Load the next weight tile and repeat until every tile is done.
Loading a tile takes time: shifting a 256 × 256 tile into TPU v1’s matrix unit takes 256 cycles. To hide it, the unit holds a second tile and loads the next one while the current one computes (double buffering).7
Convolutions become matrix multiplications
A slides small filters over an image. It can be rewritten (“lowered”) as one matrix multiplication by building a matrix whose columns are the image patches each output needs, a step usually called . In cuDNN’s small example, 3 channels of a 3 × 3 image and two 2 × 2 filters become a 2 × 12 filter matrix times a 12 × 4 patch matrix. The cost: each input value is copied up to 4 times (filter height × width), which takes memory and extra traffic.12
Accelerators often build the patch matrix on the fly instead of storing it. Gemmini’s optional im2col unit does this beside the array; without it, the host CPU has to do the rearranging, and the choice of CPU then matters a lot to overall speed.11
Memory to feed it
The array only stays busy if operands arrive every cycle. TPU v1’s matrix unit reads and writes 256 values per cycle, fed from a 24 MiB Unified Buffer that takes almost a third of the die; the matrix unit is about a quarter, and control just 2%.7 In a Gemmini configuration with a 16 × 16 array, the array is 11.3% of the area and its 256 KB 52.9%.11 Getting data to these buffers from off-chip memory fast enough is the subject of The memory wall.
Mapping GEMM dimensions
SCALE-Sim describes any dataflow as a projection of the GEMM onto two spatial dimensions , and one temporal dimension . For OS, maps to , to and to : each PE accumulates over in time. WS and IS keep one operand in space and stream the other, so the dimension that maps to time is the one not held by the stationary operand.6 In this chapter’s sim, WS holds ( on rows, on columns) and streams the rows of ; OS holds and streams .
On an array the mapping needs folds.6 Which dimension is folded decides which data is re-read: a WS array folding over sends partial sums to the accumulators after every fold and reads the activations once per column tile; an OS array folding over re-reads all of for every row tile.
Convolution dimensions
For a layer with batch , input channels, filters of , and outputs, lowering gives a filter matrix and a data matrix , with each input duplicated up to times.12 In GEMM terms the reduction depth is , the output columns are the filters and the streamed dimension is . Three consequences:
- Early layers tend to have few channels but large filters; late layers have many channels but small filters. The product , the reduction depth, is usually fairly large either way, which is why lowering performs consistently.12
- Layers with fewer output channels than the array has columns leave columns idle. TPU v1 measured that on active cycles of one CNN only about half the 65,536 MACs held useful weights “because some layers in CNN1 have shallow feature depths”.7
- Depthwise convolution (one filter per channel) has a reduction of only , typically 9, so reuse is low; Gemmini reports MobileNetV2 maps poorly to spatial arrays for exactly this reason.11
Accumulator sizing and precision
TPU v1 sized its accumulators from the roofline: about 1350 operations per weight byte are needed to reach peak, rounded up to 2048 accumulator vectors, then doubled to 4096 so the compiler could double-buffer.7 TPU v2 and v3 use a 128 × 128 array with “streaming LHS and results” and a “stationary RHS”, bfloat16 multiplies and float32 accumulation.8 Tesla’s FSD accelerator accumulates 8-bit × 8-bit products into 30-bit accumulators.10 The rule behind all three: the accumulator must be wide enough for products without overflow or excessive rounding, and that width is what makes moving partial sums expensive. The number formats themselves are in Number formats.
The weight matrix is 2 × 2 tiles of the array’s size. Each tile is loaded, used, and replaced; partial sums meet in the accumulators.
Tap a pixel to highlight its copies in the patch matrix, or a column to highlight its window.
TPU v1: the 24 MiB Unified Buffer is almost a third of the die, the matrix unit about a quarter, control just 2%.
The first TPU’s grid had 65,536 calculators. That sounds unstoppable, but a calculator only helps when it is busy. Three things leave them sitting idle.
Starting and finishing. Numbers march in one cell per tick, so the far cells wait at the start. The last answers also take a while to march out. On a long job this barely matters. On a short job it wastes a lot.
Bad fit. If a job is smaller than the grid, part of the grid has nothing to do.
Waiting. If numbers don’t arrive from memory in time, the whole grid pauses.
These add up. Across six of Google’s real jobs, only about 23% of the calculators were doing useful work, on average. That is less than one in four.7
is the useful MACs done divided by the most the array could have done in the same time: number of PEs × cycles. It falls for four reasons.
Fill and drain
Because inputs are skewed, the far corner of the array starts work several cycles after the near corner, and the last outputs need several cycles to leave. These are the cycles. In the simulation’s default (4 × 4 weight-stationary, 4 rows streamed) the job is 64 MACs and takes 14 cycles, including 4 to load weights, so utilization is . Streaming 16 rows instead gives 256 MACs in 26 cycles, . The overhead is fixed per pass, so the longer the stream, the less it matters.6
Fit
A matrix that doesn’t divide evenly into the array leaves the edge tiles partly empty. TPU v1’s authors gave an example: a 600 × 600 weight matrix needs 9 tiles on a 256 × 256 unit (18 µs). A 512 × 512 unit would need only 4 tiles, but each takes four times as long, for 32 µs: the bigger array is slower.7
Waiting for weights
In TPU v1, weights came from off-chip DRAM. For the four workloads limited by memory bandwidth (two MLPs and two LSTMs), the matrix unit was active only 8–13% of cycles, and 44–62% of cycles were stalls waiting for weights.7
Small batches
A weight-stationary array reuses each weight once per row streamed, and the rows come from the : the number of requests processed together. Bigger batches mean more reuse but longer waits. One TPU v1 workload had to answer within 7 ms at the 99th percentile, which caps the batch size every chip can use.7
Array size trades these effects against reuse. For TPU v2 Google compared, at the same total FLOPS, one 256 × 256 array, four 128 × 128 arrays and sixteen 64 × 64 arrays. Four 128 × 128 arrays gave 1.6 times the utilization of one 256 × 256 array, with half the operations per operand; sixteen 64 × 64 arrays gained little more utilization (1.7×) for another halving of reuse. They chose 128 × 128.8
The fixed cost per fold
For one fold on an array SCALE-Sim gives for OS, WS and IS alike. In OS, the far PE receives its first operands at , accumulates for cycles and the results drain in . In WS, loading the weights takes cycles (no skew needed, since nothing computes during the load), the first input reaches the last column after , streaming takes and the reduction down the column takes .6 The overhead is independent of , so for a full fold
| Array | |||
|---|---|---|---|
| 128 × 128 | 25% | 73% | 91% |
| 256 × 256 | 14% | 57% | 84% |
(Computed from the formula; it assumes no overlap between folds, which double-buffered designs such as TPU v1 partly achieve.)
Fragmentation
With folds, the useful share of PE slots is . For TPU v1’s 600 × 600 example: on 256 × 256 versus on 512 × 512, consistent with the reported 18 µs versus 32 µs.7 Fragmentation is worse in two dimensions than one, which the authors compared to internal fragmentation of large memory pages.7
Measured: TPU v1 performance counters
Table 3 of the TPU paper splits the matrix unit’s time across six production workloads (MLP0 and 1, LSTM0 and 1, CNN0 and 1). Mean values: array active 28% of cycles, useful MACs 23% of peak, unused MACs on active cycles 5%, weight stalls 43% and weight shifting 12%. CNN0 reaches 78.2% useful MACs and 86 of 92 peak TOPS; CNN1 reaches only 22.5%, half its active MACs idle from shallow layers and 28.1% of cycles stalled on weights for its fully connected layers.7 Four of the six workloads sit on the bandwidth-limited slope of the TPU’s , whose ridge is at about 1350 operations per weight byte.7
Mitigations, and what they cost
- Overlap folds. Double-buffer weights so the next tile shifts in during compute (TPU v1). Costs a second weight register per PE.7
- Smaller, more numerous arrays. 128 × 128 instead of 256 × 256 (TPU v2), and more of them per core (two in TPU v3, four in TPU v4). Costs reuse per operand and so buffer bandwidth.89
- Bigger batches raise for WS, limited by latency targets and memory capacity.7
- Pick the dataflow by shape: WS amortizes overhead over , OS over . The sim makes the mirror symmetry visible.5
- Scale out. SCALE-Sim’s follow-up asks whether one big array or several partitions is faster for a given layer, and builds the runtime model above to search that space.6
T = 128 rows streamed on a 128 × 128 array: 128 useful cycles out of 510, so utilization is 25%. Load, fill and drain cost 382 cycles every pass.
600 × 600 weights: 9 tiles on 256 × 256 (61% of slots useful), 4 on 512 × 512 (34%). The TPU v1 paper: 18 µs vs 32 µs, so the bigger array is slower.
- TPU v1 matrix unit
- 256 × 256 MACs
- TPU v1 useful MACs, mean of 6 workloads
- 23%
- DRAM access energy vs. one MAC (65 nm)
- ≈ 200×
- Utilization gain, 4×(128²) vs 1×(256²)
- 1.6×
Sources: the TPU v1 paper, Eyeriss and the TPU v2/v3 Hot Chips talk.748
What these numbers mean:
- 256 × 256 is 65,536 tiny calculators in one grid. On the first TPU, each one did a multiply-and-add 700 million times a second.7
- 23% is how much of that grid did useful work, on average, in real use. Much of the rest of the time it was waiting for numbers.
- 200×: fetching a number from the main memory chips takes about 200 times the energy of one multiply-and-add. That is the cost the whole design tries to avoid.
- 1.6×: Google later swapped one giant grid for four smaller ones. Together, the small grids stayed busy about 1.6 times as much.8
TPU v1 matrix-unit counters
| Workload | MLP0 | MLP1 | LSTM0 | LSTM1 | CNN0 | CNN1 |
|---|---|---|---|---|---|---|
| Array active cycles | 12.7% | 10.6% | 8.2% | 10.5% | 78.2% | 46.2% |
| Useful MACs (% of peak) | 12.5% | 9.4% | 8.2% | 6.3% | 78.2% | 22.5% |
| Weight stall cycles | 53.9% | 44.2% | 58.1% | 62.1% | 0.0% | 28.1% |
| Weight shift cycles | 15.9% | 13.4% | 15.8% | 17.1% | 0.0% | 7.0% |
| Delivered TOPS (92 peak) | 12.3 | 9.7 | 3.7 | 2.8 | 86.0 | 14.1 |
Reading the table:
- The convolutional network CNN0 reuses each weight across many image positions, so weights never stall and the array runs at 78% of peak.
- The MLPs and LSTMs use each weight only once per request in the batch, so the array spends most of its time waiting for weights from memory and delivers 3–13% of peak.
- CNN1 shows the other losses: half the MACs idle on active cycles because some layers are too shallow for 256 columns, plus weight stalls in its fully connected layers.
Even so, the paper reports the TPU 15 to 30 times faster on these workloads than the CPU and GPU of its day.7
Points worth noting:
- Useful MACs track active cycles closely for MLP0, MLP1, LSTM0 and CNN0, so there the loss is time (stalls), not space (fragmentation). CNN1 loses about half its active MACs to unused slots, and LSTM1 about 40%.
- Weight shifting is 13–17% of cycles in the MLPs and LSTMs even with double buffering: when each weight tile is used for few rows, its 256-cycle shift can’t be hidden behind compute.
- Eyeriss reports its row-stationary dataflow 1.4–2.5× more energy-efficient than WS, OS and NLR in AlexNet’s convolution layers and at least 1.3× in fully connected layers at batch sizes of 16 or more.4
- The TPU v4 authors attribute part of their energy advantage to reuse: a 128 × 128 MXU reuses each 128-entry input 128 times, so the on-chip SRAM is read less per operation.9
Building a chip around a grid like this means making some bets:
- One big grid or several small ones. A big grid reuses each number more, but it is harder to keep busy. Google moved from one big grid to several smaller ones.8
- Which numbers stay put. Keeping weights still suits some jobs. Keeping totals still suits others.
- Special or flexible. The grid is superb at multiplying big tables and poor at almost everything else. The chip needs other parts for the rest.
- Memory takes the space. The grid itself is small. Much of the chip goes to the memory that feeds it.11
Array size: reuse against utilization
A bigger array reuses each operand more (each activation crosses more columns) but loses more to fill, drain and fit. At the same peak FLOPS, Google’s analysis showed four 128 × 128 arrays got 1.6× the utilization of one 256 × 256, at half the operations per operand read.8
Dataflow: what you save and what you pay
- Weight-stationary saves weight reads but ships partial sums between PEs and to accumulators.
- Output-stationary keeps sums local but must stream both operands and re-read one of them for every tile of outputs.
- Row-stationary balances all three at the cost of more complex PEs and mapping.
Speed against area and power
Registers between every pair of PEs keep wires short and the clock fast, but registers cost area and power. Gemmini compared a fully pipelined “TPU-like” array with one where PEs within a tile are chained without registers (“NVDLA-like”). With 256 PEs, the pipelined design ran at 2.7 times the frequency but used 1.8 times the area and 3.0 times the power.11
Specialization
TPU v1’s array was designed for dense matrices; support for sparsity was left out to ship sooner.7 Layers with little reuse, such as depthwise convolutions, map poorly.11 Activation functions, normalization and data rearrangement need other units: TPU v2 replaced v1’s fixed activation pipeline with a general vector unit and added a transpose and permute unit beside the matrix unit.8
Ways designs go wrong
- Starved arrays. Weights or activations can’t arrive fast enough, as in TPU v1’s MLPs and LSTMs.7
- Shape mismatch. Layers narrower or shallower than the array leave PEs idle.7
- Latency limits. Response-time targets cap the batch size, so reuse stays low.7
- Host overhead. Without on-chip im2col, Gemmini’s convolution speed depends heavily on the host CPU.11
Reuse arithmetic
In an WS array, each activation read is used times, each weight read times per load, and each partial sum leaving the array represents MACs. Operations per operand read therefore scale with the array edge, which is why quartering a 256 × 256 array into four 128 × 128 arrays halves operations per operand (4× to 2× on Google’s chart, normalized to 64 × 64) while improving utilization 1.6×.8 The buffer must supply the difference: four arrays of edge 128 need twice the operand bandwidth of one array of edge 256 at the same FLOPS.
Wide sums on the wires
Kung noted in 1982 that paths carrying partial results must be wider than paths carrying weights, because results carry more bits for accuracy.2 With int8 operands and 32-bit accumulation, a WS column carries 32-bit sums PE to PE, four times the width of the activations crossing the row. Tesla’s FSD accelerator takes the other side: it computes 96 output pixels by 96 output channels at a time with the input-channel and kernel loops innermost, accumulating 8-bit × 8-bit products into 30-bit sums held in the MAC array, and lists “in place data reuse vs result movement” among its power choices.10
Clocking a big array
Nearest-neighbor wiring removes long data wires but not the clock. Kung warned that large 2-D systolic arrays may need a slower global clock to absorb , while one-dimensional arrays tolerate large skew between their ends.2 Today that is the job of clock tree synthesis, but the clock remains the one signal that spans the whole array.
Flexibility tax
Supporting several dataflows needs muxes and extra registers in every PE, and SCALE-Sim found fixing one dataflow rarely costs much; Gemmini makes it a build-time choice so designers can measure.511 For layers whose matrices are small, the shape mismatch matters more than the dataflow: a tiny on a WS array is mostly fill and drain. More on batch and decode in The memory wall.
One 256 × 256 array: the most reuse per operand, but the most lost to fill, drain and fit.
This part goes deeper, into the math, models and algorithms behind the chapter. It’s written for the Expert level.
The models behind the simulation and the numbers above, step by step, with their assumptions.
1. Dataflow as a loop order
A GEMM is three nested loops. A dataflow chooses which index is spatial (unrolled across PEs) and which is temporal, and the stationary operand is the one whose indices don’t change in the innermost temporal loop. Illustrative pseudo-code for one tile:
# C[M][N] += A[M][K] * B[K][N] on an R x C array
# Weight-stationary: B[k][n] pinned in PE(k, n)
for k0 in 0..K step R: # fold: load B[k0:k0+R][0:C]
for m in 0..M: # time: stream rows of A
parallel for k, n in R x C: # space
C[m][n] += A[m][k0+k] * B[k0+k][n]
# Output-stationary: C[m][n] pinned in PE(m, n)
for m0 in 0..M step R: # fold: new tile of outputs
for k in 0..K: # time: stream A columns and B rows
parallel for m, n in R x C: # space
C[m0+m][n] += A[m0+m][k] * B[k][n]
drain C[m0:m0+R][0:C]- 1L3Each fold loads a new weight tile; partial sums for different k0 add in the accumulators.
- 2L4M is the temporal dimension: the longer it is, the more the fold overhead is amortized.
- 3L9Each fold re-reads all of B for a new tile of outputs.
- 4L10K is temporal: the accumulation happens in place, so partial sums never leave the PE.
Tesla’s slides show the same idea for convolution: the seven-deep loop nest is reordered so output pixels and output channels step by 96 in the outer loops and input channels and kernel positions run innermost.10
2. Cycle-exact timing
Weight-stationary, , one fold, cycles counted from the start of the fold:
- Load: weight rows shift in from the top, bottom row first, one row per cycle: cycles. Nothing computes, so no skew is needed.6
- Stream: enters row at stream cycle and meets PE() at .
- Exit: leaves the bottom at . The last one, , , leaves at .
- Total: , which is SCALE-Sim’s with .6
Output-stationary: enters row at cycle , enters column at , so PE() does its -th MAC at cycle . The last MAC is at ; then cycles shift the results out the bottom: with . The busy count at stream cycle is the number of with , a diagonal band; it reaches only if . That is why the sim’s 4 × 4 default () peaks at 12 busy PEs.
3. Runtime and utilization with folds
Assumptions: operands always available (no memory stalls), no overlap between folds, outputs leave without stalling, and every fold pays the full overhead even when partly empty. Real designs overlap weight loading with compute (TPU v1 double-buffers) and may pipeline consecutive folds, so is a pessimistic bound on overhead and an optimistic one on memory.57 SCALE-Sim’s authors checked its cycle counts against an in-house RTL model for one case: output-stationary multiplication of matrices the size of the array.5
4. Buffer traffic
Counting each operand that enters the array edge as one buffer read (the sim’s model, ):
- WS: reads = (each weight once) + (each activation once per column tile); accumulator writes = , of which all but the last fold also need a read-modify-write.
- OS: reads = (each activation once) + ( re-read for every row tile of outputs); result writes = .
- Lower bound for either: , every operand read once. Without reuse a MAC needs two operand reads, so .3
Example from the sim: , , . WS reads for 256 MACs (3.2 MACs per read, the minimum possible); OS needs 4 row tiles and reads (2.0 MACs per read). Swap to , and the roles reverse in cycles, though here both read 128 because and are both 64 values.
5. Energy from reuse
Eyeriss models a value’s data-movement energy from how many times it is reused at each level. If a value is read from DRAM times, each of those copies from the global buffer times, passed across the array times and read from the register file times, then4
with , 6, 2 and 1 MAC-equivalents. Partial sums are counted the same way for their reads and writes.4 A worked example with those costs (illustrative): an activation that must meet 128 weights. Read from the buffer for every MAC it costs . Read once from the buffer and passed PE to PE across a 128-wide WS row it costs , about 3× less. Read from DRAM every time it would cost . Sze et al. estimate AlexNet’s 724M MACs would need nearly 3000M DRAM accesses with no reuse and 61M with perfect on-chip reuse.3
n = 128: 25,600 / 768 / 262 MAC-equivalents. Passing PE to PE is 2.9× cheaper than re-reading the buffer (→ 3× cheaper as n grows), 98× cheaper than DRAM.
6. Lowering convolution
Filter tensor ( filters × channels × ) reshapes to of ; the data matrix is , built by copying each input patch into a column; is .12 Each input value appears up to times in , so materializing it multiplies activation memory and traffic by up to ; cuDNN instead forms tiles of lazily in on-chip memory, and Gemmini does the equivalent in hardware at the array edge.1211 Sze et al. note that the same redundancy, or a complex access pattern, is the price of treating convolution as plain matrix multiplication, and that its row-stationary dataflow exploits the convolutional reuse directly instead.3
7. What the simulation leaves out
- Memory: buffers deliver every operand on time; there are no DRAM, bandwidth or bank-conflict stalls.
- Overlap: folds run back to back with the full load, fill and drain each time (no double buffering).
- Shape: always has as many columns as the array (), so column fragmentation isn’t modeled.
- Numerics: small positive integers with unlimited precision; no 8-bit saturation or accumulator width.
- Energy and area: reads are counted, not weighted by the cost of each memory level.
Q1Kung’s example: memory delivers 10 million bytes per second and each operation needs 2 bytes moved. A chain of six processing elements reuses each fetched value. What rate becomes possible?
Q2A 4 × 4 array (16 PEs) does a 64-MAC job in 14 clock cycles. What is its utilization?
Q3What stays in place in an output-stationary array?
Q4Why does data enter row of the array cycles later than row 0?
Sources
Show Hide 12 sources
- Systolic Arrays (for VLSI)The name from the heart’s systole; processors that rhythmically compute and pass data; a hexagonally connected array for matrix multiplication; simple, regular communication suited to cheap VLSI.
- Why Systolic Architectures?Multiple computations per memory access; 10 MB/s and 2 bytes per operation cap one PE at 5 MOPS while a six-PE array reaches 30 MOPS; the family of convolution designs where weights, results or inputs stay; idle half-arrays; wide partial sums; the clock as the only global signal; clock skew in large 2-D arrays.
- Efficient Processing of Deep Neural Networks: A Tutorial and SurveyThree reads and one write per MAC; AlexNet’s 724M MACs need nearly 3000M DRAM accesses without reuse, 61M at best; relative access energy DRAM 200×, buffer 6×, inter-PE 2×, register file 1×; WS, OS, NLR and RS dataflows; Toeplitz lowering of convolution; resistive crossbars as the ultimate weight-stationary design.
- Eyeriss: A Spatial Architecture for Energy-Efficient Dataflow for Convolutional Neural NetworksDataflow taxonomy (WS, OS and its variants, NLR) and row stationary; energy per access normalized to a MAC in 65 nm; the reuse-based energy equation; RS 1.4–2.5× more efficient in conv layers; chip spec: 168 PEs, 0.5 kB per PE, 108 kB buffer, 200 MHz, 16-bit fixed point.
- SCALE-Sim: Systolic CNN Accelerator SimulatorOutput-, weight- and input-stationary mappings on a systolic array; loading the stationary matrix costs cycles with no compute; WS beats IS when output pixels outnumber weights; no single dataflow wins everywhere.
- A Systematic Methodology for Characterizing Scalability of DNN Accelerators using SCALE-SimMapping GEMM dimensions to the array’s rows (S_R), columns (S_C) and time (T); skewed inputs; runtime 2S_R + S_C + T − 2 for OS, WS and IS; folds ⌈S_R/R⌉·⌈S_C/C⌉, each 2R + C + T − 2 cycles.
- In-Datacenter Performance Analysis of a Tensor Processing Unit256×256 8-bit MACs, 92 TOPS peak, 700 MHz, 28 nm, 40 W; 24 MiB Unified Buffer and 4 MiB of 32-bit accumulators; systolic execution to cut buffer reads; diagonal wavefront; double-buffered 64 KiB weight tiles that take 256 cycles to shift in; Table 3 utilization counters; the 600×600 tiling example.
- Google’s Training Chips Revealed: TPUv2 and TPUv3 (Hot Chips 32 slides)128×128 systolic matrix unit with streaming LHS and results and a stationary RHS; bfloat16 multiply, float32 accumulate; ‘Why 128x128?’: at constant FLOPS, 4×128×128 gives 1.6× the utilization of one 256×256 at half the operations per operand; TPUv3 doubles the matrix units per core.
- TPU v4: An Optically Reconfigurable Supercomputer for Machine Learning with Hardware Support for EmbeddingsTwo TensorCores per chip, each with four 128×128 MXUs; 275 TFLOPS (bf16 or int8) at 1050 MHz in 7 nm; 128 MiB CMEM; TPU v3 at 123 TFLOPS and 940 MHz; twice TPU v3’s matrix multipliers; a 128×128 MXU reuses each 128-entry input 128 times.
- Compute and Redundancy Solution for the Full Self-Driving Computer (Hot Chips 31 slides)Two neural-network accelerators per chip, each a 96×96 MAC array at 2 GHz+ (36.8 TOPS) with 32 MB of SRAM; 8b × 8b products into 30-bit accumulators held in place; loop order with output pixels and output channels outermost; goal of about 80% utilization at batch size one.
- Gemmini: Enabling Systematic Deep-Learning Architecture Evaluation via Full-Stack IntegrationOpen-source generator; PEs use weight- or output-stationary dataflow; fully pipelined TPU-like vs combinational NVDLA-like arrays (2.7× frequency for 1.8× area and 3.0× power); a 16×16 array is 11.3% of system area beside a 256 KB scratchpad at 52.9%; on-the-fly im2col; low reuse in depthwise convolution.
- cuDNN: Efficient Primitives for Deep LearningLowering convolution to matrix multiplication: a K × CRS filter matrix times a CRS × NPQ data matrix; each input value duplicated up to R·S times; matrix multiplication is efficient when the matrices are large.