Opening: when the CPU and GPU stop competing and start sharing

Lesson 7 and Lesson 8 described the memory hierarchy of a traditional PC: the CPU has its own RAM, the GPU has its own VRAM, and the two are joined by a PCIe bus. Every time the CPU needs the GPU to process data (rendering graphics, training an AI model), that data has to be COPIED from RAM into VRAM over PCIe — a step that costs time while computing nothing, merely "moving data from one place to another". Apple Silicon asked the reverse question: if the CPU and GPU sit on ONE piece of silicon, why not have them SHARE a single pool of RAM?


📚 Prerequisites
Worth reading Lesson 7 (caches) and Lesson 8 (virtual memory) — the UMA in this lesson is a DIFFERENT way of organising the very memory hierarchy already covered, not a separate concept.

1. System on a Chip (SoC) vs a traditional motherboard

A traditional PC mounts the CPU, GPU, RAM and drives as SEPARATE COMPONENTS on a motherboard, connected by buses (PCIe, SATA and so on). Apple Silicon (the M series) integrates the CPU, GPU, Neural Engine (NPU) and memory controller onto the SAME piece of silicon — a SoC (System on a Chip). The physical distance between blocks becomes extremely short, cutting latency and energy consumption substantially compared with signals travelling long board traces between separate chips.

soc_layout.txt (an SoC vs discrete components)
A traditional PC (separate components on a motherboard):
  [CPU chip] ---bus--- [discrete GPU chip] ---bus--- [RAM DIMM]
  Signals travel LONG board traces between separate chips

An Apple Silicon SoC (a single piece of silicon):
  +---------------------------------------------------+
  | [CPU: Firestorm x N] [CPU: Icestorm x M] [GPU]    |
  | [Neural Engine (NPU)] [UMA memory controller]     |
  +---------------------------------------------------+
  Physical distance between blocks is VERY SHORT
  -> lower latency and lower energy per transfer
⚠️ Pitfall: an SoC trades away upgradeability
Because the RAM and other components are SOLDERED onto the same package as the CPU/GPU, a user CANNOT upgrade the RAM or replace individual parts when they fail — quite unlike a traditional PC where RAM and the GPU sit in removable slots. This is a DELIBERATE trade-off between performance and power efficiency (SoC) on one side and repairability and upgradeability (discrete architecture) on the other — not an accidental technical limitation.

2. Big and little cores (big.LITTLE) & UMA

The Apple M1 has 2 kinds of CPU core: Firestorm (high performance, high power draw) and Icestorm (power efficient, lower performance) — the operating system AUTOMATICALLY pushes background work (syncing email, virus scanning) onto Icestorm to save battery, and heavy work (compiling code, rendering video) onto Firestorm. More importantly for this lesson: the CPU (both core types) and the GPU all access ONE shared pool of LPDDR5 RAM — that is UMA (Unified Memory Architecture).

uma_vs_traditional.txt (the two models side by side)
Traditional PC:
  [CPU] --own RAM--     [GPU] --own VRAM--
     |______________PCIe Gen4 x16 (~32 GB/s)_____________|
     Whenever the GPU needs data the CPU holds -> COPY it over PCIe

Apple Silicon (UMA):
  [CPU] ---+
           +--- ONE shared LPDDR5 pool (e.g. 400 GB/s on M1 Max) ---+
  [GPU] ---+                                                        |
     Both read and write it DIRECTLY - no copy step at all.
     But see Section 4: that pool is SHARED, not private to the GPU.
⚠️ Pitfall: mis-scheduling wastes battery
If the operating system's thread scheduler pushes a light background task onto a high-performance Firestorm core BY MISTAKE (instead of an efficient Icestorm one), battery is drained POINTLESSLY — the work still completes correctly, but at far more energy than needed. Getting big.LITTLE scheduling right is a continuous balance between "finish fast" and "finish cheaply", with no fixed formula for every task.

3. Working the bandwidth numbers: PCIe vs UMA

Rendering ONE 4K frame (3840×2160 pixels at 32-bit colour — 4 bytes per pixel for R/G/B/Alpha) needs exactly:

$$\text{Frame Bytes} = 3840 \times 2160 \times 4 = 33{,}177{,}600 \text{ bytes}$$

