NVIDIA GPU Architectures Series — Presentation 07

Hopper — H100, FP8, and the Transformer Engine

The architecture that trained GPT-4-class frontier models. FP8 native tensor cores doubled compute over BF16; the Transformer Engine made FP8 actually usable; TMA, thread-block clusters, distributed shared memory and DPX rewrote the SM-level programming model.

H100GH100H200 FP8Transformer Engine TMAThread-Block Cluster DSMEMDPXNVLink 4
GH100 → SM → TC4 → FP8 → TMA → Cluster → TE → NVLink 4
00

Topics We'll Cover

A focused tour of NVIDIA's Hopper generation — the architecture that made FP8 a first-class citizen and turned the SM into something that looks more like a tile-streaming engine than a stack of warp lanes.

01

Hopper in One Page

Hopper, named for Grace Hopper, is the first NVIDIA GPU with native FP8 tensor cores. That single change — together with the Transformer Engine that made FP8 usable without manually rescaling every layer — redefined what "frontier" meant. GPT-4-class models were trained on Hopper.

PropertyValue
ProcessTSMC 4N (custom 5 nm class)
Transistors80 billion
Die area814 mm²
UnveiledMarch 2022 (GTC)
Volume availability2023
Compute capability9.0
Native low-precisionFP8 (E4M3 / E5M2), BF16, TF32, FP16
Launch SKUsH100 SXM5 (700 W), H100 PCIe (350 W)
RefreshH200 (mid-2024, HBM3e)
SuperchipGH200 = Grace CPU + Hopper GPU via NVLink-C2C

Why it mattered

More than doubled BF16 throughput per chip vs A100, then doubled that again with FP8. The Transformer Engine meant model authors didn't have to hand-roll FP8 scaling. Result: training a frontier-class transformer at 2×–3× the wall-clock speed of A100 at the same node count.

What landed at the SM level

TMA (asynchronous tile DMA), thread-block clusters (a new level of the launch hierarchy), distributed shared memory across blocks in a cluster (DSMEM), DPX dynamic-programming instructions, and warp-group MMA — the largest tile primitive NVIDIA had ever shipped.

The single sentence

Hopper is the first GPU where the tensor core stopped being a bolt-on and became the architectural centre, with the rest of the SM (TMA, clusters, DSMEM, async transactions) reorganised to keep that tensor core fed.

02

GH100 — The Die

GH100 is the silicon under every H100 / H200 / GH200 SKU. Two perspectives below: the compute fabric and the memory subsystem. Yields and SKU binning mean none of the shipping products use the full die — the H100 enables 132 of 144 SMs, the H200 keeps the same compute config but swaps to denser memory.

Compute fabric

  • 144 SMs total, 132 enabled on H100 SXM5
  • 8 GPCs × 9 TPCs each × 2 SMs (full die)
  • 66 TPCs on H100 SXM5 (after disable)
  • 18 432 FP32 CUDA cores (full die)
  • 576 4th-gen tensor cores (4 per SM, full die)
  • 50 MB L2 cache, partitioned
  • 2 video encoders (NVENC), 1 video decoder (NVDEC)

Memory subsystem

  • H100: 5 active stacks of HBM3 (one disabled) → 80 GB at 3.35 TB/s (SXM5) or 2 TB/s (PCIe)
  • H100 NVL: special edition with 94 GB, 3.9 TB/s — built for inference of larger models
  • H200: 6 stacks of HBM3e → 141 GB at 4.8 TB/s — same GH100 die, denser memory
  • 5120-bit HBM interface, ECC enabled by default
  • L2 acts as a giant on-chip staging buffer for tile-based kernels
GH100 floorplan — schematic only HBM3 #0 HBM3 #1 HBM3 #2 HBM3 #3 HBM3 #4 HBM #5disabled L2 50 MBpartitioned GPC09 TPC / 18 SM GPC19 TPC / 18 SM GPC29 TPC / 18 SM GPC39 TPC / 18 SM GPC49 TPC / 18 SM GPC59 TPC / 18 SM GPC69 TPC / 18 SM GPC79 TPC / 18 SM NVLink 4 (18 links, 900 GB/s aggregate) PCIe 5 x16 + multi-instance front-end Full die: 144 SMs / 18432 FP32 cores / 576 tensor cores. H100 enables 132 SMs / 66 TPCs.
SKU footnote

