Gates → GPUs · Notes
Stanford CS149 · Lecture 1 companion

Why parallelism? Why efficiency?

Imagine a program that takes a minute to process a video. For years, you could often make it faster simply by running it on next year's processor. Around 2005, that reliable improvement in single-thread speed slowed sharply. To get the minute down now, you need to know which parts of the work can happen together, how the workers share results, and how quickly they can reach the data. This lecture builds that argument from the ground up.

You'll be able to
Calculate speedup and parallel efficiency, and explain how communication and uneven work limit them. Trace an instruction through a simple processor, identify safe instruction overlap, and explain why wider cores and faster clocks faced diminishing returns. Follow a load through memory and the cache hierarchy, from address to latency.
Background
None required. This page builds everything it uses. Units 1–3 of these notes go deeper into the processor, and Lecture 2 continues directly from here.
Source
Follows the structure and examples of Stanford CS149 Parallel Computing, Fall 2023, Lecture 1, "Why Parallelism? Why Efficiency?" (Kayvon Fatahalian and Kunle Olukotun). The course-logistics slides are left out. The explanations, diagrams and explorers here are original study material, not a copy of the slides.
The path through this lecture: first measure what parallel workers actually gain. Then see why chip designers stopped making one instruction stream dramatically faster and started adding more processing units. Finally, follow the data those units need: a fast arithmetic unit has little value while it waits for memory.
Part 0 · The course's big ideas

What parallel computing is about

A definition, a measure of success, and three classroom experiments that reveal what gets in the way.

Section 1

A parallel computer, and how to measure it

Suppose you need to add a million numbers. One worker could add them all; several could each add a share and then combine their partial sums. A computer doing the latter is a parallel computer: a collection of processing elements (workers such as CPU cores) that cooperate on a problem. Cooperation is useful only if the workers save more time on arithmetic than they spend dividing work, exchanging results, or waiting for one another.

We measure the result with speedup. Run the same job with one processor and with P processors, then divide the two execution times:

speedup(P processors) = execution time using 1 processor ÷ execution time using P processors

If the job takes 60 seconds on one processor and 20 seconds on four, its speedup is 60 / 20 = 3×. Four processors could ideally finish in 15 seconds, a 4× speedup, if every part divided perfectly and cooperation were free. A second measure compares the observed gain with that ideal:

efficiency = speedup ÷ P

Here, efficiency is 3 / 4 = 75%: the observed speedup is three quarters of the ideal fourfold gain. That number tells us how far we are from ideal scaling, but not why. The next experiment separates two possible causes: uneven work and the cost of combining results.

Use a fair one-processor baseline. Compare with a good sequential solution to the same job and input. Running a parallel program on one processor can include extra splitting and combining work, making the baseline slower and the reported speedup look better than it is.
Section 2

Three experiments: what limits speedup

The lecture makes processors out of students and asks them to add a pile of numbers. The explorer below models the same exercise. Split N numbers among P processors. Each processor adds its own share (one time unit per addition) and produces a partial sum. Those partial sums must become one final answer: passing one to another processor costs c time units per message, and adding two partial sums costs one more.

Start with the presets in order. Each changes one source of delay:

  1. Demo 1: communication costs time. Two workers split the additions, but one must pass its result to the other. Moving closer reduces c, so the same arithmetic finishes sooner.
  2. Demo 2: an uneven split leaves workers waiting. With four workers, the job finishes only when the last one finishes. Balancing the shares shortens that longest stretch.
  3. Demo 3: too many workers can slow the job. Each worker gets fewer additions, but gathering more partial sums takes longer. A different combining plan can change the outcome.
Preset:
Processors P
Numbers to add N =
Cost per message c = time units
Work split
Combine partial sums

Where the time goes. Each row is one processor over time. Yellow: adding up its own share. Teal: passing a partial sum (sender and receiver are both busy). Orange: adding a received partial sum. Hatched: idle. The lower chart repeats the whole experiment for P = 1 to 32 with the current settings. The dashed line is ideal speedup (P×). Try Demo 3 and watch the curve peak and then fall as more processors are added.

