GPU fundamentals
torch.compile
PyTorch traces a model into a graph, fuses the glue with Inductor and Triton, and can wrap the result in CUDA graphs. Dynamic shapes are the catch.
torch.compile (PyTorch 2.x) traces your model into a graph, optimizes it, and generates device code. TorchInductor often emits Triton kernels for fusions, and delegates big GEMMs to cuBLAS-like libraries. You gain automatic fusion and less Python in the hot loop. You pay compile time and you fight dynamic shapes.
Concept
A graph break is a place the tracer cannot follow, usually data-dependent Python control flow. That stretch falls back to eager. Guards recompile when a tracked value, such as a sequence length, leaves the range the compiled code assumed.
The big idea
torch.compile is a chef who preps the mise en place once, then cooks fast. The first dinner can take minutes. The next ones are quick, until someone orders a wildly different plate and prep starts over.
The picture breaks for multi-request serving. Engines like vLLM have their own schedulers. torch.compile is one tool in the stack, not a serving solution.
Walking through it
import torch
import torch.nn as nn
class Tiny(nn.Module):
def __init__(self):
super().__init__()
self.lin = nn.Linear(8, 8)
def forward(self, x):
return torch.relu(self.lin(x))
model = torch.compile(Tiny(), mode="reduce-overhead")
x = torch.randn(4, 8)
print(tuple(model(x).shape))Output:
(4, 8)mode="reduce-overhead" leans on CUDA graphs for further launch reduction. max-autotune spends more compile time searching kernel configs.
What bites you:
- Compile time. First run, and cache misses, can take seconds to minutes.
- Dynamic shapes. Varying batch or sequence length can cause recompiles. Serving engines often pad or bucket to stabilize shapes.
- Graph breaks. Data-dependent Python drops you back to eager.
Compile and autotune caches must travel with the deployment. Cold pods that recompile on first request create multi-second TTFT cliffs. Bake caches into images or warm pods before taking traffic. Treat compile as part of the release artifact.
| Path | Strength | Weakness |
|---|---|---|
| torch.compile / Inductor | Great fusions in the PyTorch ecosystem | Dynamic shapes, graph breaks, long compile |
| TensorRT / TensorRT-LLM | Aggressive engine build, strong NVIDIA performance | Heavier pipeline, less flexible mid-flight |
| Hand Triton / CUDA | Exact control for novel ops | Maintainership cost |
Serving engines mix these: library attention, compiled MLP glue, graphs around the step. Know which layer owns a hotspot before rewriting anything.
Common mistakes
- Compiling at request time on a cold replica. That is a TTFT outage, not a speedup.
- Letting every sequence length through unpadded. You will recapture and recompile under load.
- Treating
torch.compileas a replacement for a serving engine.
Check yourself
- Why do serving stacks pad or bucket lengths under
torch.compile? Answer: To keep shapes inside the compiled guards so you do not recompile every request. - What does
mode="reduce-overhead"lean on besides fusion? Answer: CUDA graphs, to cut launch tax after the graph is built.
Key takeaways
torch.compilefuses the glue and leaves big GEMMs to libraries.- First compile is slow. Ship the cache with the replica.
- Dynamic shapes and graph breaks are the usual regressions.
- It is one layer in a serving stack, not the stack.
Further learning
torch.compile documentationRedirecting…pytorch.orgBack to the kernels, fusion and compilation hub.
Related
Kernels, fusion and compilation
Kernel fusion, torch.compile, CUDA graphs and the libraries that make models fast.
CUDA graphs
Record a fixed sequence of GPU launches once and replay it every token, so the CPU is no longer the metronome for decode.
Kernel fusion
Combine several ops into one GPU kernel so intermediates stay in registers or shared memory instead of round-tripping through HBM.