Engineering Atlas › Foundations

How a Computer Runs Your Code: CPU, Memory and Disk

15 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

From source code to instructions

You write Python, TypeScript or Go. Something turns it into machine instructions the CPU understands. Go is compiled ahead of time to a native binary. TypeScript compiles to JavaScript, which a runtime such as V8 interprets and then just-in-time compiles hot functions to machine code. CPython compiles your file to bytecode and runs it in an interpreter loop, which is itself a compiled C program. The paths differ, but at the bottom a CPU core is executing machine instructions such as "load this address into a register", "add two registers", "jump if zero".

The fetch-decode-execute loop

A core keeps a program counter: the address of the next instruction. It fetches the instruction from memory, decodes what it means, executes it (an arithmetic operation, a memory load or store, or a branch), and moves on. Modern cores overlap many instructions in a pipeline, run several at once, guess which way branches will go and execute ahead speculatively. A 3 GHz core can finish several instructions per nanosecond when its data is ready. When the data is not ready, it waits.

The memory hierarchy

LevelTypical sizeRough latency
RegistersHundreds of bytesUnder 1 ns
L1 cacheTens of KB per coreAbout 1 ns
L2 cacheHundreds of KB to a few MB per coreAbout 4 ns
L3 cacheTens of MB sharedAbout 10 to 40 ns
Main memory (RAM)GBsAbout 100 ns
NVMe SSD random readTBsAbout 10 to 100 µs
Network round trip in a data centreAbout 0.5 ms
Spinning disk seekTBsAbout 5 to 10 ms
Round trip across a continentAbout 50 to 150 ms

The exact numbers vary by hardware and year; the ratios are what matter. The figures above match the interactive table by Colin Scott, which extrapolates a classic list forward by year and holds the data-centre round trip fixed (Colin Scott's interactive latency table). The older 2012 list that many people quote has L1 at 0.5 ns, L2 at 7 ns, a 4 KB SSD read at 150 µs and a disk seek at 10 ms (Bonér's latency gist). Treat both as orders of magnitude and measure your own machine. If an L1 hit were one second, a RAM access would be about two minutes, an SSD read a few hours to a day and a cross-continent round trip two to five years.

Caches and locality

The CPU never fetches a single byte from RAM. It fetches a cache line, usually 64 bytes, and keeps it in cache (Drepper, What Every Programmer Should Know About Memory, section 3). If the next access falls in the same line, it is almost free. If the program jumps around memory, every access can miss and pay the RAM price. Two kinds of locality make caches work:

Arrays and slices are laid out contiguously, so walking them in order is cache friendly. Linked lists, maps of pointers and objects scattered across the heap are not. Hardware prefetchers also spot simple sequential or fixed-stride access and fetch lines before the program asks for them, which is why sequential code often looks nearly free; Drepper devotes section 6.3 to prefetching and shows in section 6.2.1 how reordering a matrix multiply to walk memory sequentially cuts its run time sharply.

Disk, files and the page cache

Disks and SSDs store data in blocks of a few KB. The operating system keeps recently read file blocks in RAM in the page cache, so the second read of a file is often a memory read, not a disk read. Writes usually land in the page cache first and reach the disk later, when kernel flusher threads write out data that has been dirty long enough or when dirty memory passes a threshold (Linux vm sysctl docs). fsync blocks until the file's data and metadata are on the device, including flushing the drive's own write cache, but it does not make the file's directory entry durable: for a newly created file you must also fsync the directory (fsync(2)). That is why databases call fsync on commit, and why "it was written" and "it survives a power cut" are different claims.