Read the timeline from left to right: the job ends when its last required result arrives, not when the average processor finishes. Demo 2 shows this directly. Next compare Demo 3's two combining plans. If all P workers send to processor 0, it handles P − 1 messages in sequence. In a tree, pairs combine at the same time, then pairs of pairs combine. For 32 workers, that takes five communication rounds rather than 31 sequential messages to one receiver. The tree still communicates; it makes much of that work overlap.

PredictAnswer before opening, then check with the explorer.
  1. With N = 32, c = 8 and the even split, is 2 processors faster than 1? By how much?
    Show answer

    One processor needs 31 additions: 31 time units. Two processors each add 16 numbers (15 additions), then one message (8) and one final addition (1): 15 + 8 + 1 = 24 time units. The speedup is 31 / 24 ≈ 1.29×, far from 2×. Communication cost 8 of those 24 units. With c = 1 it becomes 31 / 17 ≈ 1.82×.

  2. Why does adding processors eventually make Demo 3 slower?
    Show answer

    Doubling P halves each processor's share of the additions, but the combining work grows: more messages, each costing c. Once each share is tiny, nearly all the time is communication, and more processors add more of it. The best P balances the two costs.

Section 3

The course's three themes

Theme 1 · Parallel thinkingBreak a job into pieces that can run together, assign those pieces evenly, and arrange communication and synchronization—waiting when one piece needs another's result. A program scales when adding workers continues to reduce its running time.
Theme 2 · How the hardware worksUnderstand what processors and memory actually do, so you can predict when a design choice makes work cheaper or more expensive.
Theme 3 · EfficiencyCompare a program's speedup with the hardware it uses. A 2× speedup on 10 processors has 20% parallel efficiency, even though the program finishes sooner.
Why hardware mattersThe demos made communication cost visible. On a real machine, memory and communication costs likewise determine whether a promising way to split work pays off.
Parallel thinking, in three steps. Decompose the job into pieces that can safely run at the same time. Assign the pieces to processors, keeping the load balanced. Orchestrate: arrange the communication and synchronization the pieces need, keeping it small and cheap. Each step corresponds to a demo lesson: balance (Demo 2) and communication (Demos 1 and 3).

A 2× speedup on 10 processors reaches only 20% of ideal scaling. That figure alone cannot tell us whether processors sat idle, spent time communicating, or did extra work; the timeline and the program can. The programmer chooses the work split and coordination. The hardware designer chooses capabilities under limits of chip area, power, and cost. Understanding both sides will matter once we ask why computers acquired so many parallel workers in the first place.

Part 1 · Why parallelism?

The free lunch, and why it ended

Why programmers could ignore parallelism for decades, and why they no longer can.

Section 4

Historical context: why avoid parallel processing?

Our summing experiment showed that parallel work takes planning. For a long time, programmers could often avoid that planning. From the mid-1980s to the early 2000s, a program running on one processor typically gained speed as new processors arrived; single-thread performance grew at roughly a doubling every 18 months over that period. The cumulative gain was more than a thousandfold.

A thousandfold in under twenty years. A logarithmic axis turns steady doubling into a straight line: each grid line is 10× the one below. The line is a trend (doubling about every 18 months), not measured data. The lecture's chart plots individual processors (Olukotun and Hammond, ACM Queue 2005), and they follow the same slope.

That changed the programmer's calculation. Rewriting a program for two processors might take substantial effort and, after communication costs, yield less than 2×. At the historical rate on the chart, a new processor could provide a comparable single-thread gain in about a year without rewriting the program. The benefit came from new hardware, not from free computation; for software authors it felt like a free lunch.

Wait this many years for a new CPU:

The "free lunch" calculator. At a doubling every 1.5 years, waiting y years gives a speedup of 2y/1.5, with no programmer effort. Compare that with the result from the demo explorer above.

Two important sources of that single-thread gain were:

  1. Instruction-level parallelism (ILP): a processor overlaps independent instructions from one program, even though the program was written as a sequence.
  2. Higher clock frequency: more clock cycles each second give the processor more opportunities to advance work.

Neither source could keep delivering the same gains indefinitely. To see the limits, we first need a picture of the instructions a processor receives and the state they change.

Section 5

What is a program, to a processor?

Start with x = 1. Double it ten times and the result is 1024. In C, a short loop expresses that whole job; a compiler turns it into machine instructions. Each asks for a smaller operation, such as copying a value, adding, comparing, or jumping to another instruction. The figure writes those instructions in readable assembly notation; the processor receives their binary encoding.

