Exercises — CUDA cores and execution model
Parent note: CUDA cores and execution model. Prerequisites you may want open: 6.2.01-GPU-vs-CPU-architecture, 6.2.02-GPU-memory-hierarchy, 6.1.03-SIMD-vs-SIMT, 6.2.04-occupancy-and-performance.
Before we start, three tiny pieces of vocabulary the whole page leans on, built from zero:
Level 1 — Recognition
Exercise 1.1. How many threads are in one warp, and what does "in lockstep" mean for them?
Recall Solution 1.1
A warp contains 32 threads. "In lockstep" means all 32 execute the same instruction on the same cycle — like 32 dancers who are only ever allowed to make the same move at the same time. They differ only in the data each one holds in its own registers, never in which instruction they run at a given tick.
Exercise 1.2. True or false: a CUDA core is a full processor like a CPU core, with its own branch predictor and instruction decoder.
Recall Solution 1.2
False. A CUDA core is just an ALU (arithmetic logic unit) — a small calculator that does one FP32 or integer operation per cycle. It has no private decoder, branch predictor, or independent instruction stream. It shares all that control logic with the other 31 cores in its warp. That sharing is exactly why GPUs are cheap-per-ALU but bad at branchy code.
Exercise 1.3. Name the four levels of the architecture hierarchy from smallest to largest.
Recall Solution 1.3
- CUDA core (a scalar ALU, 1 op/cycle)
- Warp (32 threads in lockstep)
- Streaming Multiprocessor (SM) (holds many cores + schedulers + registers + shared memory)
- GPU chip (holds many SMs)
Level 2 — Application
Exercise 2.1. A grid is launched with blockDim.x = 256. For the thread in block blockIdx.x = 7 at position threadIdx.x = 40, compute its global thread ID.
Recall Solution 2.1
Use the indexing formula
What each piece means: blockIdx.x is which block (which platoon), blockDim.x is how many threads per block (platoon size), and threadIdx.x is your seat inside your own block. Multiplying block index by block size skips over all the earlier platoons; adding your seat lands you exactly.
Exercise 2.2. You have N = 1{,}000{,}000 elements and choose threadsPerBlock = 256. Using the standard ceiling launch formula, how many blocks do you launch?
Recall Solution 2.2
The formula rounds up (that is the floor bracket applied to a number nudged past the next whole value) so no element is left uncovered:
Why the + threadsPerBlock - 1? Plain integer division rounds down, which would drop the last, partly-full block. Adding one-less-than-a-block first forces a round-up whenever there is any remainder — this is exactly a ceiling in disguise.
That launches threads — the extra are switched off by the if (i < N) guard in the kernel.
Exercise 2.3. A block holds 256 threads. How many warps is that? What if the block holds 250 threads?
Recall Solution 2.3
Warps are minted in chunks of 32, so we round the thread count up with the ceiling bracket:
- warps exactly. Clean.
- warps. The 8th warp is only threads full; the remaining lanes are inactive but still occupy the warp slot. You pay for a whole warp either way.
Level 3 — Analysis
Exercise 3.1. An SM has 128 CUDA cores. A student claims "a warp of 32 threads therefore finishes in of a cycle." Diagnose the error and give the correct minimum latency of one warp instruction.
Recall Solution 3.1
The correct minimum is 1 cycle, not a quarter cycle.
Why the student is wrong: a warp instruction is atomic per issue — the smallest slice of time the scheduler deals in is one cycle. Having 128 cores does not chop that cycle into pieces.

What to observe in the figure: the grid shows all 128 cores of the SM on a single tick. The 32 magenta squares are the lanes running this one warp — they light up and finish together on that tick. The 96 pale squares are idle for this warp, but the orange label reminds you they are free to run a different warp on the same tick. So the picture makes the point visual: extra cores widen the SM sideways (more warps at once), they never make this one warp's single tick shorter.
The real benefit of extra cores is throughput, not lower latency: with 128 cores and multiple schedulers, the SM can issue several warp-instructions in the same cycle (from different warps), keeping all 128 cores busy — but each individual warp still takes at least one cycle.
Exercise 3.2. 32 threads in a warp each read a float (4 bytes). In case A they read A[0..31] (consecutive). In case B thread reads A[i*32] (stride 32). Compute the transaction efficiency for each, using 128-byte transactions.
Recall Solution 3.2
Efficiency is bytes you needed ÷ bytes hardware moved: Useful bytes in both cases .
Case A (coalesced): all 32 consecutive floats span exactly bytes = one aligned transaction. Case B (stride 32): consecutive threads land bytes apart, so each thread's float sits in its own 128-byte line → 32 separate transactions. Case B moves more bytes for the same useful data — that is the " slower" you were warned about.