Same die powers everything Hopper-branded. The H100 NVL is two H100 PCIe boards bridged with NVLink and binned for higher VRAM (94 GB each); it was launched specifically because Llama-2-70B FP16 wouldn't fit on a stock 80 GB H100. The H200 is a die-identical refresh whose news is entirely the HBM3e upgrade.

03

The Hopper SM

The Hopper SM keeps the Ampere-style four-partition layout but adds a pile of new fixed-function units. The headline isn't another increment in CUDA cores — it's that almost everything around the cores has been redesigned for asynchronous, tile-based execution.

Hopper SM — 4 partitions, new asynchronous units, 256 KB unified L1+SMEM Partition 0 32× FP32 16× INT32 16× FP64 TC4 (1) 4× LD/ST + tex Warp scheduler / 64 KB regs L0 i-cache + dispatch Partition 1 32× FP32 16× INT32 16× FP64 TC4 (1) 4× LD/ST + tex Warp scheduler / 64 KB regs L0 i-cache + dispatch Partition 2 32× FP32 16× INT32 16× FP64 TC4 (1) 4× LD/ST + tex Warp scheduler / 64 KB regs L0 i-cache + dispatch Partition 3 32× FP32 16× INT32 16× FP64 TC4 (1) 4× LD/ST + tex Warp scheduler / 64 KB regs L0 i-cache + dispatch 256 KB unified L1 / shared memory (carveable). 64-bit shared atomics. TMAasync tile DMA DSMEM routercross-block SMEM DPXmin/max+add for DP Async transaction barriers New in Hopper TMA + Cluster + DSMEM + DPX + WGMMA + async-transaction mbarrier semantics

What's actually new vs Ampere

04

4th-Gen Tensor Cores + FP8

The headline numerical change in Hopper is native FP8. Two 8-bit float formats are supported, chosen so that one favours mantissa precision and the other favours dynamic range. They share an exponent bias and convert losslessly to/from FP16/BF16.

E4M3 — precision-leaning

  • 1 sign + 4 exponent + 3 mantissa bits
  • Range: ~±448 (with NaN at the top)
  • Used for weights and activations after per-tensor scaling
  • Higher mantissa → better numerical fidelity in the quantised matmul

E5M2 — range-leaning

  • 1 sign + 5 exponent + 2 mantissa bits
  • Range: ~±57 344
  • Used for gradients (which span many orders of magnitude)
  • Higher exponent → survives backward-pass dynamic range

Throughput on H100 SXM5

FormatDense TFLOPSSparse (2:4) TFLOPSvs FP16
FP64 (tensor core)67—0.07×
TF324959890.5×
FP16 / BF1698919791.0× (ref)
FP8 (E4M3 / E5M2)197939582.0×
INT8197939582.0×

WGMMA — warp-group MMA

The PTX instruction is wgmma.mma_async.sync.aligned.m64nNkK. A warp group (4 warps = 128 threads) collectively issues a single MMA over a 64×N×K tile, with N up to 256. This is far larger than Ampere's m16n8k16 MMA — one instruction now drives a much larger fraction of the matmul. The async suffix is the key: the tensor core proceeds in the background while the warp group does other work (e.g. issues the next TMA or accumulates the previous tile).

PTX shape comparison: Ampere vs Hopper
// Ampere FP16: one warp issues a 16×8×16 tile, blocking-ish
mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32  d, a, b, c;

// Hopper FP8: a warp-group (4 warps) issues a 64×128×32 tile, async
wgmma.mma_async.sync.aligned.m64n128k32.f32.e4m3.e4m3  d, a-desc, b-desc, scaleD;
wgmma.commit_group.sync;
wgmma.wait_group.sync  0;     // drain when needed
Why "async" matters

On Ampere the MMA was effectively synchronous in software terms: the warp had to feed it operands and wait for the result before issuing the next. On Hopper, WGMMA is a fire-and-forget tile job; while one tile crunches, the warp group can issue a TMA for the next tile, accumulate the previous one, or store partial sums. CUTLASS 3.x and the Transformer Engine kernels are entirely structured around this overlap.

05

Transformer Engine — Software Magic for FP8

FP8 has only ~256 representable values. Naively casting a transformer's activations to FP8 destroys accuracy unless every tensor is scaled into FP8's narrow dynamic range, then unscaled back. The Transformer Engine (TE) is the open-source library that does this for you.

