Serving a large model on a GPU is a different sport from running one on a laptop: the difference between a demo and a production endpoint is measured in tokens per second per dollar, and every millisecond of kernel launch overhead and every wasted byte of KV cache shows up on the invoice. TensorRT-LLM is NVIDIA’s answer to that invoice. The repository β€” Apache-2.0-licensed, targeting Python 3.10 and 3.12 on CUDA 13.4 β€” bundles specialized kernels for the operations that dominate transformer math, a compiled C++ runtime for scheduling and cache management, and a pythonic framework on top so you never have to write CUDA to benefit from it. A 1.4 release candidate, the project sits underneath a large slice of production deployments β€” it is the engine behind several hosted-model vendors and NVIDIA’s reference implementation for each new GPU generation, from Hopper tensor cores to Blackwell attention paths.

The architecture is best understood as one API bolted onto two engines. The front door is the LLM class in tensorrt_llm/llmapi/llm.py β€” under two thousand lines that hide process spawning, multi-GPU coordination, and engine selection behind a constructor and a generate() call. Its arguments funnel into llm_args.py, a six-thousand-line configuration module that normalizes every knob from quantization to parallelism strategy. Requests then flow into one of two execution paths: a compiled C++ engine reached through the Python bindings in executor/executor.py, or a pure-PyTorch path in tensorrt_llm/_torch/ where the model, the scheduler, and the KV cache are ordinary Python objects you can read and subclass. Around both, a serving ring offers an OpenAI-compatible HTTP server, a gRPC surface, a CLI, and disaggregation coordinators that split prefill and decode across machines. As always in this series, what follows is an educational tour of published source code.

TensorRT-LLM overview architecture diagram

TensorRT-LLM at a glance: the CLI launches the OpenAI server, which drives the LLM facade; LlmArgs normalizes configuration; the Executor submits requests into either the compiled C++ engine or the Torch-native path; the C++ side schedules micro-batches over a paged KV cache, while the Torch side loads weights into a model zoo with quantization fused in.

Reading the overview from left to right:

Why You Need This

The first reason is the configuration discipline, which is the part most projects get wrong. Every option a user can set β€” tensor parallelism, expert parallelism, cache size, quantization recipe, speculative decoding strategy, CUDA graph batch sizes β€” enters through one choke point: the LLM class collects them, and LlmArgs reconciles them into a consistent world state. That file is enormous for a reason: it encodes the compatibility matrix of the entire system, which backends require which cache layouts, which parallelism combinations are legal on which GPU counts, and which defaults change when a model is multimodal. Because the validation lives in one place, the engines below can trust what they receive, and the same file computes derived quantities β€” cache sizes from GPU memory, worker counts from parallelism settings β€” so operators describe intent while the system derives the physics. Config as a validated, centralized contract rather than kwargs scattered across twelve constructors: that alone is worth the visit.

The second reason is the Torch-native path, which is where the project became genuinely readable. In tensorrt_llm/_torch/, the model engine implements the canonical inference loop β€” schedule requests, run a forward pass, sample tokens, append to cache, repeat β€” in ordinary Python you can step through with a debugger. The model loader resolves checkpoints, applies sharding for multi-GPU layouts, and fuses quantization during load; the model zoo holds hand-optimized implementations of Llama, DeepSeek, Qwen and peers. Performance comes from pluggable parts rather than monoliths: the attention backends let a model run on FlashInfer, TensorRT-LLM’s own kernels, or FlashMLA depending on hardware; CUDA graph capture eliminates launch overhead on the hot decode path; speculative decoding contributes draft-and-verify strategies such as EAGLE, Medusa, and lookahead; and the MoE directory handles expert routing with fused communication. Reading this half teaches how the tricks you see in papers translate into a production loop.

