Home Knowledge Base The frontend, TorchDynamo, captures a graph by hooking CPython itself.

torch.compile is the just-in-time graph compiler that PyTorch 2.0 wraps around an ordinary eager model to make it run as fused, optimized kernels: you write model = torch.compile(model) and change nothing else. The first call traces the model and compiles it; later calls run the compiled code. It keeps the debuggability of eager mode while recovering most of the speed that used to require hand-written kernels or a separate graph framework, and unlike the older TorchScript it almost never asks you to rewrite your model to make tracing succeed.\n\nThe frontend, TorchDynamo, captures a graph by hooking CPython itself. Using the frame-evaluation API (PEP 523), Dynamo intercepts the bytecode of your function and symbolically traces it into an FX graph of tensor operations. The important design choice is what happens when it hits something it cannot trace — data-dependent control flow, a print, a .item(), an unsupported library call. Instead of failing, Dynamo inserts a graph break: it compiles the graph up to that point, runs the offending line in normal eager Python, then resumes capturing a fresh graph afterward. This is why torch.compile is safe to drop onto arbitrary code; the worst case is simply less of the model gets fused.\n\nAOTAutograd captures the backward pass ahead of time, so training compiles too. A plain graph capture only sees the forward computation, but most of the cost of training is in the backward pass. AOTAutograd traces forward and backward together into a joint graph, which lets the compiler fuse and schedule gradients as aggressively as activations and decide which intermediate tensors to save versus recompute. Beneath it, PrimTorch decomposes PyTorch's roughly two thousand operators into a small, stable set of about two hundred and fifty primitives, so a backend only has to implement the primitives rather than the entire sprawling API surface.\n\nTorchInductor, the default backend, lowers that graph to real fused kernels. Inductor performs operator fusion, memory planning, and buffer reuse, then generates code: Triton for the GPU and vectorized C++/OpenMP for the CPU. This is the concrete link between the two most important compilation tools in PyTorch — the graph that Dynamo captured is ultimately emitted as Triton kernels, so torch.compile is, at the bottom, a Triton kernel generator wrapped in a Python-capture frontend. The fusion is where the speedup comes from: pointwise chains, normalizations, and activation functions collapse into single passes that keep data in registers instead of streaming it back and forth through HBM.\n\nGuards and recompilation are the mental model you actually tune against. When Dynamo compiles a graph it installs guards on the properties it assumed — tensor shapes, dtypes, Python attribute values. If a later call violates a guard (a new sequence length, say), that specialization is invalid and Dynamo compiles a new one. Left unmanaged this causes recompilation storms, so torch.compile supports dynamic shapes to compile one shape-agnostic graph instead of one per size. The other lever is graph breaks: because each break caps how much can be fused, keeping .item(), host-side prints, and untraceable calls out of the hot path is the main way to make a compiled model faster, and fullgraph=True turns any graph break into a hard error so you can find them.\n\n| Layer | What it does | Produces |\n|---|---|---|\n| TorchDynamo | captures Python bytecode into a graph; graph-breaks on the untraceable | FX graph(s) + guards |\n| AOTAutograd | traces the backward pass as well as the forward | joint forward+backward graph |\n| PrimTorch | decomposes ~2000 ops into ~250 primitive ops | canonical operator set |\n| TorchInductor | fuses operations and generates kernels | Triton (GPU) / C++/OpenMP (CPU) |\n\n``svg\n\n \n\n \n torch.compile — Dynamo to Inductor to Triton Kernels\n one decorator · TorchDynamo graph capture · AOTAutograd fwd+bwd · TorchInductor kernel fusion\n\n \n \n COMPILATION STACK\n\n \n \n @torch.compile(model) # one decorator\n\n \n \n \n \n \n \n\n \n \n TorchDynamo\n traces bytecode → FX graph + guards\n \n captured FX graph\n \n \n x\n \n w\n \n \n add\n \n mul\n \n relu\n \n \n \n \n \n \n out\n\n \n\n \n \n AOTAutograd\n captures forward + backward in one graph\n\n \n\n \n \n PrimTorch — ~2000 ops → 250 primitives\n\n \n\n \n \n TorchInductor\n pattern-match, fuse, lower to kernels\n\n \n \n \n \n \n \n Triton (GPU)\n \n C++ / OpenMP (CPU)\n\n \n \n GUARD → CACHE → EXECUTE\n\n \n First call — compile once\n \n compile (one-time cost)\n \n exec\n kernel written to disk cache after compile\n\n \n 2nd+ call — cache hit\n \n \n exec\n ~1.8× faster\n guard check (ns) + cached kernel\n\n \n \n\n \n Guards Dynamo inserts (checked each call):\n \n \n input.shape == compiled shape\n \n input.dtype (float32, float16...)\n \n device == cuda:0, contiguous layout\n \n no new closure variables captured\n\n \n Guard fails → recompile (avoid with):\n \n \n new batch size → dynamic=True\n \n new dtype → separate compiled kernel\n \n graph break → eager fallback on that op\n\n \n \n FUSION CUTS HBM TRIPS\n\n \n Eager — 3 op-by-op kernel launches\n \n add\n \n mul\n \n relu\n\n \n \n HBM — slow global memory\n\n \n \n \n \n \n \n\n 6 HBM read+write trips per fwd pass\n\n \n \n\n \n Compiled — 1 fused Inductor kernel\n \n SRAM — intermediates stay on-chip\n \n add → mul → relu (one kernel)\n 2 HBM trips: 1 read + 1 write\n\n \n \n ~3× fewer HBM trips\n Inductor fuses pointwise chains and\n emits a single fused Triton kernel\n\n \n \n Compile modes\n default — general-purpose fusion\n reduce-overhead — wraps in CUDA graphs\n max-autotune — autotuned Triton configs\n fullgraph=True — error on any graph break\n longer compile = faster inference runtime\n\n \n Graph breaks — what causes them\n print() or Python side effects\n data-dependent control flow (if tensor)\n unsupported ops (custom C++ extensions)\n in-place mutation on graph inputs\n torch._dynamo.explain(model) to diagnose\n\n \n What Inductor produces\n GPU: fused Triton kernels (OpenAI Triton)\n CPU: C++ + OpenMP with SIMD vectorization\n Typical speedup: 1.5–3× over eager\n Same model weights, same numerical outputs\n — only the execution path changes.\n Compilation result cached to disk across runs\n\n`\n\nRead torch.compile` through a capture-then-fuse lens rather than a magic-speed-flag lens: Dynamo decides how much of your Python it can turn into a graph, AOTAutograd extends that to the backward pass, and Inductor turns the result into the same Triton kernels you could have written by hand — so the two things you actually control are how many graph breaks you leave in the hot path and how often guards force a recompile.

torch compiletorchcompiletorch.compiletorch inductortorchinductorpytorch compilertorchdynamotorch dynamoaot autogradaotautogradprimtorchgraph breakpytorch 2.0pytorch 2pytorch performance optimization

Explore 500+ Semiconductor & AI Topics

From EUV lithography to CUDA optimization — search the full knowledge base or chat with our AI assistant.