Unit 8 · AI infrastructure · Lesson 1 of 10
GPU architecture, memory hierarchy and arithmetic intensity
Step 1: Learn the ideas
- Streaming multiprocessors, thread blocks, warps and SIMT execution
- Latency hiding and occupancy (registers and shared memory per block)
- Tensor cores, supported precisions and shape alignment (concept level)
- Memory hierarchy: registers, shared memory/L1, L2, HBM, host memory
- Coalesced global-memory access
- Arithmetic intensity, the ridge point and the roofline model
- Compute-bound, memory-bound and overhead-bound operations
- Kernel launch overhead, asynchronous execution and CUDA graphs
- Operator fusion and kernel compilers (torch.compile, Triton) at awareness level
- Profiling with the PyTorch profiler, Nsight Systems and Nsight Compute
Why it matters
Most AI-infrastructure questions reduce to one diagnosis: is the GPU waiting on arithmetic, on memory, or on the host? Interviewers listen for whether you make that diagnosis before proposing a fix. Suggesting a faster GPU for a bandwidth-bound decode step, or kernel fusion for a step dominated by data loading, shows the model is missing. The same reasoning returns in training memory, FlashAttention, KV caches and quantization.
Core ideas
Execution model. A GPU contains many streaming multiprocessors (SMs). A kernel launch creates a grid of thread blocks; each block runs on one SM, and its threads execute in groups of 32 called warps that issue one instruction at a time for all their threads. When a warp waits on memory, the SM switches to another resident warp almost for free, so GPUs hide latency with parallelism rather than large per-thread caches. If few warps fit on an SM (many registers per thread, much shared memory per block), occupancy drops and latency shows. Enough parallelism to cover latency is the goal, not maximum occupancy.
Tensor cores run small matrix multiply-accumulate operations far faster than the general-purpose cores, but only for supported formats (16-bit floats and, on newer parts, TF32, int8 or fp8) and well-aligned shapes. Whether a plain fp32 matmul uses TF32 is a framework setting: check it.
Memory hierarchy. From fastest and smallest: registers (per thread), shared memory and L1 (per SM; the FlashAttention paper calls it SRAM), L2 (shared by all SMs), HBM (device memory, tens of gigabytes), and host memory over PCIe or NVLink. Each step down costs a large factor in bandwidth or latency. Global loads are efficient only when a warp's threads touch contiguous addresses (coalescing).
Arithmetic intensity and the roofline. Intensity I is FLOPs performed divided by bytes moved to or from HBM. With peak compute P and bandwidth B, attainable throughput is at most min(P, I × B). The ridge point P/B separates memory-bound from compute-bound work; compute it from your GPU's datasheet for the precision you use. Two reference cases in 16-bit:
- Elementwise op on N values: about 2N bytes read, 2N written, a few FLOPs per element, so I is around one FLOP per byte or less. It is memory-bound everywhere; its time is roughly bytes divided by bandwidth.
- Matmul (M×K)·(K×N): 2MNK FLOPs over at least 2(MK + KN + MN) bytes. For square n×n matrices I ≈ n/3, so large matmuls are compute-bound. A matrix-vector product has I ≈ 1 because each weight is read once and used once; batching b vectors raises I to roughly b. This is why small-batch LLM decoding is bandwidth-bound.
Overhead. Every kernel launch has a fixed CPU and driver cost that does not shrink with tensor size. With small tensors the GPU finishes before the host enqueues the next kernel, leaving gaps on the timeline. Remedies: larger batches, fusion (torch.compile, Triton or library kernels), CUDA graphs that replay a recorded launch sequence, and removing host synchronization (.item(), printing tensors, data-dependent shapes) from the hot loop.
Asynchrony. CUDA work is queued on streams and Python returns immediately, so a wall clock without synchronization measures enqueue time. Use CUDA events or torch.utils.benchmark, warm up, and synchronize before reading the clock.
Profilers. The PyTorch profiler gives per-operator time, memory and an exportable timeline. Nsight Systems puts CPU threads, CUDA API calls, kernels, copies and communication on one timeline ("where did the time go?"). Nsight Compute dissects one kernel: achieved bandwidth and FLOP/s, occupancy, stall reasons ("why is this kernel slow?").
Common misconceptions
- "nvidia-smi shows 100% utilization, so the GPU is saturated." That metric is the fraction of time at least one kernel was running; a tiny kernel running continuously reports 100%. Measure achieved FLOP/s or bandwidth instead.
- "Fewer FLOPs means faster." Only for compute-bound work. Memory-bound operations speed up when bytes moved go down, even if FLOPs go up; FlashAttention's recomputation is the standard example.
- "GPUs are fast because of big caches." Per-thread cache capacity is small; GPUs depend on many independent warps and coalesced access.
- "I timed it with time.time(), so the kernel takes 2 ms." Without warm-up and synchronization you measured enqueue time or one-time compilation and allocation.
- "Tensor cores are used for every matmul." They need supported dtypes and aligned dimensions; badly shaped matrices waste part of every tile.
How to approach it
Build a two-number habit: for any operation write down FLOPs and bytes, compute intensity, compare with the device's ridge point, then confirm with a measurement. At graduate level, expect to explain compute- versus memory-bound with matmul and elementwise examples. At mid level, expect to read a trace and rank fixes by payoff. At senior level, expect to set utilization targets for a whole system and judge when custom kernels are worth their maintenance cost.
Suggested order
- 1Read the NVIDIA GPU Performance Background User’s Guide (architecture fundamentals, execution model, understanding performance, operation categories) and copy its definition of arithmetic intensity into your notes.
- 2Read "Making Deep Learning Go Brrrr From First Principles" and classify ten operations from a model you know as compute-, memory- or overhead-bound.
- 3Work through "All About Rooflines" in How to Scale Your Model, redoing its calculations with the datasheet numbers of a GPU you can access.
- 4Skim CUDA Programming Guide section 1.2 (Programming Model) for thread blocks, warps and the memory spaces.
- 5Follow the PyTorch Profiler recipe on a small model, export a trace and label every gap and copy on the GPU timeline.
- 6Do the roofline benchmark project: matmuls of growing size plus an elementwise operation, timed correctly and plotted against your predicted roofline.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
NVIDIA Deep Learning Performance guides (GPU background, matrix multiplication, mixed precision)Start here
NVIDIA DocumentationSection: GPU Performance Background User’s Guide
Vocabulary for SMs, tensor cores and the execution model, and the official treatment of arithmetic intensity and math- versus memory-limited operations.
Open(opens in a new tab)Link responded, 5 Oct 2026Making Deep Learning Go Brrrr From First PrinciplesStart here
Horace He's websiteIntuition for the compute, memory-bandwidth and overhead regimes, operator fusion and why small operations leave GPUs idle.
Open(opens in a new tab)Link responded, 5 Oct 2026How to Scale Your Model: A Systems View of LLMs on TPUs
Google DeepMind (online book)Section: All About Rooflines
Worked roofline calculations and the habit of estimating before measuring.
Open(opens in a new tab)Link responded, 5 Oct 2026CUDA Programming Guide
NVIDIA DocumentationSection: 1.2. Programming Model
Precise definitions of grids, thread blocks, warps and memory spaces when you need exact terminology.
Open(opens in a new tab)Link responded, 5 Oct 2026PyTorch Profiler recipe
PyTorch tutorialsCollecting operator-level profiles and timelines to attribute time inside a training or inference step.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
Start with the roofline benchmark: predict achieved throughput for each shape before running it, then explain every mismatch. Next, profile one real training step and label the GPU timeline. The profiling and benchmarking part of the CS336 systems assignment trains the same skill under realistic conditions. Opening a link is not practice; a written prediction-versus-measurement table is.
- Roofline benchmark: matmuls and elementwise operations on your GPUOpen(opens in a new tab)Not attemptedSelf-directed (PyTorch benchmark utilities)
- Profile a training step and attribute its timeOpen(opens in a new tab)Not attemptedSelf-directed (PyTorch profiler)
- Stanford CS336 assignments (tokenizer and transformer, systems, scaling, data, alignment)Open(opens in a new tab)Not attemptedStanford CS336
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1What is a warp waiting on when it stalls, and how does the SM hide the stall?
- Question 2What limits occupancy, and why is maximum occupancy not always the goal?
- Question 3What are the FLOPs, minimum bytes and arithmetic intensity of a 4096×4096 by 4096×4096 bf16 matmul?
- Question 4Why does nvidia-smi utilization not measure efficiency?
- Question 5Name three causes of idle gaps between kernels and how each appears in a trace.
- Question 6When does fusing two elementwise kernels help, and when does it not?
You’re done when you can
- Compute FLOPs, bytes and arithmetic intensity for a bf16 matmul and an elementwise activation of given shapes, and state which is memory-bound on a GPU whose datasheet you consulted.
- Benchmark a matmul and an elementwise operation with warm-up and synchronization and report achieved FLOP/s and GB/s next to your predictions.
- Label the main kernels, copies and gaps in a profiler timeline of a training step and name the dominant bound.
- Explain in under two minutes why decoding with batch size 1 is bandwidth-bound and how batching changes that.
Finish the lesson
Unlocks next: Training memory accounting, Input pipelines, host-device transfer and overlap, Prefill, decode and KV-cache sizing, FlashAttention and IO-aware exact attention