Before TE — the manual way

  • Author writes custom CUDA kernels per layer
  • Track activation max per tensor; recompute every step
  • Pick E4M3 vs E5M2 by hand for fwd vs bwd
  • Insert cast_to_fp8 / cast_from_fp8 around every matmul
  • Re-tune scale factors when the model changes
  • Easy to lose >1 perplexity point through bad scaling

With TE — the automatic way

  • Replace nn.Linear with te.Linear
  • Wrap the forward in fp8_autocast(...)
  • TE tracks per-tensor amax history, computes scale
  • Picks E4M3 for activations/weights, E5M2 for gradients
  • "Delayed scaling" reuses the previous step's amax to avoid stalls
  • Drops in to PyTorch, JAX, NeMo, Megatron-LM, DeepSpeed
PyTorch: enable FP8 on a transformer block in three lines
import transformer_engine.pytorch as te
from transformer_engine.common import recipe

fp8_recipe = recipe.DelayedScaling(
    margin=0, interval=1,
    fp8_format=recipe.Format.HYBRID,    # E4M3 fwd, E5M2 bwd
    amax_history_len=16, amax_compute_algo="max",
)

# Replace nn.Linear with te.Linear — same API, FP8 internals
class Block(nn.Module):
    def __init__(self, d):
        super().__init__()
        self.fc1 = te.Linear(d, 4*d, bias=True)
        self.fc2 = te.Linear(4*d, d, bias=True)

# Wrap the forward; everything between is FP8-accelerated
with te.fp8_autocast(enabled=True, fp8_recipe=fp8_recipe):
    out = block(x)
    loss = out.sum()
loss.backward()                       # grads computed in E5M2 path

Delayed scaling — the trick that hides the rescale stall

"Online" scaling would have to read the activation, compute its max, broadcast that max, then scale — a multi-pass dependency on every tensor. Delayed scaling instead remembers a short rolling history of recent amax values per tensor (default 16 steps), takes the running max, and uses that to scale the next forward pass. The cost is one stale step at start-up; the win is no synchronisation barrier in the hot path.

Production status

TE ships with NVIDIA's NGC PyTorch and JAX containers, and is integrated into NeMo, Megatron-LM, MaxText, Hugging Face Accelerate, and (for inference) vLLM and TensorRT-LLM. For most modern transformer architectures (Llama, Mixtral, Qwen, GPT-OSS) it gives BF16 accuracy at FP8 throughput with no model changes — the headline reason H100 doubled real-world training speed over A100.

06

TMA — Tensor Memory Accelerator

Before Hopper, every thread that participated in a tile-based matmul also had to participate in loading that tile from global memory: address arithmetic, predication, vectorised LDG, register pressure. That overhead competed with the tensor cores for issue slots and consumed registers and ALU bandwidth. TMA removes it.

What it is

TMA flow — one tile request, asynchronous bulk fill HBM3 global memory [N×K matrix] L2 50 MB tiles cached here TMA tensor map + descriptor engine SMEM (256 KB) tile A, tile B, double-buffered bulk read arrive on mbarrier one cp.async.bulk.tensor inst
CUDA C++: the kernel side, sketched
// Build a tensor map once on the host
CUtensorMap tmap_a;
cuTensorMapEncodeTiled(&tmap_a, CU_TENSOR_MAP_DATA_TYPE_FLOAT8_E4M3,
    2, dev_a, dim, stride, box, elem_stride,
    CU_TENSOR_MAP_INTERLEAVE_NONE, CU_TENSOR_MAP_SWIZZLE_128B,
    CU_TENSOR_MAP_L2_PROMOTION_L2_128B, CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE);

// In the kernel: one warp issues the TMA load
__shared__ alignas(128) __nv_fp8_e4m3 sA[128][64];
__shared__ uint64_t bar;
if (threadIdx.x == 0) {
    cde::cp_async_bulk_tensor_2d_global_to_shared(&sA, &tmap_a, ki*64, mi*128, &bar);
    cde::cp_async_bulk_commit_group();
}
cde::cp_async_bulk_wait_group<0>();   // or wait on bar with mbarrier
// sA is now populated; warp group can WGMMA on it.
The architectural consequence

With TMA, kernels become almost pure compute: threads issue one TMA, wait on a barrier, run WGMMA, repeat. Address generation, predication, and global LDG are gone from the inner loop. CUTLASS 3.x's Hopper kernels run with double-buffered TMA prefetch hidden under WGMMA, achieving >80% of theoretical FP8 throughput on dense matmul shapes.

