Technopedia

The technologist’s encyclopedia

Memory Hierarchy

The memory hierarchy organizes storage from fast, small, expensive (registers, caches) to slow, large, cheap (DRAM, disk). It is relevant whenever a program’s performance is limited by memory access rather than computation. The fundamental motivation is the memory wall: CPU performance grew at roughly 60% per year through the 1990s and early 2000s, while DRAM latency has remained roughly constant at 60–100 ns. The hierarchy exists to hide this growing gap.

Registers 1 cyc · KB L1 Cache 4 cyc · 32–64 KB L2 Cache 12 cyc · 256 KB–1 MB L3 Cache 40 cyc · 8–64 MB DRAM 100 cyc · 8–128 GB SSD 50–100 µs · 256 GB–8 TB HDD 10 ms · 1–20 TB miss → miss cascade
LevelLatency (cycles @ 1 GHz)Latency (time)BandwidthCapacity
Registers11 ns~100 GB/s (per core)KB
L1 cache44 ns~100 GB/s32–64 KB
L2 cache1212 ns~50 GB/s256 KB–1 MB
L3 cache4040 ns~50 GB/s8–64 MB
DRAM100100 ns~50 GB/s (dual-channel DDR4)8–128 GB
SSD (SATA)100,000100 µs~500 MB/s256 GB–8 TB
SSD (NVMe)50,00050 µs3–7 GB/s256 GB–8 TB
HDD10,000,00010 ms~200 MB/s1–20 TB

Authoritative sources

Latency vs. Bandwidth

Two distinct quantities govern memory performance, and confusing them leads to wrong design decisions:

A single random 64-byte read from DRAM takes 100 cycles (100 ns) but transfers only 64 bytes, yielding an effective rate of 0.64 GB/s. A sequential stream of 64-byte lines, however, keeps the DRAM pipeline full and achieves the full 50 GB/s. The distinction is critical: sequential access is bandwidth-bound; random access is latency-bound.

DRAM achieves peak bandwidth through bank interleaving: a DRAM chip is divided into 8–32 banks, each with its own row buffer. While one bank is in row-activation (tRCD), another can be in column-read (tCL), and a third can be precharging (tRAS). The memory controller issues commands to different banks in parallel, so the aggregate throughput approaches the bus limit even though each individual access takes the full tRCD + tCL sequence.

Details

Why sequential access saturates bandwidth: A DRAM column read returns 64 bytes per burst. If the next request is to the adjacent column in the same row, the row is already open and the controller skips tRCD, issuing only tCL. For a fully sequential stream, every access after the first is a “row hit” and the effective latency drops to tCL (~13 cycles). The bus is never idle, so bandwidth equals the bus rate.

Why random access is latency-bound: Each random access likely hits a different row, forcing a full tRCD + tCL sequence (~30 cycles) per access. With only one outstanding request, the effective bandwidth is 64 B / 30 cycles ≈ 2 GB/s per channel—far below the 25.6 GB/s bus rate. Modern CPUs hide this with out-of-order execution and multiple outstanding loads, but the fundamental limit remains.

Worked example: A program reads 1 GB sequentially from a dual-channel DDR4-3200 system (51.2 GB/s peak). Time = 1 GB / 51.2 GB/s ≈ 19.5 ms. The same 1 GB read with random 64-byte accesses at 100 ns each: 16,384,000 accesses × 100 ns = 1.64 s—84× slower.

Caches

A cache is a small, fast memory that stores copies of frequently accessed data. The L1 cache (32–64 KB, 4 cycles) is per-core; the L2 cache (256 KB–1 MB, 12 cycles) is per-core or per-pair; the L3 cache (8–64 MB, 40 cycles) is shared. A cache miss stalls the CPU pipeline until the data arrives.

Details

Cache line and mapping

The cache line (typically 64 bytes) is the unit of transfer between levels. A miss loads the entire line, not a single byte. This exploits spatial locality: nearby data is likely to be accessed soon.

Mapping determines which set a given address can occupy:

Replacement and write policies

Replacement (when a set is full):

Write policy:

Store buffers and write combining

Between the CPU pipeline and L1, two distinct hardware stages decouple stores from the rest of the machine:

These buffers are the hardware substrate of the x86-TSO (Total Store Order) memory model: stores are visible to other cores in program order, but loads may be reordered with respect to stores. The mfence instruction flushes the store buffer, making all prior stores globally visible. On ARM, the weaker default ordering (stores may be reordered with other stores) requires explicit dmb (Data Memory Barrier) instructions. See Parallelism for the full memory-model discussion.

Locality

Worked example: tracing a write

Trace a 4-byte write to virtual address 0x1000 through a 2-way set-associative L1 (32 KB, 64-byte lines, write-back, write-allocate):

  1. Address decomposition: 32 KB / 64 B = 512 lines. 2-way → 256 sets. Offset = 6 bits (64 B), Index = 8 bits (256 sets), Tag = remaining bits. For 0x1000: offset = 0x00, index = 0x10 (set 16), tag = 0x1.
  2. Tag compare: Check both ways in set 16. Suppose way 0 holds tag 0x1 (hit) → write the 4 bytes into the cached line. Mark the line dirty (write-back).
  3. Store buffer: The 4-byte store enters the store buffer. The pipeline retires immediately.
  4. Write-combining: If this is the first partial write to this line, it is buffered. If a second 4-byte write to the same line arrives before the line is committed, both are coalesced.
  5. Commit: When the store buffer drains, the 4 bytes are written into L1 way 0, set 16. The dirty bit is set. No write to L2 occurs until the line is evicted.
Problem

A 4-way set-associative L1 has 64 KB, 64-byte lines. How many sets does it have? If address 0x3F8C is accessed, what are the tag, index, and offset (assuming a 32-bit address)?

Solution

64 KB / 64 B = 1024 lines. 4-way → 256 sets. Offset = 6 bits, Index = 8 bits (256 sets), Tag = 32 − 6 − 8 = 18 bits.

0x3F8C = 0000 0000 0000 0011 1111 1000 1100

  • Offset (low 6 bits): 001100 = 0x0C
  • Index (next 8 bits): 11111000 = 0xF8
  • Tag (top 18 bits): 000000000000001111 = 0x003F

Prefetching

A hardware prefetcher predicts future memory accesses and loads data into the cache before it is requested, hiding latency and improving effective bandwidth. Prefetching is the primary mechanism that turns a latency-bound random access pattern into a bandwidth-bound sequential one.

Details

Types of prefetchers

Cost of a wrong prefetch

A wrong prefetch (the predicted address is never accessed) has three costs:

  1. Cache pollution: the prefetched line occupies a cache set and may evict a useful line (increasing the miss rate for the actual workload).
  2. Bandwidth waste: the DRAM bus is occupied transferring data that is never used, reducing bandwidth available for real requests.
  3. Coherence traffic: in a multicore system, the prefetched line enters the coherence protocol (MESI), generating snoop traffic for a line no core will use.

Modern prefetchers use a confidence counter: a prefetch is issued only after the stride has been confirmed by 2–3 consecutive accesses. If a prefetch is not consumed before the line is evicted, the counter is decremented, eventually disabling the prefetch for that pattern.

Prefetch depth and cache size

The prefetch distance (how many lines ahead to fetch) must be large enough to cover the DRAM latency (100 cycles) but small enough to fit in the cache. For a 32 KB L1 (512 lines), a prefetch distance of 8–16 lines is safe; 64 lines would evict the working set. The optimal distance is approximately DRAM_latency / access_interval. For a sequential scan at 1 line per 4 cycles, the distance is 100/4 = 25 lines.

Worked example: stride-4 over a 1 MB array

A program accesses a[0], a[4], a[8], … over a 1 MB array (16,384 elements of 64 B each, i.e., 16,384 cache lines). The L1 is 32 KB (512 lines).

Cache coherence

