Compilers, Kernels, and the CUDA Moat
The previous chapter ended with both of its arguments pointing below the framework, at the compiler. This chapter follows them down. A captured graph still has to become device code, and the gap between naive device code and good device code is not a percentage, it is the better part of an order of magnitude, governed by one cost model: on a modern accelerator, moving bytes costs more than doing arithmetic on them. That model explains why fusing operations is the fundamental compiler optimization, why the fastest attention implementation is best understood as an algorithm rather than a kernel, and why the deepest moat in the AI industry is not silicon but nineteen years of software sediment on top of it. By the end, a reader can trace a line of Python down to the instructions a GPU executes, say where along that line performance is won, and explain why that line runs through one vendor's territory, what it costs everyone that it does, and how models writing their own kernels might change it. The quantized kernels that serve models in production have their own treatment in Chapter 34; this chapter is about the machinery that produces kernels at all.
Count bytes, not FLOPs
A kernel, in GPU vocabulary, is one program launched onto the device: a matrix multiply, an elementwise add, a softmax. Whether a kernel runs fast is decided almost entirely by one ratio, its arithmetic intensity: the FLOPs it performs per byte it moves to and from high-bandwidth memory (HBM). The roofline model turns that ratio into a picture: attainable throughput is the smaller of the hardware's peak arithmetic rate and its memory bandwidth times the kernel's intensity, so performance climbs linearly with intensity until it hits the flat compute ceiling (Williams et al. 2009). The corner where the two roofs meet is the machine's character in a single number. An H100 delivers roughly 989 dense BF16 teraFLOPs against 3.35 TB/s of HBM bandwidth, putting the corner near 295 FLOPs per byte: any kernel doing less arithmetic per byte than that is memory-bound, and its runtime is simply bytes moved divided by bandwidth.
Almost everything in a transformer other than the matrix multiplies lives far below the corner. An elementwise add performs one FLOP per output element while moving six bytes in half precision, an intensity around 0.17, three orders of magnitude short. Run each such operation as its own kernel and the tensor makes a round trip to HBM between every step, so a chain like "subtract the max, exponentiate, sum, divide" pays for four trips when one would do. The remedy is fusion: compile the chain into a single kernel that reads the input once, keeps intermediates in on-chip registers, and writes the result once. The arithmetic is identical; only the traffic changes; and since traffic is the price, fusion is the closest thing this layer has to a free lunch (He 2022). Measurement on real training confirms how much of the bill it addresses: a study of transformer training found the memory-bound operations, not the matmuls, dominating runtime, and reclaimed a third of it by data-movement optimization alone (Ivanov et al. 2021). A second, smaller tax pushes the same direction: each kernel launch costs microseconds of driver overhead, which is why runtimes record and replay whole launch sequences as CUDA graphs rather than launching kernels one by one (Gray 2019).
The runnable makes the accounting concrete: a softmax over a large matrix, executed as four separate kernels versus one fused kernel, same FLOPs, three times the bytes, and both intensities so far below the roofline corner that predicted runtime is just traffic divided by bandwidth.
N, d, B = 4096, 4096, 4 # one fp32 score matrix, bytes per element
flops = 5 * N * d # max, subtract, exp, sum, divide: ~one op each per element
# unfused: four kernels, each reading and writing the N*d matrix from HBM
unfused = (2 + 3 + 2 + 3) * N * d * B # reads+writes per kernel, summed
# fused: read the input once, write the output once
fused = 2 * N * d * B
for name, traffic in [("unfused", unfused), ("fused", fused)]:
print(f"{name:8s}: {traffic/1e6:6.1f} MB moved, "
f"intensity {flops/traffic:5.2f} FLOP/byte, "
f"bandwidth-bound time on H100: {traffic/3.35e12*1e6:5.0f} us")
print("same FLOPs; the fused kernel is faster exactly by the traffic it skipped")
The canonical lesson: FlashAttention
If fusion is the everyday move, FlashAttention is the layer's masterpiece, and it earns a section because it changed what the field believes a kernel can be. Attention's textbook form materializes an score matrix, and for long sequences that matrix neither fits in fast on-chip memory nor deserves the HBM round trips it forces. FlashAttention computes exact attention, no approximation, by tiling: stream blocks of keys and values through the small on-chip SRAM, maintain a running maximum and normalizer so the softmax can be computed incrementally in one pass, a trick published years earlier as online softmax (Milakov and Gimelshein 2018), and never write the score matrix anywhere (Dao et al. 2022). Memory drops from quadratic to linear in sequence length, and the paper proves the byte count is asymptotically optimal for exact attention across the whole range of SRAM sizes. The control that makes the lesson stick: an earlier memory-efficient attention achieved the same memory bound without arranging the computation around the memory hierarchy, and its own tables show it running slower than the baseline (Rabe and Staats 2021). Saving memory was never the point; choreographing bytes was.
The sequels sharpen the lesson into an uncomfortable one for portability. FlashAttention-2 got from 25-40% of peak to as much as 73% not with a new idea but by repartitioning the same computation across the GPU's workers (Dao 2023); FlashAttention-3 reached 85% of Hopper's BF16 peak only by embracing that chip's newest hardware, dedicated asynchronous copy engines and warp-group matrix instructions, so the fastest attention is now written against one architecture's private vocabulary (Shah et al. 2024). Peak performance, it turns out, is not portable. That fact shapes everything in the next two sections.
The lowering pipeline
Between the framework's captured graph and the bytes-conscious kernel sits a compiler stack, and by 2026 its shape is settled enough to draw. PyTorch's torch.compile captures graphs from Python bytecode, as the previous chapter described, and hands them to TorchInductor, which fuses what it can and emits Triton for the GPU; measured across 180 real models, the pipeline buys a 2.27x inference and 1.41x training geomean speedup over eager execution, essentially by automating this chapter's cost model (Ansel et al. 2024). JAX's lane was born compiled: traced programs lower to a portable operator dialect and then into XLA, the compiler that has fed TPUs since 2017 and, spun out as OpenXLA in 2023, now fronts more than one vendor's silicon. The two lanes converge at the bottom: NVIDIA's compilers take over at PTX, a virtual instruction set that the driver translates to the physical one, which is the hinge on which several moat arguments below will turn.
The stack's intellectual lineage matters because it explains the division of labor. Halide, an image-processing language, contributed the founding separation: describe what to compute apart from how to schedule it onto the machine, so schedules can be searched rather than hand-derived (Ragan-Kelley et al. 2013). TVM carried that separation to deep learning and made the search automatic, a learned cost model exploring schedules no human would enumerate (Chen et al. 2018). MLIR industrialized the plumbing, a compiler framework of composable dialects that XLA, Triton's internals, and most newer entrants now share (Lattner et al. 2021). And Triton set the abstraction level the ecosystem converged on: the programmer writes one program per tile, a small matrix block, while the compiler handles memory coalescing, shared-memory staging, and scheduling within the processor, hard parts that CUDA makes the programmer own (Tillet et al. 2019). Triton is how TorchInductor emits kernels, and its backends now target NVIDIA, AMD, and Intel silicon, which is why it carries the ecosystem's portability hopes. It also carries the counter-evidence: to chase Hopper and Blackwell peaks, Triton grew warp specialization and then Gluon, a lower dialect exposing exactly the architecture-specific controls the abstraction was meant to hide. The pattern from FlashAttention-3 recurs at the compiler level: portability of authorship is achievable; portability of peak performance keeps slipping.
The moat
CUDA, launched in 2007, is named as if it were a language, but what the name covers is a vertical stack: the driver and runtime, the PTX contract between them, and above all the libraries, cuBLAS for linear algebra, NCCL for the collectives of Chapter 62, and cuDNN, whose 2014 release welded the young frameworks to NVIDIA silicon at the exact moment deep learning industrialized (Chetlur et al. 2014; Nickolls et al. 2008). Two decades of that sediment now stand behind a claimed six million CUDA developers. The moat is legal as well as technical: NVIDIA's license forbids translating compiled CUDA outputs to run on other vendors' hardware, and the one serious attempt at a binary compatibility layer, ZLUDA, has cycled through corporate funding, legal takedown, anonymous revival, and, as of mid-2026, a return to hobby status. Competitors therefore attack the layer above the binary. AMD's current accelerators lead on memory, 288 GB of HBM3E and formats treated in Chapter 34, and its ROCm stack runs upstream PyTorch and the major serving engines; yet the most careful independent benchmarking of the MI300X generation concluded that public-software training throughput trailed NVIDIA by more than 2.5x, under the title "CUDA moat still alive" (SemiAnalysis 2024). The one camp that verifiably escaped did so by never entering: Google's TPU line, dense systolic arithmetic fed by the XLA compiler, has trained frontier models since before the moat had its name (Jouppi et al. 2017), and AWS's Trainium follows the same XLA-descended path at half-million-chip scale. Both escapes, tellingly, grew Triton-like kernel languages of their own once their users hit the ceiling of what a compiler alone delivers.
This layer reaches all the way up into Part I's architecture choices. Hooker's "hardware lottery" names the mechanism: a research idea wins not only on merit but on how well it maps to the software and silicon that exist, so decades of ideas off the matmul path were never really auditioned (Hooker 2021). The kernel and compiler layer is where the lottery is drawn. An architecture whose inner loop lacks a fused, tuned kernel measures as slow regardless of its mathematics, which is part of why Chapter 9's state-space models needed custom kernels before their scan operation could compete with attention at all, and why monolithic optimized kernels make radically different architectures expensive even to evaluate (Barham and Isard 2019). What the compiler stack can lower efficiently quietly decides what the field gets to try.
Kernels written by models
The newest force at this layer is the subject of this book pointed at its own foundations. The benchmark that frames the question, KernelBench, asks models to write correct, faster-than-PyTorch GPU kernels, and frontier models solved fewer than a fifth of its tasks one-shot in early 2025 (Ouyang et al. 2025). The gap between one-shot and iterating-against-a-checker, however, is this book's recurring lesson, and it holds here: with compilation and timing as the reward signal, reinforcement-trained and search-driven systems now reliably produce correct kernels and beat PyTorch's eager operators on memory-bound workloads, occasionally by multiples (Baronio et al. 2025). The cautionary tale arrived on schedule: a widely publicized "AI CUDA engineer" claiming hundred-fold speedups was found within days to have exploited its own evaluation harness, reusing a cached correct result rather than computing one, a textbook case of the reward hacking that Chapter 27 anatomizes (Sakana AI 2025). The sober mid-2026 reading is that generated kernels are becoming the cheap way to cover the long tail of fusible operations, while nobody has shown a model matching FlashAttention-3-class engineering at the compute-bound frontier, and the integrity of the timing harness is now a first-order concern. Production serving reflects the same division of labor: vLLM compiles the fusible glue between operations by default and replays it under CUDA graphs, while attention and the quantized matmuls remain hand-written kernels the compiler treats as opaque (Ansel et al. 2024). The compiler owns the floor; the peaks are still artisanal.
Whether the moat is eroding depends on which claim is being made. That one Triton source file can now target four vendors' hardware is settled, and Meta serves production models through that path on its own accelerators. That the portable path matches the native one is false as of mid-2026: the cleanest measurement, an all-Triton Llama serving stack from the PyTorch team itself, reached 76-78% of the CUDA-kernel path's performance on H100. One camp reads the trajectory, compiler-first stacks, multi-vendor Triton, models generating kernels, as compounding depreciation of NVIDIA's software advantage; the other observes that each new hardware generation adds private machinery, asynchronous engines, new numeric formats, protected interconnect, faster than abstractions absorb it, so the moat refills from the bottom at least as fast as it drains from the top. Both camps cite FlashAttention-3 as their exhibit.
Further reading
- He, “Making Deep Learning Go Brrrr From First Principles” (the gentlest correct introduction to the cost model), 2022. horace.ioThe gentlest correct introduction to GPU performance: every workload is memory-bound, compute-bound, or overhead-bound, and fusion is the most important optimization a deep-learning compiler performs.
- Williams et al., “Roofline: An Insightful Visual Performance Model for Multicore Architectures” (the one-diagram cost model this chapter stands on), 2009. doi.orgThe roofline model: attainable throughput is the minimum of peak arithmetic rate and memory bandwidth times arithmetic intensity, making memory-bound versus compute-bound a picture instead of a debate.
- Nickolls et al., “Scalable Parallel Programming with CUDA” (the execution model from the people who built it), 2008. queue.acm.orgThe canonical description of CUDA's execution model, grids, blocks, warps, and SIMT, written by its architects the year after launch.
- Milakov & Gimelshein, “Online normalizer calculation for softmax” (the four-page trick that made attention tileable), 2018. arXiv:1805.02867A single-pass softmax that maintains a running maximum and normalizer, the four-page trick that years later made tiled exact attention possible.
- Dao et al., “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness” (counting bytes as an algorithmic idea), 2022. arXiv:2205.14135FlashAttention is an IO-aware exact attention algorithm that uses tiling and recomputation to minimize HBM accesses, achieving faster wall-clock training and linear memory usage in sequence length.
- Shah et al., “FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision” (what squeezing one specific chip looks like), 2024. arXiv:2407.08608FlashAttention-3 reaches 85
- Ragan-Kelley et al., “Halide: A Language and Compiler for Optimizing Parallelism, Locality, and Recomputation in Image Processing Pipelines” (where the algorithm/schedule separation came from), 2013. dl.acm.orgHalide separates what to compute from how to schedule it onto the machine, the founding idea behind every tensor compiler's search over schedules.
- Chen et al., “TVM: An Automated End-to-End Optimizing Compiler for Deep Learning” (the first end-to-end learned-search tensor compiler), 2018. arXiv:1802.04799TVM carries Halide's algorithm/schedule separation to deep learning and makes the schedule search automatic with a learned cost model, plus the operator-fusion taxonomy compilers still use.
- Tillet et al., “Triton: An Intermediate Language and Compiler for Tiled Neural Network Computations” (the tile abstraction that became the kernel lingua franca), 2019. dl.acm.orgTriton makes the statically-shaped tile the unit of GPU programming: the programmer writes one program per tile while the compiler handles coalescing, shared memory, and intra-processor scheduling.
- Ansel et al., “PyTorch 2: Faster Machine Learning Through Dynamic Python Bytecode Transformation and Graph Compilation” (a compiler retrofitted onto an eager framework), 2024. docs.pytorch.orgHow a compiler was retrofitted onto an eager framework: TorchDynamo captures graphs from Python bytecode and TorchInductor emits Triton, for a 2.27x inference and 1.41x training geomean speedup across 180 real models.
- Lattner et al., “MLIR: Scaling Compiler Infrastructure for Domain Specific Computation” (the shared plumbing under XLA, Triton, and the rest), 2021. arXiv:2002.11054MLIR is the compiler framework of composable dialects and progressive lowering that XLA, Triton's internals, Mosaic, and most newer ML compilers now share.
- Chetlur et al., “cuDNN: Efficient Primitives for Deep Learning” (the moat's founding document), 2014. arXiv:1410.0759The library that welded every 2010s framework to NVIDIA silicon: optimized deep-learning primitives beneath Caffe, Torch, and their successors, at the moment the field industrialized.
- Hooker, “The Hardware Lottery” (why ideas win for fitting the silicon), 2021. arXiv:2009.06489A research idea can win because it suits the available software and hardware rather than because it is superior; as silicon specializes around matmuls, ideas off that path pay escalating costs even to be evaluated.
- Ouyang et al., “KernelBench: Can LLMs Write Efficient GPU Kernels?” (the yardstick for the frontier section's open question), 2025. arXiv:2502.10517The benchmark asking whether models can write correct, faster-than-PyTorch GPU kernels: 250 workloads, a speedup-aware metric, and frontier models beating the baseline in under 20
Comments
Log in to comment