6.5.7 · D3Advanced & Emerging Architectures

Worked examples — Google TPU architecture and systolic arrays

3,862 words18 min readBack to topic

This is the worked-examples companion to the parent TPU note. There we built the why and how of a dataflow systolic array. Here we drill every case class you can meet — tiny multiplies, degenerate shapes, batch limits, quantization edge cases, and an exam twist — each worked from the ground up.

Before we start, one promise from the contract: no symbol appears before it is defined. If the parent used it, we re-earn it here.


The scenario matrix

Every question this topic throws at you falls into one of these cells. The examples below are labelled with the cell they cover.

# Case class What makes it tricky Example
A Plain square multiply the "does the wiring really compute the dot product?" sanity check Ex 1
B Non-square / rectangular () the array isn't square-shaped for your data Ex 2
C Degenerate: array bigger than data ( matrix side) wasted cells, low utilization Ex 3
D Limiting: tiny batch vs huge batch pipeline fill dominates vs vanishes Ex 4
E Throughput / roofline (peak FLOP/s) scales as , not linearly Ex 5
F Memory-traffic saving (arithmetic intensity) reuse factor = Ex 6
G Zero / sign / zero-point in quantization 8-bit overflow, negatives, dequant offset Ex 7
H Real-world word problem translate a layer into cycles & time Ex 8
I Exam twist: matrix bigger than array (tiling) you must split the multiply Ex 9

Let's hit every cell.


Example 1 — Case A: plain multiply (does the wiring work?)

Forecast: guess before reading on — is it , or ?

The figure below shows the whole picture at once: read it before the steps.

Figure — Google TPU architecture and systolic arrays

How to read the figure (annotation walk-through):

  • The four lavender rounded boxes are the hardware cells. Each is labelled with the weight it stores: the box's vertical position is the row (top = , bottom = ), its horizontal position is the column (left = , right = ).
  • The two coral arrows on the left are the rows of marching rightward into the cells — an activation is read once and passed cell-to-cell (that is the one-read-many-uses trick).
  • The two mint arrows at the bottom are the partial sums dropping downward out of each column; the number that falls out of column is exactly . Look at those two mint labels — they are the answer you are about to derive.

Steps.

  1. Load the weights. Cell stores . So the left column of cells (column ) holds (top, row ) and (bottom, row ). The right column () holds and . (These are the four lavender boxes in the figure.) Why this step? The output needs sitting where the matching will pass. Column owns output column .

  2. Stream row of , namely , into the top (the top coral arrow) and it walks across. Column 1 accumulates: Why this step? Each cell does . The partial sum travels down (mint arrow), picking up one product per cell — that is the dot product .

  3. Same row into column : Why this step? The same activation row is reused by a second column of cells with no new memory read — this is the reuse that column-parallelism buys. It computes the second dot product .

  4. Stream row of , : Why this step? A new row of produces a new row of : the weights stay stationary in the cells while successive activation rows stream through, so one weight-load serves the entire batch.

Forecast check: the correct — the first option. If you guessed you paired a row of with a row (not column) of ; the dot product runs down 's column , so the second factor is with varying, not .

Verify: by hand, . The array wired the sum in space instead of a loop in time — same answer. ✓


Example 2 — Case B: rectangular multiply ( by )

Forecast: guess the shape of and the total MAC count first.

Steps.

  1. Shape rule. is , is . The shared dimension (the length we sum over) must match — it does. So is . Why this step? only makes sense if 's column count equals 's row count ().

  2. Count outputs. has entries. Why this step? Each entry needs its own accumulator cell in the array, so counting outputs tells you how many result cells (and columns of streamed activations) the computation occupies.

  3. Count MACs. Each entry is a sum of products, so total MACs . Why this step? Total work in a matmul is always MACs — this is the number that hits the array.

  4. Array depth needed. The partial-sum direction must be at least cells deep so a full dot product accumulates as it flows down. A array handles in one pass. Here , so most of the array is idle — foreshadowing Case C. Why this step? The contraction length sets how far a partial sum must travel to become complete; if exceeded the array depth you would have to tile (Example 9), so checking against decides whether one pass suffices.

Forecast check: is (not square — the output inherits from and from ), and the MAC count is . If you guessed a or shape, remember the contraction dimension disappears from the result; only and survive.