In a multicore system, each core has its own private L1 (and sometimes L2) cache. When two cores access the same memory location, their cache copies can diverge. Cache coherence is the mechanism that ensures all cores see a consistent view of memory.

Details

MESI protocol

Each cache line is in one of four states:

M Modified E Exclusive S Shared I Invalid read miss read miss write write invalidate

MOESI: adds an O (Owned) state between M and S. The owner holds the dirty line and can serve reads to other cores without writing back to memory, reducing bus traffic.

Snoop vs. directory protocols

False sharing

False sharing occurs when two cores write to different variables that reside in the same 64-byte cache line. The coherence protocol sees writes to the same line and invalidates the other core’s copy on every write, causing a coherence ping-pong: each write forces the other core to re-fetch the line from memory (or from the other core’s cache), adding 40–100 cycles per access.

Example: Two threads each increment a counter. If counter_a and counter_b are adjacent in memory (same 64-byte line), every increment of counter_a invalidates counter_b‘s line in the other core’s cache, and vice versa. The fix is padding: allocate each counter in its own cache line (e.g., struct { int counter; char pad[60]; }).

False sharing is the most common real-world coherence performance problem. It is invisible in single-threaded benchmarks and manifests only under parallel load. See Parallelism for the broader concurrency context.

Coherence traffic cost

A coherence miss (e.g., a write to a line in Shared state) requires: (1) invalidating all other copies, (2) waiting for acknowledgments, (3) then proceeding. This adds 20–40 cycles on top of the base DRAM latency. In a 16-core system with a snoop protocol, a single write can generate 15 snoop responses, saturating the interconnect.

Physical memory

DRAM (Dynamic RAM) is the main memory: 100 cycles, 8–128 GB per system. It is volatile (loses data on power loss). SRAM (Static RAM) is used for L1–L3 caches: 4–40 cycles, but 6× the area of DRAM, making it impractical for main memory. NVRAM (Non-Volatile RAM) retains data without power; a small, emerging tier (Intel Optane was discontinued in 2022; CXL-attached persistent memory is the current direction). The register file (flip-flop based) is 1 cycle.

Details

The DRAM cell and why DRAM is slow

A DRAM cell is a 1T1C structure: one transistor (access switch) and one capacitor (stores a charge = 1 bit). The capacitor leaks charge, so DRAM must be refreshed every 64 ms (all rows re-read and re-written). This is why DRAM is called “dynamic.”

A DRAM chip is organized into banks (8–32 per chip), each containing a grid of rows and columns. An access to a specific cell requires a three-step sequence:

  1. Row activation (tRCD ≈ 14 cycles): open the row in the row buffer (sense the entire row, ~1 Kbit).
  2. Column read (tCL ≈ 14 cycles): read the specific column from the row buffer.
  3. Precharge (tRAS ≈ 35 cycles): close the row and restore the capacitor charges.

The minimum time between two accesses to the same bank is tRCD + tCL + tRAS ≈ 63 cycles. This is the fundamental source of DRAM latency. The memory controller hides this by interleaving accesses across banks (see Latency vs. Bandwidth).

DRAM timing (single bank): time → Row Activate (tRCD) Column Read (tCL) Precharge (tRAS) Data on bus tRCD ≈ 14 cyc tCL ≈ 14 cyc tRAS ≈ 35 cyc

SRAM

An SRAM cell is a 6T latch (two cross-coupled inverters + two access transistors). No refresh is needed (the state is maintained by positive feedback), but the 6 transistors occupy 6× the area of a 1T1C DRAM cell. This makes SRAM suitable only for small, fast caches (L1–L3), not for main memory.

NVRAM and emerging tiers

ECC and bandwidth

ECC (Error-Correcting Code): adds parity bits (typically 8 per 64-byte word) to detect and correct single-bit errors. Standard in server DRAM; optional in consumer systems.

Bandwidth: DRAM bandwidth = number of channels × bus width × transfer rate. A dual-channel DDR4-3200 system: 2×8 B×3200 MT/s=51.2 GB/s2 \times 8 \text{ B} \times 3200 \text{ MT/s} = 51.2 \text{ GB/s}.