Verified for real: a 4K frame weighs exactly 33,177,600 bytes (31.64 MiB). Compare the time to move that data over 2 channels — PCIe Gen 4 x16 (about 32 GB/s in practice, requiring a COPY from RAM into VRAM) against UMA on an Apple M1 Max (400 GB/s of RAM bandwidth, with the GPU accessing it DIRECTLY, no copy):

bandwidth_compare.js (excerpt from the shared cpu-core.js engine)
function frameBytes(width, height, bytesPerPixel) {
  return width * height * bytesPerPixel;
}
function transferTimeSeconds(bytes, bandwidthGBps) {
  return bytes / (bandwidthGBps * 1e9);
}
// Verified: frameBytes(3840, 2160, 4) = 33.177.600 byte
// Verified: transferTimeSeconds(33177600, 32) * 1000  = 1,0368 ms  (PCIe Gen 4 x16)
// Verified: transferTimeSeconds(33177600, 400) * 1000 = 0,0829 ms  (UMA M1 Max)
// UMA beats PCIe by EXACTLY the bandwidth ratio: 400/32 = 12.5x

For this 4K frame, PCIe takes 1.0368 ms and UMA only 0.0829 ms — exactly 12.5 times faster, matching the bandwidth ratio PRECISELY ($400/32=12.5$), rather than being a vague estimate. At 60fps (a per-frame budget of $1000/60 \approx 16.67$ ms), PCIe consumes 6.22% of the budget JUST TO COPY data (before the GPU does any actual work), while UMA takes under 0.5%.

frame_budget_60fps.js (transfer time as a share of the frame budget)
const frameBudgetMs = 1000 / 60; // ~16.6667 ms per frame at 60fps
const pcieShare = (cmp.pcieTimeMs / frameBudgetMs) * 100; // ~6.22%
const umaShare = (cmp.umaTimeMs / frameBudgetMs) * 100;   // ~0.50%
// PCIe: nearly 1/16 of the frame budget spent JUST copying data
// UMA: negligible - the GPU keeps almost the whole budget for real COMPUTE
⚠️ Do not compare raw CPU/GPU clock speeds while ignoring memory bandwidth
The 12.5x gap above comes ENTIRELY from the memory bandwidth ratio (400 GB/s vs 32 GB/s) — it has NOTHING to do with the clock speed (GHz) of either the CPU or the GPU. A high-clocked GPU that is bottlenecked on data delivery (data-starved) still runs far below its theoretical potential — which is exactly why a "clock speed spec" alone is not enough to compare real performance between two different architectures.

4. The architectural price of UMA

The three sections above cover only the upside, and the one pitfall raised so far — the RAM not being upgradeable — is a PACKAGING trade-off, not an architectural one. But UMA has two costs that belong to its design itself, and both of them live inside the very 400 GB/s figure Section 3 just used.

4.1 Bandwidth is SHARED, not additive

That 400 GB/s in Section 3 is not "the GPU's bandwidth". It is the bandwidth of the WHOLE pool — the thing the CPU, the GPU and the Neural Engine all draw from, at the same time. When the CPU is working hard, what remains for the GPU shrinks by exactly what the CPU takes:

shared_bandwidth.js (same 4K frame, with the CPU taking bandwidth from the GPU)
const bytes = frameBytes(3840, 2160, 4);            // 33,177,600 bytes
const ms = (bw) => transferTimeSeconds(bytes, bw) * 1000;

// UMA: one pool, so whatever the CPU consumes is taken OFF the GPU's share.
// Verified by running it:
//   CPU idle       -> GPU has 400 GB/s -> 0.0829 ms  (the figure from Section 3)
//   CPU 100 GB/s   -> GPU has 300 GB/s -> 0.1106 ms  (1.33x slower)
//   CPU 200 GB/s   -> GPU has 200 GB/s -> 0.1659 ms  (2.00x slower)

// A DISCRETE PC has the opposite property: the two pools are separate, so their
// bandwidths ADD instead of competing. A GPU reading from its own 320 GB/s VRAM
// gets 0.1037 ms no matter how hard the CPU hammers its own RAM.

That is why Section 3's "32 GB/s vs 400 GB/s" comparison is correct but incomplete. It is correct in that PCIe must COPY while UMA need not — and that copy step genuinely disappears, so the benefit is real. It is incomplete in that 400 GB/s is a SHARED figure: under heavy simultaneous load the GPU's actual share is considerably lower, and the 2.00x slowdown on the last row above is a measurement, not a guess. A discrete architecture trades away having to copy in exchange for two independent bandwidth pools; UMA trades the other way round.