Verify: MACs; shape has entries; . Consistent. ✓


Example 3 — Case C: array bigger than the data (utilization)

Forecast: guess — is it near , near , or tiny?

Steps.

  1. Useful cells. A multiply keeps at most output cells busy (and depth ). Why this step? Only cells that own a real and see real operands compute anything.

  2. Total cells. . Why this step? The array is a fixed physical grid; its cell count is a constant of the hardware, so it is the denominator against which any problem's useful work is measured.

  3. Utilization: Why this step? This is the degenerate case — the array is a fixed-size grid; small problems drown in unused silicon.

Forecast check: the answer is tiny (), not near or . The intuition trap is thinking " uses a fair chunk"; in fact utilization is , which collapses fast when the data is much smaller than .

Verify: , i.e. . This is exactly why the parent note says TPUs are for big matrices — small ops are catastrophically wasteful. ✓


Example 4 — Case D: the two limits of batch size

Forecast: guess to the nearest percent, and what approaches for huge .

Steps.

  1. Where the formula comes from. Total cycles . Only of those cycles emit results; the fill is overhead. So . Why this step? The first result appears only after data has crawled diagonally across all rows and columns — that diagonal delay is clocks.

  2. Limit with : Why this step? One tiny op pays the full fill cost for a single result — worst case. (This is the "latency of one small matmul" trap from the parent.)

  3. Limit : Why this step? As the batch grows, the fixed 511-cycle fill becomes negligible — the array runs at full efficiency. TPUs are throughput machines.

  4. Solve , respecting that is a whole number. Setting the equality first: Why this step? counts activation rows, so it must be a non-negative integer — a fractional batch is meaningless. is increasing in , so we need the smallest integer with . The equality boundary lands exactly on the integer : check (exactly ), while . Had the boundary been fractional we would take the ceiling ; here it is already integral, so is the answer.

Forecast check: (dismally small — one op wastes the whole pipeline fill), and the huge-batch limit is exactly . If you guessed near , you underestimated how large the fixed -cycle fill is relative to a single streamed row.

Verify: ; ; ; and confirms is the smallest integer that qualifies. ✓


Example 5 — Case E: peak throughput scales as

Forecast: guess the TOPS number, then guess how much doubling multiplies it (×2 or ×4?).

Steps.

  1. Formula. cells, each 1 MAC/clock in steady state, 2 ops per MAC: Why this step? Every one of the cells fires each clock — parallelism is spatial, so throughput area .

  2. Plug in :

  3. Double the side, : Why this step? Doubling quadruples throughput — this is the "not clock×cores like a CPU" lesson.

Forecast check: the answer is TOPS, and doubling multiplies throughput by ×4, not ×2. If you guessed ×2 you were thinking linearly (like adding CPU cores); the array grows in area, so the factor is squared.

Verify: ops/s; ; ratio confirms the scaling. See Roofline model for how this peak becomes the flat "compute-bound" ceiling. ✓


Example 6 — Case F: memory-traffic saving (arithmetic intensity)

Forecast: guess the ratio — is it about ×16, ×256, or ×65536?

Steps.

  1. Naïve reads. Every MAC re-fetches its two operands. Number of MACs , each needing 2 reads: Why this step? This is the memory-wall worst case — compute is cheap, moving data is not.

  2. Systolic reads. Each of and ( entries each) is read once:

  3. Ratio: Why this step? The reuse factor equals the array side : each byte fetched feeds a whole row/column of cells.

  4. Arithmetic intensity (MACs per input read):

Forecast check: the ratio is ×256 (equals ), not ×16 or ×65536. If you guessed ×65536 you confused it with the cell count ; the reuse factor is the array side , because each input feeds one row/column of cells, not the whole grid.

Verify: ; ratio ; AI . ✓


Example 7 — Case G: zero, sign, zero-point, and overflow in 8-bit MACs

Forecast: before reading — guess the largest magnitude a single 8-bit×8-bit product can reach, and guess why we need an offset (zero-point) instead of just a scale.

Steps.

  1. (a) Zero weight. If then for any activation . The cell still passes along. Why this step? Zero is not special in hardware — it flows through like any number; this is why zeros don't automatically save time on a dense array.

  2. (b) Most-negative and most-positive product (sign corners). The extreme positive signed product is two negatives: and the most-negative single product is a negative times the largest positive: Both fit easily in 16 bits. Why this step? Covers the sign corner — two negatives give the largest positive, mixed signs give the most-negative, so we have shown both sign quadrants are handled without overflow at the multiplier.

  3. (c) No accumulator overflow. Summing worst-case products of magnitude : A signed 32-bit register holds up to . Since , no overflow. Why this step? Explains why the accumulator (32-bit) is deliberately wider than the operands (8-bit): summing many products grows the running total, so it needs headroom the 8-bit inputs don't.

  4. (d) Quantize then dequantize a concrete value. Take a ReLU activation range mapped onto unsigned codes . Then the scale is and the zero-point is (real maps to code ). Quantize : Dequantize back: The round-trip error is , about half a scale step — the price of 8 bits. Why this step? Shows the full quantize → compute → dequantize loop the TPU actually runs: integers flow through the MAC array (cheap, dense), and results are rescaled by afterward. The zero-point is what makes a lopsided (all-positive) range fit.

Forecast check: the largest-magnitude single product is (from ), reached by two negatives, not by two large positives (whose max is ). And the zero-point is needed because a scale alone can only stretch a range symmetric about ; the offset slides the range so an all-positive interval like uses all 256 codes.

Verify: ; ; ; ; and , error . See Quantization and 8-bit inference. ✓


Example 8 — Case H: real-world word problem (a dense layer's runtime)

Forecast: guess whether the fill cost ( cycles) noticeably changes the answer.

Steps.

  1. Cycles. . Why this step? Fill/drain 511 cycles once, then one result-row per clock for the rows.

  2. Efficiency check. — a fat batch amortizes the fill, exactly as Case D predicted.

  3. Time. One cycle . So Why this step? Converts abstract cycles into a wall-clock number — the unit (s) is what an engineer actually reports.

Forecast check: the fill barely matters here — cycles on top of is only a overhead, so . With a big batch the fixed fill is nearly invisible, which is exactly why TPUs batch aggressively.

Verify: cycles ; ; time s s. ✓


Example 9 — Case I: exam twist — matrix bigger than the array (tiling)

Forecast: guess the number of tiles first, and whether the total MAC count differs from an untiled .

Steps.

  1. Split each dimension. , so break , , into blocks. Because each of the three matrix dimensions () splits into chunks, becomes a grid of output tiles output tiles. Why this step? The array is a fixed window; you slide it over the big problem — this is tiling, the standard fix when the matrix exceeds the hardware.

  2. Passes per output tile. Each output block sums over the contraction tile index , and also splits into pieces. So each of the 4 output tiles needs accumulate-passes. Why this step? One pass loads a weight block and streams one -column slab; you need one pass per (output-tile, contraction-tile) pair, adding their partial sums.

  3. Total MACs (must equal the untiled count). Each pass drives a full multiply, i.e. MACs. Over all 8 passes: Compare with an untiled multiply, which does MACs — identical. Why this step? Tiling rearranges work in space/time but never changes the arithmetic total — that equality () is the correctness invariant you check in an exam.

Forecast check: there are 4 output tiles and 8 total passes, and the MAC count is identical to . If you guessed the total MACs would shrink, remember tiling only reorders work — the arithmetic is conserved.

Verify: output tiles ; contraction tiles ; passes ; . ✓


Recap

Recall Which batch size gives 90% efficiency on a 256×256 array?

rows. ::: ~4600 rows — TPUs need big batches.

Recall Why does doubling the array side quadruple throughput?

Throughput ; it scales with area because all cells fire every clock. ::: It's spatial (area) parallelism, not linear.

Recall Does tiling change the total MAC count?

No — . Tiling only reorders the same arithmetic. ::: Same total work, split into passes.

Recall In 8-bit quantization, what does the zero-point do?

It is the integer code that stands for real value , letting a lopsided (all-positive, post-ReLU) range fit the 8-bit codes without waste. ::: The offset that anchors real 0 in integer space.

Related depth: GPU architecture and SIMT execution · ASIC vs FPGA vs general-purpose processors · Roofline model.