A program is just a list of processor instructions. The loop portion of the compiled output, x86-64 as shown in the lecture. Each C statement becomes a few instructions. The loop test i < 10 becomes a compare and a conditional jump. x = x + x becomes load, add and store, because this unoptimized build keeps x in memory at -20(%rbp). The loop ends with an unconditional jump back to the test. The number on the left of each instruction is its address in memory.

You can read these instructions like a recipe: each line says what to do next, and later steps rely on values produced by earlier ones. The processor must preserve those relationships to get the right answer. As we will see, it can still overlap steps that do not rely on each other.

Follow one trip through the unoptimized loop. It tests the counter, loads x, doubles and stores it, increments the counter, and jumps back. The addl that doubles x is only one of nine instructions on that path; the rest make the loop work. An optimizing compiler could reduce this bookkeeping. The example makes clear why speed depends on more than the arithmetic operation alone.

Section 6

What does a processor do? It executes instructions

To run the compiled program, a processor repeatedly finds an instruction, gets its input values, performs the operation, and records the result. The lecture's very simple processor groups the relevant hardware into three parts, drawn consistently in the diagrams:

  • Fetch/decode uses the program counter to find the next instruction and determine what it asks for. The program counter is the address of the next instruction.
  • The ALU (arithmetic and logic unit) performs operations such as addition. This model also needs a way to load from and store to memory, which we will meet later.
  • The execution context includes registers, small storage locations that hold values the program is using. Together with memory and the program counter, those values form the program's state: the information needed to continue execution.

Step through add R0 ← R0, R1. It means: read R0 and R1, add their values, then put the answer in R0. The arrow points to the destination; R1 keeps its old value.

Four conceptual steps. (1) Fetch the instruction. (2) Read R0 = 32 and R1 = 64. (3) Add to get 96. (4) Write 96 into R0. The diagram shows the data-register change at the last step; it leaves out the program counter, which also advances as instructions run. R1, R2, and R3 keep their values.

These are phases of an instruction, not necessarily four separate clock cycles. For the timing examples that follow, the lecture assumes a simplified processor that finishes one instruction per clock. Real processors overlap phases and handle several instructions at once. Lecture 2's Section 0 lets you follow whole programs phase by phase.

Review: what is "state"? Instructions describe operations; state records their effects. After this add, R0 is 96 and R1 is still 64. The program counter identifies the next instruction, and memory holds values beyond the registers. The processor must preserve the intended state changes even if it overlaps instructions.
Section 7

Instruction-level parallelism: more than one instruction per clock

The simple processor finishes one instruction per clock. Consider a calculation that has more independent work: a = x*x + y*y + z*z. Put x, y, and z in registers R0, R1, and R2. In our simplified instruction set, the calculation takes five instructions:

1mul R0, R0, R0// x*x
2mul R1, R1, R1// y*y
3mul R2, R2, R2// z*z
4add R0, R0, R1// x*x + y*y
5add R3, R0, R2// … + z*z → a

At one instruction per clock, this takes five clocks. But the three multiplies have no dependencies on one another: each reads and writes a different register. A processor with more execution capacity can overlap them. Each add must wait for its own inputs: instruction 4 needs the results of 1 and 2, and instruction 5 needs the results of 3 and 4. An independent multiply can still share a clock with instruction 4 if its inputs are ready.

Those opportunities to overlap instructions from one program are called instruction-level parallelism (ILP). The arrows in the graph show dependencies. Even with unlimited execution units, the chain 1 → 4 → 5 (or 2 → 4 → 5) requires three successive clocks in this one-cycle-per-instruction model. Extra units cannot shorten a chain whose next instruction needs the previous one's answer.

Instructions per clock:
Hardware:

Parallel, but still correct. With x = 1, y = 2, and z = 3, the answer is 1 + 4 + 9 = 14. The small number on each graph node is the clock in which it runs. With dependency checking, widths 2 and 3 both finish in three clocks. To see a failure, choose 3 instructions per clock and just takes the next few: that schedule puts instructions 4 and 5 together, so 5 reads R0 before 4 updates it. At widths 1 and 2, this particular naive schedule happens to be safe.