What to observe in the figure: the top row (magenta bracket) is the coalesced case — the 32 requested floats sit side by side and the single magenta box shows they all fit in one 128-byte transaction, so no byte is wasted. The bottom row (orange) is the strided case — only every 8th cell is a float we actually need (orange cells), and each one is wrapped in its own transaction box, so the hardware drags 32 full lines to deliver the same 128 useful bytes. The eye sees instantly why the top wastes nothing and the bottom wastes almost everything.
Exercise 3.3. Registers per SM cap at . You want resident threads. What is the rough per-thread register ceiling? What happens to occupancy if your kernel actually uses 64 registers/thread?
Recall Solution 3.3
The ceiling is the total budget split evenly: If the kernel truly needs 64 registers/thread, you can only keep resident — half the threads. Fewer resident threads means fewer warps to hide latency behind, so occupancy drops. (This is the exact hinge into 6.2.04-occupancy-and-performance.)
Level 4 — Synthesis
Exercise 4.1. An SM has warp schedulers. Each resident warp does cycles of useful compute, then stalls cycles on a global-memory read. How many resident warps are needed to fully hide the latency? If the SM caps at 64 resident warps, is full hiding possible?
Recall Solution 4.1
Build the bookkeeping. During the stall cycles of one warp, the SM's schedulers can issue warp-instructions of other work. Each warp only supplies instructions before it too stalls, so the number of warps needed to keep the pipeline fed is The SM caps at 64 resident warps. Since , full latency hiding is impossible for this kernel on this SM. The ALUs will sit idle part of the time — the kernel is memory-bound.
Exercise 4.2. For the same SM (, , cap warps), what is the minimum compute intensity that makes full latency hiding achievable?
Recall Solution 4.2
Set and solve for : Since counts whole instructions, you need cycles of compute per stall. Design meaning: by raising compute per memory op — fusing work, reusing values from registers/shared memory (see 6.2.02-GPU-memory-hierarchy), or the tiling patterns in 7.3.01-parallel-programming-patterns — you shrink until 64 warps suffice.
Exercise 4.3. You must add two vectors of N = 1{,}000{,}000 floats. Choose threadsPerBlock and numBlocks, state how many warps per block, and explain why 256 is a safer default than 1000.
Recall Solution 4.3
Choose threadsPerBlock = 256 (a multiple of 32) so no warp is partly empty:
Blocks needed (ceiling, because a partial block still counts):
Why not 1000 threads/block? is not a multiple of 32: warps, and the last warp runs only active lanes out of 32 — 24 lanes wasted in every block. Also 1000 is close to the 1024 hard cap, leaving no headroom for register pressure. A round multiple of 32 (128, 256, 512) keeps warps full and occupancy tuning flexible.
Level 5 — Mastery
Exercise 5.1. A colleague argues: "If I switch from an SM with 32 cores to one with 128 cores, my single warp of 32 threads will run faster." Steel-man their reasoning, then refute it precisely, and state what does get better.
Recall Solution 5.1
Steel-man (the strongest honest version): more ALUs usually means more work per unit time, and , so it feels like a speedup is on the table.
Refutation: the is real but it applies to throughput, not to a single warp's latency. A warp instruction is atomic per issue — its floor is one cycle regardless of whether 32 or 128 cores exist. On the 32-core SM, one warp/cycle fully occupies the cores. On the 128-core SM, one warp still finishes in one cycle, but now up to 4 warps (via multiple schedulers) can be issued per cycle. So the 128-core SM completes up to more warps per cycle — but any one warp is exactly as fast as before.
Verdict: aggregate warp throughput scales ~; per-warp latency is unchanged. This is the difference between finishing one race faster (latency) and running four races side by side (throughput).
Exercise 5.2. A kernel achieves transaction efficiency on a GPU with peak DRAM bandwidth 1000 GB/s. (a) What effective bandwidth do you actually get? (b) A rewrite makes accesses fully coalesced (). By what factor does effective bandwidth improve, and does the compute per element change?
Recall Solution 5.2
(a) Effective bandwidth is efficiency times peak: (b) At : The compute per element is unchanged — coalescing only fixes how the bytes move, not how many arithmetic ops each thread does. You made the memory better; the ALUs do the exact same math. This is why memory-bound kernels can leap in speed from a pure access-pattern rewrite, no algorithm change. (Directly relevant to 9.1.02-neural-network-training-on-GPUs, where huge tensor reads dominate.)
Exercise 5.3. Prove the claim "raising compute intensity always lowers the warps required to hide a fixed latency," and identify the one degenerate case where the formula breaks.
Recall Solution 5.3
Start from Here (schedulers) and (stall cycles) are fixed positive numbers, and . As a function of , this is , which is strictly decreasing for : doubling halves . So more compute per memory op always reduces the warps needed.
Degenerate case: . If a warp does zero useful compute between stalls, then is undefined (division by zero) — physically, a warp that never computes can never help hide anything; you cannot cover latency with pure stalls no matter how many warps you launch. The formula assumes (at least one real instruction between memory ops).