6.5.8 · D2Advanced & Emerging Architectures

Visual walkthrough — Neural processing units (NPUs)

2,310 words11 min readBack to topic

Step 1 — Two lists of numbers, one answer

WHAT. Forget matrices. Start with the smallest possible neural-network idea: one output neuron. It has a list of inputs (call them ) and a list of weights (call them ). The neuron's job is to produce one number from these two lists.

WHY this operation. A neuron asks "how much does each input matter?" — it scales each input by its weight and adds up the votes. That "scale-and-add-up" is the single most repeated act in all of neural computing, so it is the thing we must make cheap.

PICTURE. Two rows of coloured boxes are lined up. Each purple input box pairs with the coral weight box directly below it. We multiply each pair, then drop every product into one bucket and sum.

Figure — Neural processing units (NPUs)

  • ::: the -th input number (purple). is just a counter, to .
  • ::: the weight paired with (coral) — its "importance dial."
  • ::: how many pairs there are (the length of both lists).
  • ::: the single output number, the sum of all the products.

Step 2 — The atomic move: multiply, then add to a running total

WHAT. Look at how you would actually compute that sum by hand. You keep a running total in your head. Start at . Multiply the first pair, add it in. Multiply the next pair, add it in. Repeat.

WHY isolate this. Because the hardware can only build one small thing well. If every dot product is nothing but the same tiny act repeated — "multiply two numbers, add the result to what I already have" — then we only need to build that one gadget, and build thousands of copies.

PICTURE. A single box labelled MAC (Multiply-ACcumulate). Two inputs enter ( and ), a running total arrives from before, and out comes . The old total is replaced by the new one.

Figure — Neural processing units (NPUs)


Step 3 — Many neurons at once = a matrix multiply

WHAT. One neuron gives one output. A layer has many neurons, and we usually feed many inputs at once (a batch). Stack the input rows into a grid , stack the weight columns into a grid . Each output is the dot product of row of with column of .

WHY. Real networks never do one dot product — they do a whole rectangle of them. Naming the rectangle a matrix multiply lets us count the total work in one clean formula (next step) instead of tracking each neuron.

PICTURE. A grid on the left, a grid on top, and the answer grid at the bottom-right. To fill cell of , slide across row of and down column of , MAC-ing pairs.

Figure — Neural processing units (NPUs)

  • ::: input in row , position (which pair we're on).
  • ::: weight in column , position .
  • ::: which output row () — one per batch item.
  • ::: which output column () — one per neuron.
  • The inner is exactly the dot product from Step 1.

This is the same the parent note gave — now every letter is earned.


Step 4 — Count the work, and discover the real enemy

WHAT. Each of the output cells costs MACs (one per pair). So the total is a simple product.

WHY. We need to know how bad this gets, and where the pain lives — in the arithmetic, or in fetching numbers from memory?

PICTURE. Two side-by-side bars. The tall coral bar is the energy to fetch a number from DRAM ( pJ). The tiny mint bar is the energy of the multiply itself ( pJ). The fetch dwarfs the math by ~.

Figure — Neural processing units (NPUs)

Step 5 — Reuse: each number should feed many multiplies

WHAT. Notice from Step 3: a single weight appears in every output row — it gets used times. A single input appears in every output column — used times. Each number is reusable many times.

WHY. If a number is fetched once but multiplied times, the crushing memory cost is divided by . That is how we defeat the coral bar of Step 4 — not by making it cheaper, but by paying it far less often.

PICTURE. One weight box sits still. An arrow shows the same weight being reused across a whole row of multiplies, while fresh inputs stream past it. "Load once, multiply many."

Figure — Neural processing units (NPUs)

Step 6 — Wire the MACs into a grid: the systolic array

WHAT. Take the MAC gadget from Step 2 and tile the plane with it: rows and columns of MACs, each wired only to its neighbours (right and down — no long wires, no global memory). Load each weight into its own cell and hold it there (weight-stationary). Now stream inputs in from the left, one column of data per clock tick.

WHY a grid, why neighbours only. A grid gives us multiplies happening at the same instant (parallelism, Step 4's cure). Neighbour-only wiring means data hops one short step per tick instead of travelling to far memory — cheap and scalable. This is the systolic array the parent note named.

PICTURE. A grid of cells. Weights (coral) sit inside each cell, fixed. Inputs (purple) march left→right. Partial sums (mint) fall top→bottom, each cell adding its own product to the falling total. Watch one input diagonally sweep the whole grid.

Figure — Neural processing units (NPUs)

Because an input, once injected, marches across an entire row of cells, that single fetch feeds a whole row of multiplies — reuse (Step 5) made physical. Related ideas: Tensor cores, GPUs and SIMT.


Step 7 — Fill, drain, and the steady-state win

WHAT. The grid isn't instant. Data enters staggered (a diagonal wavefront), so early cells work while later cells still wait empty — the pipeline fill. Once full, the array is in steady state: every clock it finishes a full column of results. At the end, the last data drains out through empty-ing cells.

WHY show this. Because the honest speed depends on the ratio of busy cycles to filling/draining cycles. For a big matrix the fill is negligible; for a tiny matrix the fill is most of the run and the array is mostly idle. This is the difference between marketing TOPS and real speed.

PICTURE. Three snapshots side by side: fill (a diagonal of live cells, corners idle), steady state (whole grid live), drain (live diagonal exiting, entry corner idle again). The busy fraction is the shaded area.

Figure — Neural processing units (NPUs)

  • ::: rows and columns of MAC cells.
  • ::: cycles per second (Hz).
  • the ::: each MAC = one multiply plus one add = two operations.
  • Utilisation ::: the fraction of cells actually doing useful work — never assume .

Step 8 — The degenerate cases you must not miss

WHAT & WHY. A formula is only trusted once its edges are checked. Walk every corner:

Case What happens Why
One pair per output; the accumulator adds a single product — no chaining. Dot product of length one is just a multiply.
Layer smaller than array () Only of columns used → utilisation . Idle columns still burn area but do no work.
Tiny , huge grid Fill/drain dominates; steady state barely reached. Not enough rows to amortise pipeline latency.
Memory-bound layer PEs starve waiting for data; effective TOPS peak. Bandwidth, not the grid, is the limit (Roofline model).
Zero inputs / zero weights MAC still runs (); result correct, energy wasted. Hardware doesn't skip zeros unless specially built.
int8 vs fp32 precision int8 multiplier ≈ smaller. Multiplier area (bit-width) → 16× more MACs per area.

PICTURE. A small array with only its left two columns lit (a narrow layer). Dark idle columns on the right show wasted silicon — utilisation .

Figure — Neural processing units (NPUs)

The one-picture summary

Figure — Neural processing units (NPUs)

The whole derivation on one canvas: two lists → a dot product → one MAC gadget → a matrix of dot products → the memory tax → reuse → a marching systolic grid → fill/steady/drain → peak & utilisation.

Recall Feynman retelling — say it back in plain words

A neuron just multiplies each input by a dial and adds up the votes — that's a dot product. The only trick your hardware ever needs is "multiply two numbers and add them to a running total": one little MAC cell. A whole layer is a rectangle of these dot products — a matrix multiply — and it's billions of MACs. But the multiplying is basically free; what kills you is fetching numbers from memory, which costs ~1000× more. So the winning idea is reuse: fetch each number once and shove it through many multiplies. To do that physically, we tile a plane with MAC cells wired only to their neighbours, freeze a weight inside each cell, and let inputs march left-to-right while partial sums fall top-to-bottom. Each input, once injected, sweeps a whole row — one fetch, many multiplies. After a short fill the grid finishes a full column of answers every clock tick. Its top speed is just (number of cells) × (clock) × 2 — but only if the cells stay busy, which small or memory-starved layers ruin. And because a small-number multiplier is far tinier, dropping to int8 lets ~16× more cells fit — more math, less energy, same silicon.