Correct overlap preserves the result of sequential execution; it does not require every instruction to finish in source order. A later instruction may run early when it has all its inputs, but it must never read a value that an earlier instruction still needs to produce. Dependency checking enforces that rule. This is the parallelism a single processor core can find without asking the programmer to split the program into threads.

Section 8

Superscalar execution, and its diminishing returns

A processor that can start more than one instruction from a single stream in a clock is superscalar. It has multiple execution units and control logic that tracks which inputs are ready. The program is still written as one instruction stream; the hardware looks for safe overlap, as the previous explorer did.

A two-wide superscalar core. Two fetch/decode units and two execution units, so it can decode and execute up to two instructions per clock. The out-of-order control logic tracks dependencies to decide which instructions can safely go together. There is still one execution context: this is parallelism within one instruction stream. The lecture's aside, the Pentium 4, is a chip where a large fraction of the silicon was this kind of control logic.

The five-instruction example had room for three multiplies at once, then only one useful add at a time. Larger programs also contain dependencies and branches that limit how far the processor can look ahead. A wider core needs more execution units and more control logic, but it cannot create independent instructions the program does not have.

Diminishing returns in the illustrated workloads. The chart redraws speedups reported by Culler and Singh using Johnson's 1991 data. Raising issue capability from one to four instructions per clock gives about 2.6×; raising it from four to sixteen adds little in this example. The curve illustrates a limit, not a universal four-wide optimum for every program.

This is the first wall in the lecture's argument. Past some width, extra hardware costs chip area and power while yielding little single-thread speed. The limit depends on the workload, but every program has dependency chains. Lecture 2, Section 2 and Unit 3 show how a core looks for the independent work that remains.

Section 9

The power wall: why clocks stopped getting faster

A core can also finish work sooner by running its clock faster. Its clock marks cycles in which work can advance, so more cycles per second can mean more instructions completed per second. Clock rates rose for decades but largely leveled off in the mid-2000s, while transistor counts kept growing. The limiting question became how much power a chip could use and cool.

Two contributions matter in a simplified power model:

  • Dynamic power is spent when circuits switch. For a fixed amount of switching per cycle, it grows roughly as switched capacitance × voltage² × frequency. Running more cycles each second raises this cost.
  • Static power comes from leakage: a powered chip uses some energy even when circuits are not switching.

On a given chip design, reaching a higher clock often requires a higher supply voltage to keep switching reliable. Voltage appears squared in the dynamic-power term, so raising both voltage and frequency can make power climb much faster than clock rate. In the lecture's illustrative range, moving from about 2.4 to 4.8 GHz costs several times the power. Nearly all of that electrical energy ends up as heat that the cooling system must remove.

Clock frequency:

Doubling the clock can cost several times the power. This is an illustrative model of one desktop core: it assumes voltage rises linearly with frequency, dynamic power follows V² × f, and static power grows with voltage. Its curve resembles the lecture's measurements of Intel i7-2600K and i7-3770K chips. Neither the exact wattage nor that voltage rule applies to every processor.

Power budgets vary greatly across devices. The table gives rough figures from the lecture, including thermal design power (TDP): a cooling-design rating, not a promise that a device always consumes that many watts. The i9-10900K's 95 W figure in the lecture is its configurable lower TDP; its default TDP is 125 W, used here.

DevicePower
Mobile phone processor½–2 W
Apple M1 laptop chip13 W
Intel Core i9 10900K (desktop CPU)125 W default TDP
NVIDIA RTX 4090 GPU450 W
Standard microwave oven (for scale)900 W
World's fastest supercomputermegawatts

The exact numbers are less important than the spread: a phone must work within a few watts, while a desktop chip may have over a hundred. Neither can raise clock speed without considering heat and battery or operating cost. Designers therefore look for more useful work within a fixed power budget.

Spend the power budget on more, slower cores

One option is to divide work between two slower cores. Each core may finish its own portion less quickly than a fast core would, but together they can finish more portions per second within the same budget. That total rate is throughput. Move the power-budget slider and compare the bars for total throughput with the line for one core's speed.

Chip power budget:

More cores, lower clocks, more total work, if the program is parallel. Each bar is the chip's total throughput (cores × frequency, assuming the work splits perfectly). The line is the speed of each individual core, which is what a single-threaded program gets. Throughput rises quickly at first, then flattens as static power, which doesn't shrink with frequency, eats a growing share of the budget. Same illustrative power model as above.

The bars rise only if the work divides across cores; the summing exercise already showed why that assumption can fail. A single thread uses one core and follows the line. In this fixed-budget model, giving the chip more cores lowers the clock available to each, so that single thread can even slow down. Multiple cores create an opportunity for more throughput, not an automatic speedup for every program.

Section 10

ILP tapped out, frequency stopped: no more free lunch

Now the two limits fit together. Wider execution units found diminishing amounts of independent work in one instruction stream; higher clocks became too costly in power. The lecture captures this change in a stylized chart of processor trends, after Herb Sutter's 2005 article "The Free Lunch Is Over":

More transistors no longer meant the same single-thread gain. The chart shows trends rather than measured data (log scale). Transistor counts continued to rise while clock-frequency and instruction-level gains slowed around the mid-2000s. The flattening is an approximation of a shift in growth rates, not a claim that single-thread performance or transistor progress literally stopped.

That shift changed where chip designers spent their growing transistor budgets:

  • Single-thread speed still improves, but the old rate of improvement became much harder to sustain through higher clocks and wider issue alone.
  • Designers increasingly use transistors for parallel units: more CPU cores and vector units that operate on several data values at once. They also build specialized units for tasks such as graphics, video, and neural networks.
  • To benefit from those extra units, software must give them useful work. An unchanged single-thread program cannot use several cores simply because they are present.
Keep the trends separate. The mid-2000s power wall did not make transistor counts stop rising. It made it harder to turn additional transistors into faster execution of one unchanged instruction stream. Parallel hardware offers another route, provided a program has work to feed it.
Section 11

Parallel machines are everywhere

The design response is visible across machines of very different sizes. Even a small Raspberry Pi has several CPU cores; a supercomputer joins hundreds of thousands of them with GPUs. The chart counts several kinds of parallel hardware, so compare the scale while keeping the units in mind:

Parallel units in the lecture's examples. The scale is logarithmic: each grid line is 10× the last. CPU entries count cores; the GPU entry counts 32-bit floating-point units in the full AD102 design, not CPU-like cores. The shipped RTX 4090 enables fewer units than that full design. Frontier's figures describe its Fall 2022 configuration: 9,472 64-core AMD CPUs and 37,888 GPUs, with power around 21 MW. These unlike units cannot be compared one-for-one as measures of speed.
MachineParallel hardwareNotes
Raspberry Pi 34 Arm A53 coresA $35 board is a quad-core parallel computer
Apple A15 Bionic (iPhone 13, 14)2 "big" + 4 "small" CPU cores, multi-core GPU15 billion transistors; power-limited, so small cores do light work efficiently
Intel "Comet Lake" Core i9 (2020)10 CPU coresA typical desktop CPU
AMD Ryzen Threadripper 3990X64 CPU cores, up to 4.3 GHz boostBuilt from several multi-core chiplets in one package
NVIDIA full AD102 design (2022)18,432 fp32 units in 144 SMsRTX 4090 enables 16,384 units in 128 SMs
Frontier supercomputer606,208 CPU cores + 37,888 GPUs21 MW

What using it all is worth

The course's first programming assignment gives one striking, workload-specific result. Its optimized (-O3) single-threaded C baseline runs on a quad-core Intel CPU. A carefully rewritten version uses multiple cores, vector instructions, and hardware threads to run about 32–40× faster on that assignment. That gain is evidence of unused parallel capacity in the baseline, not a promise that every program can improve by the same factor.

Where the assignment's opportunity comes from. Four cores can work on different pieces (up to 4×). A 256-bit vector instruction can operate on eight 32-bit values at once (up to 8× per core). Hardware threads can sometimes fill otherwise unused execution time. Multiplying 4 × 8 describes available parallel lanes; actual speedup depends on whether the work divides well and data arrives fast enough. Lecture 2 develops these mechanisms.
Part 2 · Why efficiency?

Parallel is not enough

With power as the limit, the winning designs are the ones that get the most work per joule.

Section 12

Software must be efficient too: specialization