4.2 Sharing memory means keeping caches coherent

Section 2 said the CPU and GPU "read and write it DIRECTLY, with no copy step at all". That sentence skips a question: both of them have their own caches (Lesson 7). If the CPU writes a value and that value is still sitting in the CPU's cache rather than in RAM, the GPU reading the same address would see STALE data.

What prevents that is cache coherency: hardware tracks who currently holds a copy of which cache line, and when one side writes, the copies on the other side are invalidated. This is not an optional extra — it is the condition that makes "no copy needed" SAFE rather than merely fast. And it has a price: every write into a shared region can generate extra tracking traffic on chip, and at each CPU↔GPU handover point the software still has to place synchronisation markers to be certain the other side has seen the new data.

⚠️ Pitfall: "unified memory" does not mean "no synchronisation"
What disappears when moving to UMA is the copy step, NOT the synchronisation step. Beginners often read "zero-copy" as "just write and the other side will see it" — which is wrong. The program still has to state "I have finished writing" before the GPU reads, and in Apple's Metal that is still an explicit set of commands on a command buffer. Omitting the synchronisation markers produces the worst class of bug there is: correct on your machine, randomly wrong on someone else's, because it depends on exactly when a cache happened to be written back.

5. Hands-on: comparing PCIe against UMA bandwidth

Change the frame dimensions, the bytes per pixel, and the bandwidth of both channels to watch transfer time respond directly — the coloured bars below visualise the ratio:

🖥️ PCIe vs UMA bandwidth comparison
PCIe
UMA

Summary

  • ✅ UMA trades away the PCIe copy step in exchange for one shared RAM pool — but that pool's bandwidth is SHARED: verified, if the CPU is consuming 200 GB/s the same 4K frame takes 0.1659 ms instead of 0.0829 ms, exactly 2.00 times slower.
  • ✅ "Zero-copy" removes the COPY step, not the SYNCHRONISATION step — cache coherency is what makes the sharing safe, and the program must still place synchronisation markers between CPU and GPU.
  • ✅ An SoC integrates CPU/GPU/RAM onto one piece of silicon, cutting latency and energy — at the cost of losing upgradeability and replaceable parts.
  • ✅ big.LITTLE (Firestorm/Icestorm) balances performance against battery through dynamic scheduling.
  • ✅ UMA removes the CPU→GPU copy step of the traditional PC model entirely — CPU and GPU access the same RAM pool DIRECTLY.
  • ✅ Verified: a 4K frame is 33,177,600 bytes; PCIe Gen 4 (32GB/s) takes 1.0368 ms; UMA (400GB/s) takes 0.0829 ms — exactly 12.5 times faster, matching the bandwidth ratio.
  • ✅ Pitfall: the performance gap comes from memory BANDWIDTH, not CPU/GPU clock speed — comparing raw clocks ignores the factor that actually decides it.

Review quiz

Question 1

Which step does UMA (Unified Memory Architecture) eliminate from the graphics pipeline of a traditional PC?

Question 2

Verified: PCIe Gen 4 (32GB/s) takes 1.0368 ms to move a 4K frame while UMA (400GB/s) takes only 0.0829 ms — exactly 12.5 times faster. Where does that 12.5 come from?

Question 3

What does an SoC (System on a Chip), integrating CPU/GPU/RAM onto one piece of silicon, trade away relative to a traditional PC?

Question 4

Why is "mis-scheduling" (pushing background work onto a high-performance Firestorm core) a genuine pitfall of the big.LITTLE architecture?

Download the practice code for this lesson

The CPUJS JavaScript file — a mini computer-architecture library used across all 12 lessons. Lesson 9 adds frameBytes(), transferTimeSeconds() and compareTransferMethods() — the frame size model and the PCIe vs UMA transfer time comparison, with a self-test that checks every number quoted in this lesson (run node cpu-core.js; nothing to install):

Download cpu-core.js

📖 References

Related lessons in this series

Lesson 8: Virtual Memory & the TLB Lesson 10: Hardware Acceleration: GPU, NPU & AMX Back to the Computer Architecture roadmap

Comments