Virtual memory

Each process sees a contiguous virtual address space (e.g., 48-bit = 256 TB). The MMU (Memory Management Unit) translates virtual addresses to physical addresses using page tables. The TLB (Translation Lookaside Buffer) is a hardware cache of recent translations.

Details

Page tables

A page table maps virtual page numbers to physical frame numbers. On x86-64, a 4-level table: PML4 → PDPT → PD → PT. Each level is a 512-entry table (9 bits of address per level). The default page size is 4 KB (12 bits of offset).

TLB and huge pages

The TLB is a small, fully-associative hardware cache of recent page-table entries (typically 64–1024 entries). A TLB hit: 1 cycle. A TLB miss: 4–5 memory accesses (walk the page table), adding 400–500 cycles.

Huge pages reduce TLB pressure dramatically:

The OS enables huge pages via madvise(MADV_HUGEPAGE) or the hugetlbfs filesystem. Linux Transparent Huge Pages (THP) automatically coalesce 4 KB pages into 2 MB pages in the background. Huge pages are critical for workloads with large, sequential working sets (databases, JVM heaps, scientific computing) where TLB misses dominate.

Page faults

A page fault occurs when a virtual address is not mapped to a physical frame. The OS loads the page from disk (SSD: ≈100μs\approx 100 \mu s, HDD: ≈10\approx 10 ms). A major fault (page on disk) is expensive; a minor fault (page in RAM but not mapped to this process) is cheap (~1 µs).

The software management of virtual memory—swap policy, working-set model, thrashing mitigation, and per-process memory layout (text, data, heap, stack)—is covered in Operating System.

NUMA

NUMA (Non-Uniform Memory Access) exists because each CPU socket has its own DRAM memory controllers physically attached to it (see Physical memory). When a system has multiple sockets, each socket’s cores can access their local DRAM directly, but must traverse an interconnect (e.g., Intel UPI, AMD Infinity Fabric) to reach another socket’s DRAM. Remote accesses also require coherence traffic through the interconnect (see Cache coherence), adding further latency.

Socket 0 Core 0 Core 1 Local DRAM 100 cyc Socket 1 Core 2 Core 3 Local DRAM 100 cyc UPI / Infinity Fabric remote: 140–200 cyc

Accessing local DRAM takes 100 cycles; accessing remote DRAM takes 140–200 cycles (extra hop through the interconnect plus coherence snoop). This non-uniformity means that thread-to-memory placement affects performance.

Details

A 2-socket server with 2 NUMA nodes: each socket has its own DRAM channels (see Physical memory). The interconnect connects the two memory controllers. A core on node 0 reading from node 1’s memory incurs the interconnect latency on top of the DRAM latency, and the coherence protocol must snoop node 1’s caches (see Cache coherence).

Operating systems and runtimes can pin threads and memory allocations to a specific node to minimize remote accesses:

A common anti-pattern: allocating a large array on node 0 (because the allocating thread is pinned there) and then having threads on node 1 access it. The fix is first-touch policy: have the thread that will use the memory perform the first write, so the page is allocated on its local node.

Disk and storage

Storage media

HDD (Hard Disk Drive): 10 ms (10,000,000 cycles). Spinning platter, mechanical arm. SSD (SATA): 100 µs (100,000 cycles). SSD (NVMe): 50 µs (50,000 cycles). NAND flash, no moving parts.

Sectors: the minimum unit of disk I/O (512 bytes for HDD, 4 KB for SSD). A block (file system level) is typically 4 KB.

Details

HDD anatomy: platters (magnetic storage), actuator arm (positions the read/write head), spindle motor (spins the platter at 5400–15000 RPM). Seek time (5–10 ms) dominates; rotational latency (2.5–4 ms) is the wait for the sector to rotate under the head.