So far, parallel efficiency has meant speedup divided by processor count. Power adds another question: how much useful work does the machine complete per joule of energy, or how much useful throughput per watt does it deliver? More cores are one answer when work divides well. Another is specialization: hardware designed for a recurring kind of work can often do it with less energy than a general-purpose CPU, by simplifying or sharing some of the CPU's control machinery.

A phone chip is a collection of specialists. This stylized, not-to-scale Apple A15 drawing follows the lecture's annotated die photo. Large CPU cores favor responsive single-thread work; smaller cores handle lighter tasks with lower power. The GPU, Neural Engine, image and video units, and motion processor serve different workloads. Software and the operating system must send work to an appropriate unit to benefit from its design.

The same principle reaches datacenters. Google's TPU (Tensor Processing Unit), for example, is designed around machine-learning operations and deployed in groups of chips. The lecture shows other neural-network accelerators as well. Their details differ, but each trades some generality for a chance to do a common workload more efficiently.

The classroom workers had to pass partial sums to one another. A chip's workers likewise need inputs delivered and results carried away. Even a specialized arithmetic unit spends time and energy waiting for or moving data. The next part starts with the programmer's simple view of memory, then follows what a load costs in real hardware.
Part 3 · Memory

Accessing data efficiently

What memory is, why reaching it is slow, and how caches make it faster.

Section 13

What is memory? An array of bytes

Before asking why data is slow to reach a core, start with the view a program uses. Its address space looks like an array of bytes, each with a numbered address. A byte holds eight bits, enough to represent a value from 0 to 255. Addresses here use hexadecimal: the 0x prefix marks base 16, so 0x10 is decimal 16. In the explorer, click that address to read the byte stored there.

Read:
Click any address.

A 32-byte teaching example. Its addresses run from 0x0 to 0x1F. Choose “a four-byte value” and one read spans four consecutive addresses. This example treats an integer as four bytes and uses little-endian order: the lowest-addressed byte contributes the least-significant part of the number. Real machines have much larger address spaces; the size of a C int and supported byte order depend on the platform.

The simple processor moves values with two kinds of instruction. A load copies data from a memory address into a register; a store copies a register's value to a memory address. In this teaching model, arithmetic uses register values. For example, st mem[R2] ← R0 would write R0's value to the address held in R2. Other instruction sets, including x86, can also use memory operands in arithmetic instructions.

A load: ld R0 ← mem[R2]. R2 contains the address 0xff681080; a register holding an address acts as a pointer. The load reads the four-byte value beginning there, 42, and copies it into R0, replacing 96. R2 keeps the address, and neighboring memory values are unchanged. The address tells the processor where to read; the value 42 is what it finds.
Section 14

Memory latency, and stalls

A load starts a request; its data arrives later. The time between asking and receiving is memory access latency, often measured in clock cycles. At 4 GHz, one cycle is a quarter of a nanosecond: a four-cycle cache hit is about 1 ns, whereas a 248-cycle DRAM access is about 62 ns. The precise times vary by machine and where the data is found.

Latency can create a stall, a stretch when the processor cannot advance a needed instruction because its input is not ready. The next three-instruction example loads two values and adds them:

1ld r0, mem[r2]// load a
2ld r1, mem[r3]// load b
3add r0, r0, r1// needs both loads

The add needs both loaded values, so it must wait for the slower of the two arrivals. The second load does not depend on the first, however: a processor that allows outstanding loads can send both requests before either returns. In a longer program, it might do other independent work while waiting as well.

Where the data is:
Processor:

Watch the waiting change. The choices use illustrative Kaby Lake latencies at 4 GHz: L1 4 cycles, L2 12, L3 38, and DRAM about 248. A blocking processor waits after each load; one that can have both loads outstanding overlaps their latency and waits at the dependent add. The model shows only these three instructions, so it has no other useful work to run while the add waits.
Section 15

Caches: a faster implementation of the same abstraction

The address-space view tells the programmer which value a load requests; it says nothing about how long the load takes. A cache keeps recently used data close to the core. If the requested data is present, the access is a hit and can finish quickly. Otherwise it is a miss, and the hardware must fetch the data from a slower level.

