How it works
The explanation, with examples you can run.
The ladder
Approximate figures for a current desktop or server core (they vary by chip and change slowly):
| Level | Typical size | Typical latency |
|---|---|---|
| Registers | a few hundred bytes | 0 to 1 cycle |
| L1 cache (per core) | 32 to 64 KB of data | about 1 ns (4 to 5 cycles) |
| L2 cache (per core) | 1 to 2 MB | about 3 to 5 ns |
| L3 cache (shared) | tens of MB | about 10 to 20 ns |
| Main memory | GBs | about 80 to 120 ns |
| NVMe SSD read | TBs | tens of microseconds |
| Round trip in one data centre | about 0.5 ms |
The jump from cache to RAM is the one that matters for ordinary code. Two more facts make it manageable. Memory moves in cache lines, usually 64 bytes: load one double and the next seven come with it. And hardware prefetchers watch the addresses you load; if they see a sequential or regular stride, they fetch lines before you ask.
Locality, measured
A 2048 × 2048 grid of doubles stored row after row in one array is 32 MB, larger than the caches. Summing it in two orders:
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
On a Mac laptop with Node 22, row order took 3 to 5 ms and column order 10 to 14 ms over several runs. The arithmetic is identical. Row order uses all 8 values of every cache line it loads and gives the prefetcher a simple pattern; column order uses one value per line and jumps 16 KB at a time. The gap depends on the machine (big caches and clever prefetchers narrow it), so run it yourself.
Why arrays beat linked lists
A linked list stores each node in its own allocation, wherever the allocator put it. Walking the list means loading a node to learn the address of the next one, so each step can be a full cache miss, and the prefetcher can't help because the next address isn't known until the current load completes. An array of the same values streams through memory at full speed. That is why dynamic arrays, hash tables with open addressing, and B-trees (wide nodes, few pointer hops) dominate real systems, as the data structures path shows.
The same ideas apply higher up the ladder: a database page cache, a CDN and an in-process memoisation map are all caches that pay off only when access has locality.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.