core21 min studyLesson 111 in Engineering

Cache lines and useful locality

Count the bytes a program consumes and distinguish reuse from merely adjacent addresses.

On this page

What you’ll understand

  • Separate a requested element from its transferred cache line.
  • Estimate useful-byte efficiency for explicit access patterns.
  • Distinguish temporal locality, spatial locality, and working-set size.

An eight-byte load does not necessarily move only eight bytes through every level of a processor's memory hierarchy. Caches organize storage and transfers into blocks called lines, creating an opportunity to reuse neighboring bytes. That opportunity becomes a benefit only when the program actually consumes those bytes or returns before eviction. Follow a concrete access trace rather than assuming that a small allocation or a contiguous container automatically produces effective locality.

The element and the transfer unit

Imagine a hypothetical cache with 64-byte lines and an aligned array of eight-byte values. Each complete line contains eight array elements. A sequential read can consume all eight after the first fill, provided the line stays available. A read that takes one element from each distinct line consumes only one eighth of each fetched line. The cache-line size here is a declared example parameter, not a universal property of every processor or every cache level.

Alignment changes which line contains an element. An access spanning a boundary can touch two lines even when its payload is small. Distinguish the processor instruction's access size from the bytes transferred at the particular hierarchy boundary under discussion. Compression, write policies, speculative accesses, and prefetching can change traffic further. State whether an estimate counts demand fills, all memory-controller traffic, or only useful application bytes; those quantities answer different questions.

ηuseful=DconsumedDtransferred,Nline=⌈o+DL⌉\eta_{\rm useful}=\frac{D_{\rm consumed}}{D_{\rm transferred}},\qquad N_{\rm line}=\left\lceil\frac{o+D}{L}\right\rceil
Useful-byte efficiency is dimensionless. For a contiguous D-byte nonempty range starting at offset o within an L-byte line, with 0≤o<L, the expression counts distinct intersected lines; all lengths are bytes. It does not count repeated fills.↔ Scroll wide equations horizontally.

Reuse has a distance

Temporal locality means returning to previously used data; spatial locality means using nearby data that arrives together. Neither is a yes-or-no property of an entire program. A matrix multiply may reuse a small tile intensely while streaming through another matrix. A pointer chase may revisit the same nodes only after touching a much larger intervening set. The order of access therefore matters as much as total allocation size when deciding whether useful data can remain close to execution.

Blocking reorganizes work so a manageable subset receives repeated use before the program moves on. Count inputs, outputs, and temporary storage together when choosing a block. Fitting their nominal bytes into a cache is necessary for some reuse plans but does not guarantee residency: set conflicts, competing threads, replacement, and shared cache capacity still matter. Verify the transformation preserves dependencies and numerical requirements before interpreting any speed improvement as a memory benefit.

Dtile=s (mn+nk+mk)D_{\rm tile}=s\,(mn+nk+mk)
A simple dense matrix-product tile retains m×n, n×k, and m×k elements of s bytes each. The expression is a payload estimate in bytes and excludes packing buffers, metadata, and additional concurrent tiles.↔ Scroll wide equations horizontally.

Measure a claim about locality

Compare two traversal orders with identical useful work and data, while recording dimensions, layout, initialization, and thread placement. Keep correctness checks outside the timed region when appropriate, but prevent the compiler from removing the computation. Test more than one working-set size: a change that helps a tiny hot loop can lose when extra packing or copying dominates. Elapsed time is the outcome; misses and transferred bytes provide supporting evidence about the mechanism.

After the spatial byte calculation, inspect temporal reuse in the cache lab. It starts empty, uses fully associative LRU replacement, and requests address identifiers (i×s) mod 16 for N=min(4096,16+floor(2t)) requests. Reset, set capacity C=4 and stride s=4, then predict the time-zero readout: the four-address period gives four misses and twelve hits among sixteen requests. Step adds one request, which hits. For a separate run, Reset and re-set C=4, s=3: predict a sixteen-address period and sixteen misses at time zero. The first working set fits; the second exceeds four entries and continues evicting entries before reuse. Control edits recompute the trace at retained elapsed time, so Reset separates comparisons. These are abstract address identifiers, not CPU cache lines or byte addresses. The toy has no set mapping, spatial line fills, prefetching, or measured processor timing.