Caches are managed by hardware. A correctly synchronized program still reads and writes according to its memory model, even though caches can make different accesses take very different amounts of time. With multiple processors, keeping their cached views consistent requires extra machinery, a topic for later lectures.

The hardware transfers data in fixed-size, aligned cache lines. In the toy cache below, each line is four bytes: addresses 0x0–0x3 share one line, and 0x4–0x7 share the next. A miss on 0x0 brings in its whole line, so a following read of 0x1 can hit. The cache holds only two lines at once. When full, it uses least recently used (LRU) replacement: evict the line that has gone longest without an access.

Access sequence:
Cache size:

Follow the cache one access at a time. Each row reports the requested address, hit or miss, and the lines retained afterward (least recently used first). Colored underlines group bytes by four-byte line. Run Example 1 and note which later addresses reuse the first loaded line. Example 2 reads the full 16-byte array, then returns to 0x0 after its line may have been evicted.

Caches help when accesses have locality, a pattern in which future requests are likely to use data already brought close to the core. The examples show two forms:

  • Spatial locality means using nearby addresses. In Example 1, the first access to 0x0 misses, but 0x1, 0x2, and 0x3 hit in the line that miss fetched.
  • Temporal locality means using an address again soon. Repeated reads of 0x2 and 0x1 hit while their line remains cached.

In this model, a cold miss is the first request for a line that has not yet been brought into the cache. A capacity miss occurs when a previously held line was evicted because the cache could not hold all the lines the program kept using. Example 2 crosses four lines in a cache that holds two; on returning to 0x0, it misses again. Give the cache four lines and that final access hits.

Hits shorten the wait we saw in the load timeline; misses still pay to reach a slower level. A program that repeatedly uses a small collection of nearby data often gets many hits. Later lectures will also consider how fast a cache can deliver a stream of data, which is a bandwidth question rather than a single-load latency question.

Section 16

The hierarchy: a complex implementation of a simple abstraction

A real processor does not have just one tiny cache. A load first looks near the core in level 1 (L1). On a miss, the hardware checks a larger L2, then a larger L3, and finally main memory (DRAM) if necessary. The requested line travels back toward the core, where later accesses may hit. Caches hold copies, so the data need not live in only one level. The program still names one address and receives the value prescribed by its memory model.

Each farther level is larger and slower. The lecture's example machine has roughly 32 KB of L1, 256 KB of L2, 20 MB of L3, and 64 GB of DRAM. The shown latencies at 4 GHz—4, 12, 38, and about 248 cycles—are approximate total times to serve a load from each level, not times to add together. A DRAM load is roughly 60 times slower than an L1 hit in this model.

A program's working set is the data it keeps using over a stretch of execution. A small working set can fit near the core; a large or widely scattered one may send more loads to distant levels. The explorer estimates average load latency by counting what fraction of loads finish at each level. Read its sliders in sequence: “found in L1” applies to all loads; “of the rest, found in L2” applies only to L1 misses; and the L3 slider applies only to loads that missed both L1 and L2. For example, if 90% hit L1 and half of the remaining 10% hit L2, then 5% of all loads are served by L2.

Program:
Found in L1:
Of the rest, found in L2:
Of the rest, found in L3:

Estimate the average from mutually exclusive outcomes. Multiply the fraction of all loads served by each level by that level's total latency, then add the four contributions. The colored segments show those contributions. Even a small DRAM fraction can matter because its latency is much larger. This teaching model averages individual loads; actual program time also depends on overlapping requests and other work.
PredictAnswer before opening.
  1. A loop's loads hit L1 99% of the time and otherwise go to DRAM. What is the average load latency?
    Show answer

    0.99 × 4 + 0.01 × 248 = 3.96 + 2.48 = 6.44 cycles. One load in a hundred missing to DRAM increases the average by about 60%.

  2. Does a cache ever change what a correct program computes?
    Show answer

    No, for a correctly synchronized program. Caches are part of the implementation that must obey the machine's memory rules. They change access time, not the promised result. When several processors share data, hardware coherence and the language's synchronization rules determine what values each may observe; later lectures develop those details.

Summary