07

Thread-Block Clusters + DSMEM

Hopper adds a new level to the launch hierarchy. Before Hopper, the levels were Grid → Block → Warp → Thread, with the SM as the natural hardware boundary: blocks ran on one SM, no SM-to-SM cooperation existed except via global memory. Hopper inserts a Cluster level above the block.

Grid
whole kernel launch — many clusters
Cluster
block 0
block 1
block 2
…
block 15
Block
warp 0
warp 1
…
warp 31
Warp
32 threads

Co-scheduling & DSMEM

CUDA C++: declare a cluster + use DSMEM
// Compile-time cluster shape, opt-in launch attribute
__cluster_dims__(2, 2, 1)        // 4 blocks per cluster
__global__ void matmul_cluster(const half* A, const half* B, float* C) {
    namespace cg = cooperative_groups;
    auto cluster = cg::this_cluster();
    __shared__ half tile[128][64];

    // Each block loads its own slice via TMA into its own SMEM
    tma_load_my_slice(tile);

    cluster.sync();                        // all blocks have their slice

    // Read peer block's tile via DSMEM — one pointer cast away
    half* peer_tile = cluster.map_shared_rank(tile, 1);
    // peer_tile lives on another SM; load latency is L1-ish, no HBM hop.
}

Practical use — collaborative matmul

The CUTLASS 3.x and cuDNN 9 GEMM kernels split a large output tile across cluster blocks. Each block holds a slice of the working tile and exchanges tile edges with its cluster neighbours via DSMEM as the K-loop progresses. Because peer SMEM reads stay inside the GPC, this avoids the L2 round-trip an Ampere kernel would need for the same data. Net effect: more on-chip bandwidth available to the tensor cores, larger effective tile sizes, better arithmetic intensity.

Why "GPC-local"?

Cross-GPC SM-to-SM traffic would have to traverse the global crossbar — that's L2 latency, no different from going through cache. By restricting clusters to a single GPC, NVIDIA gets DSMEM at L1-class latency essentially for free. The price is a hard cap: clusters can't be larger than one GPC's worth of SMs (18 in the full die), hence the 16-block limit.

08

DPX — Dynamic Programming Acceleration

The smallest of Hopper's headline features but the most domain-specific. DPX adds a family of fused min/max + add instructions that cover the inner loop of dynamic-programming algorithms. The canonical examples are not LLM workloads at all — they are bioinformatics and graph algorithms.

Instruction classComputesUsed in
vimax3 / vimin33-way SIMD min/max on packed int16/int32Smith-Waterman, Needleman-Wunsch (genomics)
viaddmax / viaddmin(a + b) compared against c, returning min/maxFloyd-Warshall (all-pairs shortest path)
vimax3 + viaddmaxfused gap-affine alignment recurrenceprotein alignment, beam search in RL trees
vibmax / vibminmin/max with index-bit returntraceback paths in dynamic programming

What it actually wins you

CUDA C++: a DPX-accelerated Smith-Waterman cell
// Gap-affine recurrence, one diagonal cell, traditional form:
//   H[i,j] = max( H[i-1,j-1] + s(a,b),  E[i,j],  F[i,j],  0 )
// E[i,j] = max( H[i,j-1] - g_open,  E[i,j-1] - g_extend )
// F[i,j] = max( H[i-1,j] - g_open,  F[i-1,j] - g_extend )
int h = __viaddmax_s32(H_diag, score_match, 0);    // (H_diag + s) ↑ 0
h     = __vimax3_s32(h, E_left, F_up);                       // 3-way max in 1 inst
int e = __viaddmax_s32(H_left, -g_open, E_left - g_extend);
int f = __viaddmax_s32(H_up,   -g_open, F_up   - g_extend);
H[i][j] = h; E[i][j] = e; F[i][j] = f;
Why mention it on an LLM-adjacent deck

DPX is one of those features that fills the marketing slides at GTC and disappears from the working-set vocabulary three months later. It's worth knowing exists because (a) the A100 lacks it — a real reason to choose H100 in genomics, (b) future architectures inherit it, and (c) it explains a chunk of die area that would otherwise be unaccounted for in SM diagrams.

09

H100 Variants — SXM5, PCIe, NVL, H200, GH200

"Hopper" on its own doesn't tell you what's actually in the box. The same GH100 silicon ships in five very different products with widely different memory, power, and link configurations. Pick wrong and you discover at deploy time that NVLink isn't there, or that bandwidth is half what you thought.

SKUVRAMBW (TB/s)TDP (W)NVLinkNotes
H100 SXM5 80 GB HBM3 3.35 700 NVLink 4 / NVSwitch (900 GB/s) HGX baseboard. 8× in DGX H100. The default training SKU.
H100 PCIe 80 GB HBM2e 2.0 350 NVLink bridge (2-card) Standard PCIe slot, half the BW, no NVSwitch fabric. For mixed servers.
H100 NVL 94 GB HBM3 3.9 2 × 350 Paired NVLink between the two boards Special edition; sized so Llama-2-70B FP16 fits in one paired slot.
H200 SXM5 141 GB HBM3e 4.8 700 NVLink 4 / NVSwitch Same die as H100. Drops into HGX boards with firmware update.
GH200 96 GB HBM3 + 480 GB LPDDR5x 4.0 (HBM) + 0.5 (LPDDR) ~1000 module NVLink-C2C 900 GB/s to Grace CPU Grace + Hopper superchip; LPDDR5x exposed as Extended GPU Memory.

DGX H100 — the reference node

Compute

  • 8× H100 SXM5 on an HGX baseboard
  • 4× NVSwitch 3 — full all-to-all NVLink fabric
  • Every GPU sees every other at full 900 GB/s NVLink 4
  • 2× Intel Xeon Platinum (later Sapphire Rapids); some configs Grace via GH200 nodes

Network

  • 8× ConnectX-7 NICs at 400 Gb/s each (NDR InfiniBand or 400 GbE)
  • One NIC per GPU → GPUDirect RDMA per GPU → clean cross-node TP
  • 2× BlueField-3 for storage & management
  • Total node bandwidth 3.2 Tb/s east-west

Picking the right Hopper

10

Performance — Why H100 Changed Everything

The headline NVIDIA pushed at GTC 2022 was 9× training throughput vs A100 on large transformer workloads. The number is real but it's a stack of independent wins; understanding which contributes how much tells you what you actually buy when you upgrade.

Where the 9× comes from

BF16 raw
~3× (more SMs + clocks vs A100)
cumulative 3×
FP8
2× over BF16 = 6×
cumulative 6×
TMA + cluster
~1.3× (overlap, larger tiles)
cumulative ~7.5×
Bigger L2 + HBM3
~1.2× (less HBM pressure)
cumulative ~9×

The four wins compound because they are independent: doubling FP-throughput while halving memory reads while reducing kernel-launch overhead while increasing tile size all multiply rather than add. None of them on its own buys the headline; together they do.

For LLM serving

Worked example — Llama-3-70B in vLLM

GPUQuantPrefill (tok/s)Decode (tok/s/stream)Notes
2× A100 80 GB (TP=2)BF16, no FP8 KV~25 000≤ ~29140 GB of BF16 weights need two cards; each streams 70 GB at 2 TB/s.
H100 SXM5 80 GBFP8 + FP8 KV~110 000≤ ~48Native FP8, TE quantises both weights and KV.
H200 141 GBFP8 + FP8 KV~115 000≤ ~69Compute identical; bandwidth (4.8 TB/s) lifts decode.
The honest summary

H100 over A100 buys you ~4× on prefill (where compute matters) and ~1.7× on per-stream decode (where bandwidth matters). H200 over H100 buys you mostly memory capacity and decode bandwidth; prefill throughput is barely changed. The 9× figure NVIDIA quotes is end-to-end on training a transformer, where chunked prefill, overlap, and FP8 compound over many millions of microbatches.

11

Interactive: Which Hopper for What?

Pick a Hopper SKU. Get the headline numbers and a one-line use-case recommendation.

VRAM
—
Bandwidth GB/s
—
TDP (W)
—
NVLink
—
FP8 dense TFLOPS
—
Notes
—
Headline numbers, all SKUs

Same GH100 die under all five products. FP8 dense throughput differs only because of clock and SM-enable counts (H100 NVL bins lower than the SXM5). Bandwidth is the bigger differentiator: HBM3 vs HBM3e doubles per-stack throughput, and the GH200's LPDDR5x extension trades bandwidth for capacity in a way no other Hopper SKU does.