SSD: NAND flash cells store 1–4 bits each (SLC, MLC, TLC, QLC). Wear leveling: distribute writes evenly across cells to prevent premature failure. Garbage collection: erase and rewrite blocks to free space. Write amplification: the ratio of bytes written to NAND vs. bytes written by the host. SSDs connect via NVMe over PCIe, which reduces protocol overhead compared to SATA.

Redundancy and striping

RAID (Redundant Array of Independent Disks) combines multiple disks for throughput and/or fault tolerance:

LevelMinimum disksUsable capacityFault toleranceUse case
RAID 02n × singleNoneMaximum throughput (scratch, render)
RAID 12n/2 × single1 diskBoot volumes, small critical data
RAID 53(n−1) × single1 diskGeneral storage, balanced
RAID 64(n−2) × single2 disksLarge arrays, long rebuild times
Details

Quizz

Problem

Part 1 (Cache hit rate): A program accesses a 1 MB array sequentially. The L1 cache is 32 KB (8-way, 64-byte lines), the L2 is 1 MB (16-way), and DRAM is 100 cycles. What is the average access time if the L1 hit rate is 90%, the L2 hit rate is 95% (of L1 misses), and DRAM is 100 cycles?

Part 2 (NUMA): A thread on node 0 reads 50 MB from node 1’s memory. The remote DRAM bandwidth is 25 GB/s (half of local, due to interconnect contention) and the remote latency is 160 cycles. Estimate the total time.

Part 3 (Virtual memory / TLB): A process touches 1 GB of memory sequentially with 4 KB pages. The TLB has 1024 entries (fully associative, LRU). Estimate the TLB miss rate and the average address-translation cost (TLB hit = 1 cycle, TLB miss = 4 memory accesses at 100 cycles each).

Solution

Part 1: Given hit rates.

  • L1 hit: 4 cycles. Probability: 0.90.
  • L2 hit: 4 + 12 = 16 cycles. Probability: 0.10 × 0.95 = 0.095.
  • DRAM hit: 4 + 12 + 100 = 116 cycles. Probability: 0.10 × 0.05 = 0.005.

Average: 0.90×4+0.095×16+0.005×116=3.6+1.52+0.58=5.70.90 \times 4 + 0.095 \times 16 + 0.005 \times 116 = 3.6 + 1.52 + 0.58 = 5.7 cycles.

What actually happens with sequential access: For a 1 MB sequential access, 1 MB/64 B=16,3841 \text{ MB} / 64 \text{ B} = 16{,}384 cache lines. The L1 holds 512/16384≈3512/16384 \approx 3% of the array. Without prefetch, the hit rate is ≈ 0% (all compulsory misses). With a stride/next-line prefetcher, the effective hit rate can reach 80–90%, bringing the average close to the 5.7-cycle figure.

Part 2: NUMA remote read.

50 MB at 25 GB/s: 50×106/(25×109)=2 ms50 \times 10^6 / (25 \times 10^9) = 2 \text{ ms}. The initial latency (160 cycles ≈ 160 ns at 1 GHz) is negligible compared to the transfer time. Total ≈ 2 ms. For comparison, the same 50 MB from local DRAM at 50 GB/s would take 1 ms.

Part 3: TLB miss rate.

1 GB / 4 KB = 262,144 pages. The TLB holds 1024 entries. For sequential access, the TLB covers 1024×4 KB=4 MB1024 \times 4 \text{ KB} = 4 \text{ MB} of the working set at any time. After the initial fill, every 1024th page access is a TLB miss (the oldest entry is evicted).

TLB miss rate = 1/1024≈0.11/1024 \approx 0.1%.

Average translation cost = 0.999×1+0.001×400=0.999+0.4=1.40.999 \times 1 + 0.001 \times 400 = 0.999 + 0.4 = 1.4 cycles per access. (4 memory accesses × 100 cycles = 400 cycles for a TLB miss.)

With 2 MB huge pages: 1 GB / 2 MB = 512 pages. The TLB (1024 entries) holds all 512 pages → 0% TLB miss rate. Average cost = 1 cycle. This is a 1.4× improvement in translation overhead, and the benefit grows with working-set size.