What to take away

  • Single-thread speed no longer rises at its old pace. Instruction dependencies limit wider execution, and power limits faster clocks. Additional cores, vector lanes, and specialized units offer other paths to performance.
  • Those paths need suitable work. A parallel program must divide the job, keep workers busy, and coordinate their results. Speedup and parallel efficiency reveal how well that plan scales.
  • Data supply can decide the result. The same program-visible address space may be served by a nearby cache or distant DRAM. Locality helps keep loads short; misses can stall dependent instructions. The course's 32–40× assignment result illustrates what one carefully chosen workload can gain by using its machine's parallel resources.
The lecture as a chain of cause and effect. Lecture 2 picks up from the middle box: how a modern multi-core processor is built, and what each of its forms of parallelism demands from a program.
Review

Terms to know, and questions to test yourself

Speedup
Time on 1 processor ÷ time on P processors. Ideal: P.
Parallel efficiency
Speedup ÷ P: the observed speedup as a fraction of ideal P-fold scaling.
Load imbalance
Some processors have more work than others; the rest sit idle waiting for them.
Communication
Moving data (such as partial results) between processors. It costs time, and can dominate a parallel computation.
Instruction
One simple operation for the processor to perform. A program is a list of them.
State
The information needed to continue a program, including register and memory values and the next instruction's address.
ILP
Instruction-level parallelism: opportunities to overlap independent instructions from one stream while preserving the intended result.
Superscalar
A processor that finds independent instructions in one stream automatically and executes several per clock.
Power wall
The constraint created when raising clock frequency, often with voltage, makes power and heat grow too steeply for continued easy clock-speed gains.
Specialization
Hardware designed around a particular workload, such as graphics or neural networks, to improve speed or energy use for that work.
Address space
Memory as an array of bytes, each identified by its address.
Latency
Time for the memory system to deliver requested data.
Stall
A delay while a needed instruction cannot advance because an input is not ready, often after a load.
Cache, cache line
On-chip storage holding copies of recently used memory, managed in fixed-size lines.
Spatial / temporal locality
Accessing addresses near recent ones / accessing the same addresses again soon. Both make caches effective.
Cold / capacity miss
A line's first requested access / a later miss after a previously held line was evicted for lack of space.
Test yourselfThe lecture's questions, plus a few more.
  1. Is a 2× speedup on a computer with 10 processors a good result?
    Show answer

    Its parallel efficiency is 2 / 10 = 20%, so it is faster than the one-processor baseline but far below ideal scaling. Whether it is useful depends on the goal and cost. The number alone does not say why the other 80% of ideal speedup was missed: workers might wait, communicate, contend for resources, or do extra work.

  2. In the lecture's one-cycle-per-instruction model, why does a = x*x + y*y + z*z need at least three clocks even with many execution units?
    Show answer

    The dependency chain. The final add needs the first add's result, which itself needs a multiply's result. Those three dependent operations must occupy successive clocks in this model, regardless of how many other instructions can run alongside them.

  3. Why did processor clock frequencies stop increasing around 2005, even though transistors kept getting smaller?
    Show answer

    Power and cooling became limiting. Dynamic power rises with switching frequency and with the square of supply voltage; pushing a given design to a higher clock often requires more voltage. Leakage adds another cost. More transistors therefore could not keep producing the same easy gains from higher clocks.

  4. Two loads followed by an add that uses both. The data is in DRAM (~248 cycles). Roughly how long does a processor that waits after every load take, compared with one that waits only when a value is used?
    Show answer

    Waiting after every load: 248 + 248 + 1 ≈ 497 cycles. Waiting only on use lets the loads overlap: 1 + 248 + 1 ≈ 250 cycles. Either way, the processor does 3 instructions' worth of work.

  5. In the 2-line cache, what sequence of addresses would miss on every access?
    Show answer

    Cycle through three or more lines, one byte each: 0x0, 0x4, 0x8, 0x0, 0x4, 0x8, … With LRU, each new line evicts exactly the line that's needed next. Try it with "your own".

Based on Stanford CS149 (Fall 2023), Lecture 1, "Why Parallelism? Why Efficiency?", by Kayvon Fatahalian and Kunle Olukotun. The explanations, diagrams and interactive models on this page are original study material. Chart data (CPU trends, superscalar speedups, power, latencies and machine specifications) are as cited in the lecture. Charts are redrawn or stylized where noted. For the course's own slides and videos, see the CS149 course site.