ARM announced its Scalable Vector Extension (SVE) in 2016, and RISC-V ratified its "V" vector extension in 2021. Both are vector ISAs in Cray's sense: the length is not in the opcode, and they have mask registers and gather/scatter. A RISC-V loop opens every strip with vsetvli: it asks for the number of elements still to do and gets back how many this hardware will take, at most its maximum vector length; SVE gets the same effect with a mask that switches off the lanes past the end. One binary runs on every implementation, from 128-bit to 2048-bit registers. After twenty years in the opcode, the vector length moved back out of it.
SIMD today: packed extensions, GPUs, tensor cores and NPUs
After this lesson you will explain Table 30 row by row, describe a GPU's streaming multiprocessor as a multithreaded vector processor with a memory of its own, and say what a tensor core multiplies and what a TPU keeps stationary.
In 2015 Google started running a chip of its own design in its datacenters. It held 65,536 multipliers and none of the machinery this course has built since lesson 4: no caches, no branch prediction, no out-of-order execution, no multithreading. Its control logic took under 2 % of the die. On neural-network inference it was 15 to 30 times faster than the CPUs and GPUs of its day, and 30 to 80 times better in operations per watt.
How can a machine get faster by throwing away the hardware you spent a semester learning? Keep the question; the last section answers it. The way there runs through three machines you use every day — the SIMD unit of your laptop's CPU, the GPU, and the tensor cores inside the GPU — and each of them is Cray's vector idea of lessons 30–31, rebuilt for a different world.
Packed SIMD: the vector length written into the opcode
Start with the loop of the exam's strip-mining quiz, for (i=0; i<260; i++) Y[i] = X[i] + Y[i]; (lesson 21 takes its wrong options apart), and compile it not for VMIPS but for the CPU in your laptop.
From the mid-1990s the general-purpose CPUs (x86, ARM, PowerPC) added SIMD instruction sets (p. 414): Intel MMX (1996), then SSE, AVX and AVX-512; ARM NEON. A register of 128, 256 or 512 bits is treated as a short row of numbers packed side by side, and one instruction applies the same operation to all of them: 4 doubles in a 256-bit AVX register, 8 in a 512-bit AVX-512 register. The notes call these "SIMD ISA extensions", Hennessy & Patterson "multimedia SIMD extensions"; this lesson says packed SIMD.
For AVX, 260 = 65 × 4: the compiler emits a loop of 65 trips, one 4-wide add each. For AVX-512, 260 = 32 × 8 + 4: 32 trips of the 8-wide loop, and then four elements that no 8-wide add can take. They need a second loop, the scalar remainder loop:
// AVX-512: 8 doubles in every add
// main loop, 32 trips:
for (i = 0; i + 8 <= n; i += 8)
Y[i:i+8] = X[i:i+8] + Y[i:i+8];
// remainder loop, 4 scalar trips:
for (; i < n; i++)
Y[i] = X[i] + Y[i];
On a vector machine the same loop is the strip-mined loop of lesson 31. With MVL = 64, 260 = 4 × 64 + 4: five strips, each the same four vector instructions with VLR set to the strip's length, and no second loop:
// vector ISA: the length is in VLR
// strips: VLR = 64, 64, 64, 64, 4
for (i = 0; i < n; i += VLR) {
VLR = min(n - i, MVL);
// L.V, L.V, ADDVV.D, S.V:
Y[i:i+VLR] = X[i:i+VLR]
+ Y[i:i+VLR];
}
The difference fits in one sentence, and Table 30 of the notes compares the two machines row by row: in packed SIMD the vector length is part of the opcode, in a vector ISA it is the content of a register.
| Aspect | True vector machines (Cray, VMIPS) | SIMD ISA extensions (SSE/AVX) |
|---|---|---|
| Vector length | arbitrary, controlled by VLR, up to MVL | fixed by the register width (e.g. 4, 8, 16 elements) |
| Scalability | the same code runs efficiently if MVL grows (e.g. 64 → 128 elements) | code must be recompiled or rewritten when the width changes (SSE 128-bit vs AVX 256-bit) |
| Memory access | rich modes: unit stride, non-unit stride (LVWS), gather/scatter | mostly contiguous aligned loads/stores; gather only from AVX2, scatter only from AVX-512, both slower |
| Masking / predication | vector mask registers | only with the k-mask registers of AVX-512; older SIMD had no clean predication |
| Instruction count | one vector instruction covers VL operations, whatever the MVL | one instruction covers only what fits in the register; long vectors need many |
| Loop handling | strip mining + VLR: the same loop handles any size | leftovers need a scalar remainder loop or a separate vector clean-up |
| Philosophy | an architecturally first-class vector model | a scalar ISA "extended" with packed data operations |
Three rows — vector length, instruction count, loop handling — are one fact seen three times. The register width fixes the length, so one instruction covers only what fits in the register, and whatever does not fit at the end goes to the remainder loop. For a loop of \(n\) elements on a width of \(W\):
- \(I\)
- add instructions executed for the loop
- \(\lfloor n/W \rfloor\)
- trips of the SIMD loop, one \(W\)-wide add each
- \(n \bmod W\)
- leftovers, one scalar add each in the remainder loop
A vector instruction covers VLR elements whatever MVL is, and the last strip simply sets a shorter VLR: \(\lceil n/\mathrm{MVL} \rceil\) instructions per operation, no second loop.
The width in the opcode also freezes the binary. A program compiled for AVX keeps issuing 4-wide adds on a core with 512-bit hardware: half the lanes stay idle until someone recompiles it. A program compiled for AVX-512 does not run at all on a core without AVX-512. A vector binary runs on any number of lanes — the transparent scaling of lesson 31 — and gets longer strips when MVL grows, provided its loop takes MVL from the hardware instead of writing 64 into the code.
Why can't a wider core simply widen an SSE instruction?
Because the width is part of what the instruction means — the register names say it: xmm is 128 bits, ymm 256, zmm 512 — and the program relies on exactly that many doubles per register. In a VLR machine the width lives in a register, so the same ADDVV.D fills whatever MVL the hardware has.
The two rows left, memory access and masking, are about what packed SIMD lacked at birth. It loaded only contiguous, aligned blocks. Strided access, gather/scatter and per-element masks — all in vector ISAs since the Cray era — arrived generations later, if at all: gather with AVX2 (strided loads only as gathers), scatter and the k-mask registers with AVX-512. The notes misdate gather:
"AVX-512 is a vector processor." It is packed SIMD: a fixed 512-bit width in the opcode and no vector-length register. It borrowed masks and gather/scatter from vector machines, not the length register: a masked add can take the leftovers, but the compiler must build that mask for the tail, and a wider register still means new opcodes and a recompiled binary.
What packed SIMD buys, and what it costs
Why did the CPU makers take this road anyway? The notes list three gains (p. 415): compatibility with scalar CPUs — "no need for separate vector hardware, just extra datapaths in the same ALU pipeline"; widespread adoption, so compilers and libraries target it; and a good fit for multimedia and data-parallel kernels: audio, video, ML inner loops. Hennessy & Patterson add the hardware side: short vectors add little state to save on a context switch, they are fed by the caches instead of the banked memory a vector unit needs, and a 32- or 64-byte access touches at most two pages, where one 64-element vector load can touch many.
The notes' list of losses (p. 415) mirrors the other column of Table 30: no performance portability to wider registers, compilers that must generate remainder loops, clumsy strided and sparse access. The fourth bullet carries the wrong label:
Packed SIMD writes the vector length into the opcode; a vector ISA keeps it in a register (VLR). Remainder loops and recompilation for every new width both follow from that one choice.
A GPU is a vector processor that juggles threads
"The notes skip GPUs, so they are not examined." Chapter 9 of the notes is one paragraph and a QR code to another course's notes (p. 416). But the professor's exam-topic list names "Intro to GP-GPUs; Intro to Tensor Cores" and "Intro to Neural Processing Units", and the June 2025 paper asked a true/false on the GPU programming model. From here to the end of the lesson the content is background from Hennessy & Patterson (ch. 4 and ch. 7) and the vendors' documents — not in the notes, and not quoted from the slides, which are not available.
Here is DAXPY, Y = a·X + Y, written for an NVIDIA GPU in CUDA. There is no loop and no vector instruction: you write the body for one element and launch as many copies of it as there are elements.
__global__
void daxpy(int n, double a,
double *x, double *y)
{
// which element am I?
int i = blockIdx.x * blockDim.x
+ threadIdx.x;
if (i < n) // the tail idles
y[i] = a * x[i] + y[i];
}
// host: n threads, 256 per block
daxpy<<<(n + 255) / 256, 256>>>
(n, a, d_x, d_y);
Each copy is a CUDA thread with its own index i and its own registers. The launch line creates a grid of blocks of 256 threads, and the hardware cuts every block into groups of 32 consecutive threads, the warps. A warp is the real unit of execution: one instruction is fetched for it, and its 32 threads execute that instruction in lockstep on the SM's SIMD lanes, each on its own registers. In the words of lessons 30–31, a warp instruction is a vector instruction with VL = 32: on 16 lanes it takes 32 / 16 = 2 cycles. NVIDIA calls the style SIMT: you program threads, the machine runs vectors.
The hardware that runs the warps is NVIDIA's streaming multiprocessor (SM); Hennessy & Patterson call it a multithreaded SIMD processor. Figure 30 of the notes (p. 392) draws it next to a vector processor, and the widget turns one into the other.
The tail and the if: masks instead of VLR
A GPU has no vector-length register. The kernel's if (i < n) does its job: in the last block, the threads with i ≥ n are switched off by the hardware's per-thread mask and their lanes idle — the masked tail of the first widget. The same mechanism handles any if. When the threads of a warp disagree, the warp runs both paths, each under the mask of its own threads: the vector mask register of lesson 31, set by the hardware. This is branch divergence, and it costs what masking costs. A warp split 8 against 24 across an if-else issues both bodies with part of its lanes idle; a warp whose 32 threads take 32 different paths runs at 1/32 of its speed.
Memory: coalescing finds the stride at run time
VMIPS knows the access pattern from the instruction: L.V means unit stride. A GPU receives 32 independent addresses, one per thread. The address coalescing unit compares them and merges them into as few wide transactions as it can: 32 threads reading 32 consecutive doubles cost a couple of transactions, 32 threads walking down a column of a row-major matrix cost 32. It is the bad-stride story of lesson 31 again. The cure is a traversal order or a data layout in which consecutive threads touch consecutive words; padding the rows, the cure for bank conflicts, does not merge a column's addresses.
Latency: switch warps instead of waiting
A load that misses goes to DRAM and takes hundreds of cycles (the memory wall of lesson 22). A vector machine pays that latency once per 64-element vector and streams behind it. A GPU has short vectors, 32 elements, and no out-of-order engine. Instead, every SM keeps dozens of warps resident, and its warp scheduler picks, every cycle, one whose operands are ready — the fine-grained multithreading of lesson 29. The switch costs nothing because the registers of every resident warp are already in the register file: that is what the 1024 registers per lane of Figure 30 are for.
Before: one warp
After: four warps
How many warps are enough? Each warp offers a run of \(r\) instructions before its next stall, and the other warps must fill the \(t_{wait}\) cycles of its wait:
- \(t_{wait}\)
- cycles a warp waits for memory after its run
- \(r\)
- instructions a warp issues between two stalls
In Fig. 1, 1 + 6/2 = 4 warps are exactly enough. With runs of 10 instructions and a 400-cycle DRAM access you need 41. That is why an SM is built to hold dozens of warps at once, and why a single GPU thread is slow while the GPU is fast.
| NVIDIA / CUDA term | vector-machine equivalent | what it is |
|---|---|---|
| CUDA thread | one element, one iteration of the loop | has its own registers; runs in one lane |
| warp (32 threads) | a vector instruction stream with VL = 32 (H&P: a thread of SIMD instructions) | the unit the scheduler issues |
| thread block | one strip of a strip-mined loop | runs on one SM; its threads share memory and can wait at a barrier (__syncthreads()); blocks are independent, so a kernel scales with the number of SMs |
| grid | the whole vectorisable loop | everything one kernel launch computes |
| streaming multiprocessor (SM) | a multithreaded vector processor (H&P: multithreaded SIMD processor) | lanes, register file, warp schedulers |
| warp scheduler | vector issue logic + multithreading | picks a ready warp every cycle |
| lane ("CUDA core") | vector lane | executes one element |
| global (device) memory | main memory | the GPU's own off-chip DRAM |
| shared memory | no VMIPS counterpart: a memory managed by software | an on-chip scratchpad per SM, banked like vector memory |
A streaming multiprocessor is a multithreaded SIMD processor: a warp is one vector instruction stream of length 32 with per-lane registers; masks replace the VLR, coalescing replaces the strided load, and switching warps replaces long vectors as the way to hide memory latency.
Host and device: two memories, explicit copies
The kernel is half of a CUDA program. The other half runs on the CPU, the host, and its main job is moving data, because the GPU, the device, has its own DRAM (GDDR or HBM) on its own board, linked to the host by PCIe or NVLink. DAXPY on the host side:
size_t bytes = n * sizeof(double);
double *d_x, *d_y;
// 1. reserve device memory
cudaMalloc(&d_x, bytes);
cudaMalloc(&d_y, bytes);
// 2. copy the inputs in
cudaMemcpy(d_x, x, bytes,
cudaMemcpyHostToDevice);
cudaMemcpy(d_y, y, bytes,
cudaMemcpyHostToDevice);
// 3. run the kernel on the device
daxpy<<<(n + 255) / 256, 256>>>
(n, a, d_x, d_y);
// 4. copy the result out
cudaMemcpy(y, d_y, bytes,
cudaMemcpyDeviceToHost);
The pointers d_x and d_y hold addresses in the device's memory. In the basic model the host reaches that memory only through cudaMemcpy, and the kernel cannot see x and y in host memory. That is the quiz of the June 2025 paper (Quizzes, Question 3, p. 11): "In the GP-GPU programming model, the CPU (host) and GPU (device) have separate memory address spaces." True. Later CUDA versions added unified memory, which migrates pages between the two memories automatically, but it is a convenience laid on top: the basic model, and the hardware, keep two memories, and every byte crosses the link.
The split has a price: the copies cost time, and the GPU's own bandwidth serves only data that is already on the device, so a kernel must do enough work per byte copied to pay for the trip.
In the GP-GPU programming model the host and the device have separate memory address spaces: the program allocates device memory, copies the data in, launches the kernel and copies the results back.
Tensor cores: make the operand a matrix
Multiply two 4 × 4 matrices and add a third: D = A × B + C. Each entry of D is a row of A times a column of B plus one entry of C — four multiply-adds, FMAs. Sixteen entries make 64 FMAs.
On ordinary lanes those 64 FMAs are 64 lane operations, each reading its own a and b from the register file: 128 operand reads. A tensor core, a functional unit NVIDIA added to every SM with the Volta generation (2017), takes the three 4 × 4 matrices and returns D as one operation: 64 FMAs per clock, A and B in FP16, C and D in FP16 or FP32. It reads the 32 values of A and B once and uses each of them 4 times inside the unit. A Tesla V100 has 8 tensor cores per SM, 640 in all, for up to 125 tensor TFLOPS (NVIDIA's figure). Software sees a warp-level instruction: the 32 threads of a warp hold 16 × 16 tiles of the matrices in their registers and issue the multiply together.
Why does it pay? For three reasons you have met before. One operation of the unit does 64 FMAs: the vector idea, in two dimensions. Each value read is used 4 times, so the register-file traffic per FMA drops by four. And FP16 halves the bytes per operand. The accumulation stays in FP32 because a product of two FP16 numbers loses little, while a sum of thousands of them would lose precision or overflow: accumulate wide, store narrow.
The cost is that a tensor core does nothing for work that is not a matrix product. DAXPY gains nothing, and a model from Hennessy & Patterson shows why.
The roofline: when is a faster unit useless?
The roofline model (H&P, ch. 4) caps the flop rate a kernel can reach by two ceilings: the peak of the arithmetic units, and the memory bandwidth times the kernel's arithmetic intensity — the flops it performs per byte it moves from DRAM:
- \(P_{peak}\)
- peak flop/s of the arithmetic units (the flat roof)
- \(BW\)
- DRAM bandwidth in bytes/s (the slope)
- \(AI\)
- arithmetic intensity: flops per byte moved from DRAM
DAXPY does 2 flops per element, a multiply and an add, and moves 24 bytes: X[i] and Y[i] loaded, Y[i] stored, 8 bytes each. A product of two \(n \times n\) matrices does \(2n^3\) flops on \(3n^2\) doubles, read and written once each:
DAXPY stays at 1/12 flop per byte whatever \(n\) is; the matrix product grows with \(n\), to 85 for \(n = 1024\) (four times more in FP16, the tensor cores' format). The slope meets a roof at its ridge point. A kernel left of the ridge, like DAXPY, is memory-bound and cannot use a higher roof; only a kernel to its right, like the large matrix product, can.
A tensor core turns the operand from a vector into a matrix tile: 64 FMAs per operation, each input used 4 times, in reduced precision. It raises the compute roof, so only kernels of high arithmetic intensity — matrix products — gain.
NPUs: the whole chip becomes the matrix unit
A neural processing unit (NPU) is a domain-specific accelerator for neural networks: a large array of low-precision multiply-accumulate units (MACs) fed from on-chip memory, with none of the general-purpose machinery around it. The reference design is Google's first TPU (Jouppi et al., ISCA 2017): a 256 × 256 array of 8-bit MACs at 700 MHz, 28 MiB of on-chip memory managed by software, and about a dozen CISC-style instructions (Read_Weights, MatrixMultiply, Activate …) sent by the host over PCIe. Each MAC counts as two operations, so the peak is 92 TOPS, tera-operations per second:
The array is systolic: data moves between neighbouring cells in lockstep, pumped like a heartbeat, so every value fetched from memory is used again and again on its way across. The TPU's dataflow is weight-stationary: the weights are loaded into the cells and stay there; the activations enter from the left, each row one cycle after the row above, and move right; the partial sums move down and leave at the bottom into the accumulators. The widget runs it on a 3 × 3 array.
The systolic array is to a matrix what the vector pipeline of lesson 30 is to a vector: the latency of the wavefront is paid once, then results stream out, one per column per cycle.
Hennessy & Patterson's five guidelines
Hennessy & Patterson (6th ed., ch. 7) distil such designs into five guidelines for domain-specific architectures. The TPU follows each of them:
- Dedicated memories to minimise data movement — a 24 MiB unified buffer for activations and 4 MiB of accumulators, both managed by software; no caches.
- Spend the resources saved on advanced microarchitecture on more arithmetic units or bigger memories — 65,536 MACs, and control logic under 2 % of the die.
- The easiest form of parallelism that matches the domain — SIMD in two dimensions, a systolic array; no MIMD.
- Reduce data size and type to the simplest the domain needs — 8-bit integers. Quantising Google's Inception network shrank it from 91 MB to 23 MB.
- A domain-specific programming language — models written in TensorFlow, compiled for the TPU.
The NPUs of phones and laptops (Apple's Neural Engine, Qualcomm's Hexagon, the NPU blocks of recent Intel and AMD chips) apply the same recipe at a few watts: 8-bit or FP16 MAC arrays and large local SRAM.
Back to the puzzle
The TPU of the hook beat the CPUs and GPUs of its day without caches, branch prediction, out-of-order execution or multithreading. That machinery exists for programs that hide their future: branches whose direction is unknown until they execute, loads whose latency depends on a cache, independent instructions that the hardware must discover inside a window of a few hundred (lesson 8). A neural-network layer announces everything in advance — a matrix product of known size, a fixed dataflow, no data-dependent branches. Hardware that rediscovers what the program already states wastes silicon and energy; the TPU spends them on multipliers and on-chip memory instead. It is the choice Cray made in lesson 30, taken as far as it goes.
Every machine of this lesson is a step on the same road. Packed SIMD writes the parallelism into the opcode, a GPU into thousands of threads, a tensor core into a matrix tile, and an NPU into the whole chip.
An NPU is the vector idea pushed to two dimensions and to one domain: a systolic array that reuses every operand hundreds of times, fed from software-managed on-chip memory, in low precision, with none of the hardware that discovers parallelism at run time.
Exam-style questions
Explain the main concepts and benefits of each one of the following techniques used by data-parallel architectures:
- SIMD ISA extensions (e.g. SSE/AVX)
- Vector length register (VLR) with strip mining
- Vector mask registers
- Multithreaded SIMD execution in GP-GPUs (warps)
- Separate host and device memories in the GP-GPU programming model
- Address coalescing in GP-GPUs
- Tensor cores
- Systolic arrays in Neural Processing Units
Show model answer
A scalar ISA extended with instructions that apply one operation to the elements packed in a wide register (128, 256 or 512 bits: 2, 4 or 8 doubles). Benefits: several data operations per fetched and decoded instruction; cheap to add as extra datapaths in the existing pipeline; little extra state, fed by the caches; available in every CPU, so compilers and libraries use it for media and ML kernels. Limits: the width is fixed in the opcode, so a wider register needs new instructions and recompiled code, leftovers need a scalar remainder loop, and strided/sparse access and masks came late (gather with AVX2, scatter and masks with AVX-512).
VLR holds the number of elements, 0 ≤ VLR ≤ MVL, that every vector instruction processes; MVL is the hardware length of a vector register. Strip mining (a compiler transformation) splits a loop of n iterations into ⌈n/MVL⌉ strips: full strips with VLR = MVL and one strip of n mod MVL. Benefits: one loop handles any n with no remainder loop, nothing beyond the arrays is touched, and the same binary uses more lanes or a longer MVL if the loop takes its strip length from the hardware (ARM SVE, RISC-V V).
A mask register holds one bit per element; a vector compare sets it, and the following vector instructions update only the elements whose bit is 1, leaving the others unchanged (predication). Benefits: loops with if statements stay vectorised, with no branch per element, and masked elements raise no exceptions. Cost: masked elements still use their slot, so an if-else executes both bodies. GPUs do the same per thread (branch divergence).
A GP-GPU streaming multiprocessor is a multithreaded SIMD processor. Threads are grouped into warps of 32; one instruction is fetched per warp and executed in lockstep on the SIMD lanes for all 32 threads, each thread with private registers (SIMT). Many warps are resident; every cycle the warp scheduler issues from a ready one, and since all their registers stay in a large register file, switching costs nothing. Benefits: DRAM latency of hundreds of cycles is hidden by the other warps instead of by caches or out-of-order logic; the programmer writes sequential code for one element; the same kernel scales over any number of SMs.
The CPU (host) and the GPU (device) have separate memory address spaces, physically two DRAMs linked by PCIe/NVLink. The host allocates device memory (cudaMalloc), copies the inputs (cudaMemcpy), launches the kernel and copies the results back. Benefit: the device memory (GDDR/HBM) is built for bandwidth and serves the kernel at full speed. Cost: every transfer crosses the link, so a kernel must do enough work per byte copied; unified memory only automates the migration.
Each thread of a warp computes its own address; the coalescing unit merges the 32 addresses of a warp's load or store into as few wide memory transactions as possible. Consecutive threads touching consecutive words cost one or two transactions — the run-time equivalent of a unit-stride L.V — while column or scattered accesses cost up to 32. Benefit: memory bandwidth is used efficiently without the program stating the stride; it rewards unit-stride data layouts.
A tensor core is a functional unit in each SM that computes D = A × B + C on small matrices as one operation (Volta: 4 × 4, A and B in FP16, accumulation in FP16 or FP32, 64 FMAs per clock), used through a warp-level matrix instruction. Benefits: one instruction for 64 FMAs; each input reused 4 times inside the unit, so less register traffic per flop; reduced precision halves data movement — up to 125 TFLOPS on a V100. Only matrix-shaped, high-intensity work (dense linear algebra, deep learning) gains.
A systolic array is a grid of multiply-accumulate cells in which data moves between neighbours in lockstep, so each operand fetched from memory is used by a whole row or column. In the TPU (256 × 256 8-bit MACs, weight-stationary) the weights stay in the cells, activations flow right and partial sums flow down into accumulators: one activation read feeds 256 MACs. Benefits: very high throughput (92 TOPS) at low energy per operation, simple control, software-managed on-chip memory instead of caches. Limits: only dense matrix work, and a fill/drain latency per stream.
The June 2026 paper asked exactly this form for superscalar techniques: eight sub-points, five lines each. For every point write the mechanism (what the hardware or the compiler does), the benefit, and one cost. Points 2–3 are the notes' own material (lesson 31); points 4–8 are the background of this lesson, which the topic list ("Intro to GP-GPUs; Intro to Tensor Cores", "Intro to Neural Processing Units") makes examinable but the notes do not cover.
Compare a vector processor (VMIPS-like) and the streaming multiprocessor of a GP-GPU. For each one describe: the main concepts; how the length of a loop is handled; how a conditional statement inside the loop is handled; how memory latency is hidden; the main benefits; the main drawbacks.
Show model answer
| Vector processor (VMIPS) | GP-GPU streaming multiprocessor | |
|---|---|---|
| Main concepts | SIMD with one PC; vector registers of MVL = 64 elements; deeply pipelined vector FUs in lanes with chaining; one vector load/store unit; a scalar control processor. One instruction = a whole vector. | Multithreaded SIMD: threads grouped in warps of 32; one instruction per warp, executed in lockstep for its 32 threads, each with private registers (SIMT); dozens of resident warps, a warp scheduler issues from a ready one each cycle; a host CPU and a separate device memory. |
| Loop length | VLR (≤ MVL) and strip mining: ⌈n/MVL⌉ strips. | A grid of n threads in blocks; the tail is masked off by if (i < n); no VLR. |
| Conditionals | A vector compare sets the mask register; the instructions execute under the mask. | Branch divergence: the warp executes both paths, each under a per-thread mask. |
| Memory latency | Paid once per vector, then one element per cycle from interleaved banks; strided and gather/scatter loads; little or no data cache. | Hidden by switching among resident warps, whose registers stay in the register file; coalescing merges a warp's accesses; small caches save bandwidth. |
| Benefits | Few instructions (DAXPY: 6 instead of 578), no loop overhead, deep pipelines without hazards, energy-efficient, scales with lanes under the same code. | Very high throughput on massively parallel work; latency hiding without out-of-order logic; sequential code for one thread; scales with the number of SMs. |
| Drawbacks | Only vectorisable loops; needs a high-bandwidth memory; masked elements still cost cycles; bank conflicts with bad strides. | Divergence and uncoalesced accesses waste lanes; host–device copies; one thread is slow; needs thousands of threads to fill the machine. |
The forms of July 2024 (fine-grained vs simultaneous multithreading) and June 2025 (a dual-core chip) were tables with blank lines: one precise line per cell, naming the mechanism and not only the term. The loop-length, conditional and latency rows are the mapping of this lesson — VLR against a masked tail, mask register against divergence, latency amortised by long vectors against latency hidden by warps.
In the GP-GPU programming model, the CPU (host) and GPU (device) have separate memory address spaces. (True/False)
Show model answer
True. The device has its own DRAM; pointers to device memory are valid only on the device, and data moves explicitly (cudaMalloc, cudaMemcpy host to device, kernel launch, cudaMemcpy device to host).
Question 3 of the quizzes of 10 June 2025, word for word — the only GPU question of the three papers. The wrong answer usually comes from unified memory, which automates the copies but does not merge the memories.
Let's consider the loop for (i=0; i<260; i++) Y[i] = X[i] + Y[i]; compiled for a processor with the AVX-512 SIMD extension (512-bit registers, 64-bit elements), without masked instructions: the leftover elements are handled by a scalar remainder loop. How many SIMD add instructions and how many scalar add instructions are executed? (SINGLE ANSWER)
Show model answer
Answer 1. A 512-bit register holds 8 doubles, and 260 = 32 × 8 + 4: the SIMD loop runs 32 times, and the 4 leftover elements go to the scalar remainder loop. With the k-mask registers of AVX-512 the 4 leftovers would take one masked add (33 instructions); on a vector machine the loop is strip-mined with VLR instead (5 strips with MVL = 64).
The same loop as the strip-mining quiz of the papers, on a different machine. The distractors are the counts of the neighbouring machines — 65 for AVX, 33 for the masked version, 5 for the VMIPS strips — in the style the papers build their options.
Which of the following statements about the streaming multiprocessor of a GP-GPU are true? (MULTIPLE ANSWERS)
Show model answer
Answers 1, 2 and 4. A warp is one instruction stream for 32 threads with private registers; latency is hidden by switching warps; divergent paths run one after the other under masks. A GPU has no VLR (the tail is masked), and a tensor core is a matrix unit, not a wider FMA.
The two false options are the traps: carrying the VLR over from the vector machine, and imagining the tensor core as a faster ordinary lane.
Check yourself
A program compiled for SSE2 (two doubles per add, 128-bit registers) runs on a core with AVX-512 hardware. What happens?
Which of these did vector ISAs (Cray, VMIPS) have from the start, while packed SIMD got them late or never?
Hennessy & Patterson list exactly these three omissions of multimedia SIMD. AVX2 and AVX-512 added indexed access and masks; a length kept out of the opcode came back only with RISC-V V and ARM SVE.
A warp of 32 threads reaches an if-else. 8 threads take the then-body, 24 the else-body; each body is 10 instructions. How many warp instructions are issued for the two bodies, and what fraction of the lane slots does useful work?
With two bodies of equal length the efficiency is 50 % however the threads split, as long as both paths are taken.
The 32 threads of a warp read the first column of a row-major matrix of doubles with 32 columns: thread t loads M[t][0]. What does the coalescing unit produce?
A tensor core computes D = A × B + C on 4 × 4 matrices. How many of its 64 multiplications use the element a₀₀?
You move DAXPY to a GPU that has tensor cores. How much faster does DAXPY get thanks to the tensor cores?
In the TPU's 256 × 256 weight-stationary array, one activation value is read once from the unified buffer. How many multiply-accumulates use it?
Key take-aways
- Packed SIMD (MMX, SSE, AVX, AVX-512, NEON) writes the vector length into the opcode: a loop of n elements on width W needs ⌊n/W⌋ SIMD instructions plus a remainder loop of n mod W, and a wider register needs new instructions and a recompiled binary.
- A vector ISA keeps the length in VLR: one strip-mined loop handles any n and uses more lanes or a longer MVL without recompiling; RISC-V V and ARM SVE brought the length-agnostic model back.
- Packed SIMD won in CPUs because it is cheap — extra datapaths in the existing pipeline, little state, data from the caches — and got gather (AVX2), scatter and masks (AVX-512) late; its drawback is more instructions, not fewer.
- A GP-GPU's streaming multiprocessor is a multithreaded SIMD processor: a warp is one instruction for 32 threads, each with private registers; masks replace VLR, coalescing replaces the strided load, and switching among dozens of warps hides DRAM latency.
- Host and device have separate memory address spaces: data is copied in and out explicitly — the true/false of June 2025 is True.
- A tensor core computes D = A × B + C on small tiles (Volta: 4 × 4, FP16 in, FP32 accumulation, 64 FMAs per clock); reuse and narrow data raise the compute roof, which only high-intensity kernels such as matrix products can reach — DAXPY, at 1/12 flop per byte, gains nothing.
- An NPU such as the TPU (a 256 × 256 weight-stationary systolic array of 8-bit MACs, 92 TOPS, 28 MiB on-chip) follows H&P's guidelines: dedicated memories, more arithmetic, the simplest parallelism, small data types, a domain-specific language.
What to watch and read
-
Digital Design and Comp. Arch. — Lecture 20: GPU Architectures
Onur Mutlu, ETH Zürich
The bridge from vector machines to CUDA drawn step by step: SIMD hardware under a SIMT programming model, warps scheduled as fine-grained multithreading to hide memory latency, divergence handled with masks.
-
Intel Intrinsics Guide
Intel
Next to Table 30: look up _mm256_add_pd and _mm512_add_pd (one operation, two widths, two encodings), then _mm256_i32gather_pd (AVX2) and _mm512_i32scatter_pd (AVX-512) — the dates of the first red pen.
-
Programming Tensor Cores in CUDA 9
NVIDIA Technical Blog, 2017
The source of the tensor-core numbers: the 4 × 4 × 4 cube figure, 64 mixed-precision FMAs per clock, 8 per SM, and the warp-level 16 × 16 × 16 fragments.
-
An in-depth look at Google's first Tensor Processing Unit (TPU)
Kaz Sato, Cliff Young (Google)
The systolic array animated, the 8-bit quantisation (91 MB to 23 MB), the dozen CISC instructions, and the list of what the chip left out — the hook of this lesson.
-
In-Datacenter Performance Analysis of a Tensor Processing Unit
Jouppi et al., ISCA 2017
The primary source: 65,536 MACs, 92 TOPS, 28 MiB on chip, "15X – 30X faster" and "30X – 80X" better per watt — and a roofline analysis of six production workloads.