Reading one value from each line

An aligned array contains 8,192 eight-byte values. Assume cold reads, 64-byte line fills, no prefetching, and no repeated eviction. Compare consuming every value with consuming only indices 0, 8, 16, and so on.

  1. The full payload is 8,192×8=65,536 bytes. Eight values fit in each declared 64-byte line, so the full traversal intersects 1,024 lines.

  2. The strided traversal consumes 1,024 values, or 8,192 useful bytes. It still touches those 1,024 distinct lines, requiring 65,536 bytes of demand fills under the stated model.

  3. Useful-byte efficiency is 65,536/65,536=1 for the full traversal and 8,192/65,536=0.125 for the strided one. These are traffic ratios, not predicted runtime ratios; instruction count and overlap differ.

The result

Spatial locality is useful only when the fetched neighbors contribute to the workload. The strided example uses 12.5% of its demand-fill bytes.

Explore the model

Interactive lab

Interactive lab · cache

Temporal reuse in a four-entry LRU cache

Reset, set C=4 and s=4, and predict four misses plus twelve hits at time zero; Step adds one hit. For a separate comparison, Reset and re-set C=4, s=3: predict sixteen misses at time zero. This empty-start fully associative LRU toy requests (i×s) mod 16; it does not model CPU cache-line geometry, set conflicts, prefetching, or benchmark performance.

Temporal reuse in a four-entry LRU cacheA fully associative LRU cache starts empty and requests address (i·s) mod 16. Hits move an address to the newest position; misses evict the oldest when full. Playback starts with 16 requests and adds two per second, capped at 4096 explicitly simulated requests. This toy stream is not a processor-cache benchmark. Current readings: Hits / requests: 0 / 16; Hit rate: 0%; Address-stream period: 16 addresses. All parameters are adjustable with the labeled controls below.Fully associative LRU cache after 16 deterministic requests: 0 hits and 16 misses.Most recent 16 requestsaddress = i × 3 mod 160M3M6M9M12M15M2M5M8M11M14M1M4M7M10M13MCurrent cache order · oldest → newest471013Latest address 13 · miss: inserted as newestToy address stream · period 16 · starts empty
Cached addressMiss / evictionMost recent request
Hits / requests
0 / 16
Hit rate
0%
Address-stream period
16 addresses
t = 0 s

A fully associative LRU cache starts empty and requests address (i·s) mod 16. Hits move an address to the newest position; misses evict the oldest when full. Playback starts with 16 requests and adds two per second, capped at 4096 explicitly simulated requests. This toy stream is not a processor-cache benchmark. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forA repeated address period that fits the toy capacity permits reuse; a larger period can evict entries before their next use.

Check your understanding

A traversal reads one eight-byte value from each cold 64-byte line. Which statement follows under a demand-fill-only model?

Try it yourself

A 96-byte contiguous range begins 48 bytes into a 64-byte line. Count intersected lines, then calculate useful-byte efficiency if each line is filled once and no other bytes are consumed.

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Locate the final byte's line, or use the starting offset plus the range length.

Compare with the solution

The range covers offsets 48 through 143 relative to the first line's start, touching three lines. Their fills transfer 192 bytes, so efficiency is 96/192=0.5. An equally sized aligned range touches only two lines. This calculation isolates alignment and demand fills; it is not a claim that the two implementations have a fixed two-to-three latency ratio.

Take it with you

  • Requested bytes and transferred bytes belong to different accounting layers.
  • Reuse depends on the access order and intervening working set.
  • Validate locality hypotheses with unchanged useful work and explicit traffic definitions.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.