The third reason is the compiled engine underneath, which shows what the same problem looks like when interpreted Python is no longer fast enough. Below the bindings of the Python Executor, the C++ side models the world explicitly: the request class carries tokens, sampling parameters, and stopping criteria; the micro-batch scheduler forms batches each iteration while the capacity scheduler decides what fits given live KV pressure. The KV cache manager runs paged allocation with eviction and reuse, and the data transceiver ships cache blocks between processes β€” the substrate for disaggregated serving, where prefill runs on one machine and decode on another. The memory layer in the C++ runtime supplies tensor buffers, IPC, multicast groups, and NCCL communicators, and the kernel directories β€” kernels/, flash_mla/, deep_gemm/, deep_ep/, CUTLASS extensions β€” hold the device code behind each attention and MoE call. Around everything sits the serving tier: the OpenAI server β€” thirty-five hundred lines of routes, tool handling, and streaming β€” with protocol schemas, a gRPC alternative under grpc/, the serve CLI, and the disaggregation coordinator that matches prefill and decode workers into pairs.

TensorRT-LLM detail architecture diagram

The detail view: the Python API layer with facade, config, tokenizer, reasoning parser and MPI sessions; the Torch-native engine from scheduler and CUDA graphs through attention backends, speculative decoding, MoE and disaggregation; the C++ core from request lifecycle through micro-batch scheduling, paged cache and KV transport; and the serving tier of OpenAI routes, coordinators, CLI and gRPC.

Walking the detail diagram, one request is traceable end to end. A client posts chat messages to the OpenAI server, which validates them against the protocol schemas and hands a prompt to the LLM facade; the reasoning parser can split chain-of-thought channels from final answers mid-stream, and the MPI session has already arranged the worker processes for a multi-GPU layout. The facade submits through the Executor bindings, and the two engines tell one story twice. On the Torch path, the model engine asks the scheduler which requests run, replays a captured CUDA graph for the decode step, and manages pages in the Torch KV cache. On the C++ path, the same semantics live in compiled classes with the same names and sharper edges, connected by explicit C++ types instead of Python objects.

The second walkthrough worth taking is disaggregation, because it shows a systems design turned into working code. The coordinator server registers prefill and decode instances and matches them into pairs; the disaggregation coordinator tracks load and routes accordingly; and the transport β€” the C++ data transceiver on the compiled path, the Torch disaggregation module on the other β€” moves KV blocks so a prompt processed on machine A continues generation on machine B without recomputation. The insight repeats everywhere: the fast path is compiled, the control path is Python, and the boundary between them is a thin, named interface β€” a pattern even the agent-flavored scaffolding controller follows for multi-step workloads.

From Install to Your First Model

The supported route is NVIDIA hardware and either a container or pip. The pip route is pip install tensorrt-llm --extra-index-url https://pypi.nvidia.com, with CUDA 13 runtime and a compatible driver on the box; the container route pulls a prebuilt image with every CUDA dependency pinned. Your first lines are the whole point of the facade: from tensorrt_llm import LLM, then llm = LLM(model="meta-llama/Llama-3.2-1B-Instruct"), then out = llm.generate(["Explain quantum tunneling in one sentence."]) β€” the constructor builds the engine, spawns workers if parallelism demands it, and loads weights. For chat, llm.chat([...]) applies a model’s chat template, and the streamed variant returns tokens as they arrive. To serve instead of script, trtllm-serve serve starts the OpenAI-compatible endpoint assembled by the serve CLI from a YAML config, and any OpenAI client can point at it unchanged. Quantized checkpoints, speculative decoding, and expert parallelism are constructor arguments, not code changes β€” flip the option, restart, measure.

Honest limits: this is NVIDIA-only in practice β€” the kernels target CUDA, and the meaningful performance story requires recent datacenter GPUs, so nothing here runs on Apple silicon or consumer AMD cards. The configuration surface is deep enough that wrong combinations surface as exotic failures rather than clean errors, and the project moves quickly enough that API details shift between minor versions β€” the 1.4 line was still a release candidate while writing. The compiled path builds slowly and debugging it requires C++ and CUDA tooling, and the dual-engine design means some features are ahead on one path than the other. None of that changes the verdict: for anyone serving models on NVIDIA GPUs, TensorRT-LLM’s source is the most complete public map of what production inference actually spends its time on.

Watch PyShine on YouTube

Contents