A failed write-back is the nasty case. After a buffered write fails at the hardware level, Linux filesystems typically drop the affected pages and mark them clean, so a later fsync can report success for data that never reached the disk (LWN: PostgreSQL's fsync() surprise). Error reporting improved in Linux 4.13 and later, and PostgreSQL now defaults data_sync_retry to off, so a failed flush crashes the server and recovers from the write-ahead log instead of retrying (PostgreSQL docs).

The same shape everywhere

A small, fast, expensive store in front of a big, slow, cheap one, with a policy for what to keep: that is the L1 cache, the OS page cache, an application cache, Redis, and a CDN. Hit rate, eviction, invalidation and warm-up are the same questions at every level. Learn them once here and you will recognise them in every system design interview.

Down the stack

Layer 1The contract

Your program sees a flat address space: memory is a long array of bytes, every load returns the last value stored, and files are byte streams that persist. Latency is not part of this contract. Two programs that are equally correct can differ in speed by ten times purely because of how they touch memory and storage.

Layer 2The mechanism

Each process gets virtual memory: addresses that the CPU's memory management unit translates to physical RAM pages, usually 4 KB each, through page tables cached in a translation lookaside buffer (TLB) (OSTEP, Ch 18: Paging and Ch 19: TLBs). Caches are split into sets; an address maps to one set, and when a set is full the hardware evicts a line, roughly least recently used. When a page is not in RAM, a page fault hands control to the OS, which loads it from disk or swap (OSTEP, Ch 21: Swapping). Linux's vm.swappiness (default 60, range 0 to 200) sets how eagerly it swaps anonymous memory instead of dropping file cache (Linux vm sysctl docs). Storage devices read and write whole blocks, and the OS batches and reorders I/O.

Layer 3The machine

RAM is far from the core in time, not distance: each access goes through the memory controller and DRAM rows have to be opened and read. Caches are SRAM built into the CPU die, much faster but much more expensive per byte, which is why they are small. SSDs are flash chips behind a controller that maps logical blocks to physical pages, erases in large blocks and writes in small pages. Spinning disks must physically move a head and wait for the platter to rotate, which is why random reads on them cost milliseconds.

Why it's built this way

  1. Fast memory is expensive and small; cheap memory is big and slow. Physics and cost force a trade-off.
  2. Real programs reuse data and touch nearby data.
  3. So a hierarchy of caches gives most accesses the speed of the small store at the price of the big one.
  4. When a workload breaks locality, it falls through to the slow level and performance collapses.
  5. The same economics produce caches in software and across networks, with the same failure modes.

Diagram

flowchart LR
  CPU["CPU core: registers"] --> L1["L1 cache, about 1 ns"]
  L1 -->|miss| L2["L2 cache, about 4 ns"]
  L2 -->|miss| L3["L3 cache, tens of ns"]
  L3 -->|miss| RAM["RAM, about 100 ns"]
  RAM -->|page fault| SSD["SSD, tens of µs"]
  SSD -.->|remote data| NET["Network, 0.5 ms and up"]

Worked example

A service computes a daily report by summing a 10,000 by 10,000 grid of 8-byte numbers (800 MB) stored row by row. A new engineer writes the loop with the column index outside and the row index inside. It takes about 9 seconds. Swapping the loops so the code walks each row in order brings it under 1 second, with the same arithmetic.

Why: in row order, each 64-byte cache line holds 8 consecutive numbers, so one RAM fetch serves 8 additions, and the hardware prefetcher sees the sequential pattern and fetches ahead. In column order, consecutive accesses are 80,000 bytes apart, so every access touches a new line (and a new 4 KB page, which also strains the TLB), and by the time the loop comes back to a line it has often been pushed out of L1 and sometimes out of the whole cache. Almost every addition pays a deep-cache or RAM latency. The 9 and 1 second figures are illustrative; the gap on real hardware depends on cache sizes and associativity, so measure rather than trust them. Drepper's matrix example in section 6.2.1 shows the same effect with measured numbers (Drepper).

The same team later finds the report reading 2 GB of input files. The first run of the day takes 40 seconds; reruns take 6. The difference is the page cache: the first run reads from SSD, later runs read file blocks already in RAM. The fix is to read the files once, in large sequential chunks, rather than to buy a faster disk.

A back-of-envelope check helps here: 100 million random RAM accesses at about 100 ns each is 10 seconds; 100 million sequential cache-friendly reads is a fraction of a second. You can make that estimate before writing any code.

In the wild

Trade-offs

ChoiceStrengthsWeaknessesChoose when
Contiguous arrays or slicesCache friendly, prefetchableCostly inserts in the middleIteration-heavy data
Pointer-based structures (lists, trees)Cheap inserts and deletesScattered memory, cache missesFrequent structural changes
Keep data in RAMNanosecond accessLimited size, lost on crashHot working sets
Read from SSD on demandHuge capacity, durableMicroseconds per random readCold or large data
Large sequential I/OHigh throughputReads more than you may needScans, batch jobs
Small random I/OReads only what you needPays latency per requestPoint lookups with an index

Pitfalls in production

Leadership lens

Check yourself

  1. Why is iterating over an array usually faster than iterating over a linked list of the same length?

    Show answer

    The array is contiguous, so each cache line fetched from RAM holds several elements and the prefetcher can load ahead; list nodes are scattered, so each step can be a cache miss costing about 100 ns.

  2. Roughly how much slower is a RAM access than an L1 cache hit, and a data-centre round trip than a RAM access?

    Show answer

    About 100 times for RAM versus L1, and about 5,000 times for a 0.5 ms round trip versus a 100 ns RAM access.

  3. Why does the second run of a file-processing job often finish much faster than the first?

    Show answer

    The OS page cache kept the file blocks in RAM after the first read, so the second run reads from memory instead of disk.

  4. What do a CPU cache and a CDN have in common?

    Show answer

    Both are small fast stores in front of a big slow one, rely on locality of access, and face the same questions of hit rate, eviction, invalidation and cold start.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →