加速计算专家团队 薛博阳
trtllm-serve)用户可以通过几行代码尝试 PyTorch 工作流。
# Import LLM API
from tensorrt_llm.torch import LLM
# Create a LLM object
llm = LLM(model="./Llama-3.1-8B-Instruct")
# Prepare prompts
prompts = [
"Hi, pls tell me something about reasoning model",
"Hi, pls tell me something about TensorRT-LLM"
]
# Generate output
output = llm.generate(prompts)
PyTorch workflow
* LLM API 的设计思路借鉴自 vLLM 团队。
更多示例和参数:
更多带有附加参数的示例可在 examples/pytorch/quickstart_advanced.py 中找到。
LLMArgs: 模型路径、tokenizer、张量并行度、量化等...
LLM()。PyTorchConfig: 用于 PyTorch 的附加配置,例如 CUDA graph 和后端选择等。
下图展示了 TensorRT-LLM 中 TensorRT 工作流和新的 PyTorch 工作流的整体架构。
两个工作流共享相同的上层 Serving 和 API 接口,但在 Runtime 和 Modeling 层面有所不同。
此工作流需要手动进行模型转换和引擎构建,并使用 Python 包装器调用 C++ 运行时来构建新模型。
这是一个基于 LLM API 的单步工作流,底层使用 TensorRT。它通过 Python 包装器简化了新模型的构建,并使用带有 Python 绑定的 C++ 运行时。
这是基于 LLM API 的单步工作流,底层使用 PyTorch。它采用基于 PyTorch 的模型 API 来构建新模型,并通过重用模块化的 C++ 运行时来执行。
PyTorch 工作流专注于易用性和灵活性,其路径如下图高亮部分所示:
该流程从 LLM (Torch) API 开始,通过 PyExecutor 和模块化的运行时接口,调度 PyTorch Engine 执行。模型层 (torch.nn.Module) 可以使用 PyTorch 原生算子、自定义算子以及复用底层的 TRT-LLM Kernels。
TensorRT-LLM 的代码结构清晰地划分了不同功能模块。
llm.py 模块继承了 LLM API,是 PyTorch 工作流的用户入口。
pyexecutor/ 目录包含了 Python 运行时的实现。
模型定义相关代码位于多个模块中,包括:
* attention_backend/: 实现了多种 Attention 后端,如 Vanilla, flashinfer, TRT-LLM, StarAttention。
* models/: 使用 PyTorch 模块实现各种模型。
* modules/: 包含构成模型的基本 PyTorch 模块,如 Linear, Norm, Attention, MLP, MoE 等。
tensorrt_llm/_torch/: 包含所有 PyTorch 工作流的代码。__init__.py: Python 模块初始化。attention_backend/: Vanilla, flashinfer, TRT-LLM, StarAttention。compilation/: 与 torch.compile 相关。custom_op/: 自定义算子注册。distributed/: allreduce, allgather, reducescatter。llm.py: 继承 LLM API。metadata.py: 现在用于 KVCache 元数据。model_config.py: pretrained_config, device mapping, quant_config, attn_backend, moe_backend。models/: 使用 PyTorch 模块实现模型。modules/: PyTorch 模块: Linear, Norm, Attention, MLP, MoE 等。peft/: LoRA 支持。pyexecutor/: Python runtime。speculative/: eagle3, mpt, ...下图展示了基于 PyTorch 的建模在整个系统架构中的位置。它位于底层,负责模型的定义和执行,并与上层的 Python 运行时、C++ 运行时以及服务层(如 Triton Inference Server)进行交互。
整个流程分为以下几个层次:
之前,TensorRT-LLM 提供了一套类似 PyTorch 的 API 来使用 TensorRT 开发模型。
tensorrt_llm.Module 对应 torch.nn.Moduletensorrt_llm.functional 对应 torch.nn.functionaltensorrt_llm.Tensor 对应 torch.TensorTRT Plugins 对应 torch.ops.trtllm这使得模型开发比使用原生的 TensorRT API 更简单。
tensorrt_llm/torch/models/modeling_XXX 中进行定制。下图展示了模型的层次结构:
从外到内依次是:
1. PyTorchModelEngine
2. DecoderModelForCausalLM
3. LMHead
4. DecoderModel
* Embedding
* RMSNorm
* DecoderLayer x N
* RMSNorm
* Attention
* MLP
文档链接: https://nvidia.github.io/TensorRT-LLM/torch/adding_new_model.html
input_tensor: tensorrt_llm.Tensor
# Slicing (results in a new Tensor)
sliced_tensor = slice(input_tensor, starts=[1, 0], sizes=[2, 2])
# Indexing
indices = constant(np.array([0, 2], dtype=np.int32))
gathered_tensor = gather(input_tensor, dim=0, indices=indices)
# Boolean masking
mask = gt(input_tensor, 5)
masked_tensor = masked_select(input_tensor, mask)
# Unary Op
abs_tensor = input_tensor.abs() # Does not support abs()
* 每个操作都必须产生新的 Tensors。 * 这依赖于图优化来高效执行。
input_tensor: torch.Tensor
# Slicing (creates a view, materialized when needed)
sliced_tensor = input_tensor[1:3, 0:2]
# Indexing
indexed_tensor = input_tensor[[0, 2]]
# Boolean masking
masked_tensor = input_tensor[input_tensor > 5]
# Unary Op generating a new Tensor
abs_tensor = input_tensor.abs()
# Unary Op with in-place modification
abs_tensor = input_tensor.abs_()
* 在可能的情况下创建张量的“视图”(views),仅在需要时物化新的张量。 * 命令式编程更加自然。
tensorrt_llm.functional 实现了 LLM 推理中最常见的功能。def softmax(input: Tensor, dim: Optional[int] = None) -> Tensor:
axes = dim_to_trt_axes(dim)
layer = default_trtnet().add_softmax(input.trt_tensor)
layer.axes = axes
return_create_tensor(layer.get_output(0), layer)
torch.nn.functional 提供了多样的操作。def softmax(input: torch.Tensor, dim: Optional[int] = None) -> torch.Tensor:
return F.softmax(input, dim=dim)
TensorRT plugin: 创建一个插件类。
class Fp4GemmPlugin : public BasePlugin有大量的样板代码(10+个成员函数)需要填写。
Fp4GemmPlugin(...)Fp4GemmPlugin(const void*, size_t)~Fp4GemmPlugin()clone()getOutputDimensions(...)supportsFormatCombination(...)configurePlugin(...)getWorkspaceSize(...)enqueue(...)getOutputDataType(...)核函数调用在 enqueue 中。
Torch op: 只是一个带有 Python 绑定的 C++ 函数。
TORCH_LIBRARY_IMPL(trtllm, cuda, m)m.impl("fp4_gemm", &torch_ext::fp4_gemm);at::Tensor fp4_gemm(at::Tensor const& mat1, ...)shape, dtype, data_ptr 等。TensorRT plugin
PyTorch op
实现一个单元测试是很痛苦的。
为一个 Module 定义两个主要方法:
__init__forwardtensorrt_llm.layers
class RmsNorm(Module):
def __init__(self, ...):
# ...
if self.elementwise_affine:
self.weight = Parameter(shape=self.normalized_shape, dtype=dtype)
else:
self.register_parameter('weight', None)
self.eps = eps
self.dtype = dtype
def forward(self, x, ...):
weight = None if self.weight is None else self.weight.value
if self.normalized_shape is None:
normalized_shape = self.normalized_shape
return rms_norm(x, normalized_shape, self.num_groups, weight, self.eps)
class RmsNorm(nn.Module):
def __init__(self, ...):
super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size, dtype=type))
self.variance_epsilon = eps
def forward(self,
hidden_states: torch.Tensor,
residual: Optional[torch.Tensor] = None
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
if IS_FLASHINFER_AVAILABLE:
from ...custom_op import flashinfer_fused_add_rmsnorm
# ...
if residual is not None:
flashinfer_fused_add_rmsnorm(hidden_states, residual,
self.weight, self.variance_epsilon)
return hidden_states, residual
# ...
调试一个张量的值,例如 MLP 的输出。
在 TensorRT 模块中:
output = self.proj(inter)
self.register_network_output('mlp_output', output)
* 使用标志构建 TensorRT 引擎。
trtllm-build ... --enable_debug_output
* 在 \1 中为调试模式开启。
runner_kwangs = dict(debug_mode=True, ) # ...
model_runner = ModelRunner.from_dir(**runner_kwags)
* 在 \1 中捕获输出张量。
if self.debug_mode:
#...
print(self.debug_buffer['transformer.layers.0.mlp_output'])
output = self.down_proj(inter)
print(output.shape, output[0])
* 可以在 IDE 中使用断点。
tensorrt_llm._torch.modules 包含用于 LLM 的常见构建块,包括:
Linear (线性层)
RMSNorm
MLP
up_gate_projFused_moe
EP
Attention
qkv_proj and o_projDecoderLayer
TensorRT 工作流: 继承 (Inheritance)
init 之后执行。PyTorch 工作流: 组合 (Composition)
load_weights 处理 TP, GEMM fusion, 量化等。Linear.load_weights 以统一的方式处理权重加载。其参数是模块所需的权重张量。
model.layers.0.self_attn.qkv_proj 需要:model.layers.0.self_attn.q_proj.weightmodel.layers.0.self_attn.k_proj.weightmodel.layers.0.self_attn.v_proj.weight仅当启用 TP 时才加载相关分片(shard)的权重。
weight.shape=[4096, 4096], tp_size=4, tp_rank=1, tp_mode=COLUMNweight[1024:2048, :] (注意:第一个维度是 PyTorch 中的 out_features)所需的 qkv 分片被融合并复制到 Parameter 中。
FP4: E2M1
双重量化 (Double quantization)
由 ModelOpt 支持的量化
激活量化是动态的
与 FP8s 相比
在 _create_weights (由 load_weights 调用) 中声明模块的参数。
class Linear(nn.Module):
def _create_weights(self):
# Quantized weights
self.weight = Parameter(torch.empty(
[self.out_features, self.in_features // 2],
dtype=fp4_utils.float4_e2m1x2,
device=device),
requires_grad=False)
# FP8 per-block scaling factors
self.weight_scale = # ...
# FP32 per-tensor global scaling factor = 448x6 / amax_input
self.input_scale = # ...
self.inv_input_scale = # ...
# (amax_input*amax_weight) / (448*6*448*6)
self.alpha = # ...
self.profiler = torch.classes.trtllm.FP4GemmRunner.get_instance(
self.dtype)
self.needs_profiling = True
正确加载权重缩放因子
- 例如,对于FP4模型,model.layers.0.self_attn.qkv_proj也会接收到:
- model.layers.0.self_attn.q_proj.weight_scale
- `model.layers.0.self_attn.q_proj.weight_scale_2`
- `model.layers.0.self_attn.q_proj.input_scale`
- 这同样适用于 k_proj 和 v_proj。
代码片段(Page 31)展示了load_weight_scales_nvfp4函数,它处理权重的加载,并在拼接后对权重缩放因子进行重排。
实现 apply_linear
- 动态激活量化
- 提供静态全局SF和BF16激活。
- 返回FP4量化的激活和FP8块状SFs。
代码片段(Page 32)展示了apply_linear函数的实现。该函数首先进行性能分析,然后使用torch.ops.triton.fp4_quantize对激活进行量化,并最终调用run_gemm执行GEMM操作。
AttentionBackend接口来替换Attention的实现。class AttentionBackend(Generic[Metadata]):
def forward(self,
q: torch.Tensor,
k: Optional[torch.Tensor],
v: Optional[torch.Tensor],
metadata: TMetadata,
*,
attention_mask: AttentionMask = PredefinedAttentionMask.CAUSAL,
**kwargs) -> torch.Tensor:
可用的后端:
trtllm: 使用专有核,gptAttentionPlugin被封装为PyTorch操作,提供最佳性能。vanilla: 使用torch的SDPA(Scaled Dot-Product Attention),实现简单但速度较慢。FlashInfer: 使用FlashAttention、RoPE Fusion等进行优化。StarFlashInfer: 基于FlashInfer的定制化后端(由NV Research发明)。可以通过PyTorchConfig.attn_backend进行切换。
tensorrt_llm._torch.modules中的Embedding, FusedMoE和Linear模块。Attention模块。Qwen3MoeConfig中的参数来初始化超类。
代码片段(Page 34)展示了Qwen3Moe和Qwen3Attention类的__init__方法,演示了如何使用模型配置来初始化模型组件。
代码片段(Page 35)展示了Qwen3MoeDecoderLayer和Qwen3MoeModel的forward方法,说明了数据在模型层级间的流动过程。
DecoderModel和LogitsProcessor(即LMHead,可能包含后处理)组成。
代码片段(Page 36)展示了Qwen3MoeForCausalLM的实现,它将Qwen3MoeModel和LogitsProcessor结合起来,用于因果语言建模任务。
DecoderModelForCausalLM.load_weights是加载Hugging Face检查点权重到PyTorch模块的入口点。Linear模块的load_weights方法。class DecoderModelForCausalLM(nn.Module,
def load_weights(self,
params_map = {
'qkv_proj': ['q_proj', 'k_proj', 'v_proj'],
'gate_up_proj': ['gate_proj', 'up_proj']
}
Qwen3MoeForCausalLM中就重写了此方法。tensorrt_llm/models/qwen/http://model.py: ~500 行代码 (LOC)
tensorrt_llm/models/qwen/http://convert.py: ~1200 LOC
examples/models/core/qwen/http://convert_checkpoint.py: ~300 LOC
convert.py中的函数。tensorrt_llm/_torch/models/http://modeling_qwen3_moe.py: ~350 LOC
无额外的检查点转换代码
注意:这并非严格的苹果对苹果比较。例如,1200行的convert.py包含了一些TensorRT工作流不再使用的遗留代码。这只是为了展示PyTorch工作流的代码库更加清晰。
下图展示了运行时在整个系统架构中的位置,重点突出了Python运行时及其与C++组件的交互。
上图(Page 41)展示了整个系统的架构。请求从Triton推理服务器或OpenAI服务器进入,可能通过Dynamo,最终到达基于PyTorch的LLM。系统分为服务层、API层、运行时层和建模层。运行时层包含Python实现的GenerationExecutor和PyExecutor,并与C++实现的调度器和KV缓存管理器交互。建模层基于torch.nn.Module,并利用PyTorch原生及自定义操作,底层调用TRT-LLM核。
上图(Page 42)详细展示了Python运行时的组件结构。PyExecutor是顶层组件。模块化的Python运行时接口定义了ModelEngine, RequestScheduler, BaseResourceManager和Decoder等核心抽象。Python运行时实现了这些接口,例如PyTorchModelEngine和SimpleScheduler。底层则调用C++实现的组件,如tensorrt_llm::batch_manager中的调度器和KV缓存管理器。
ResourceManager
KVCache ManagerScheduler
Capacity Scheduler
MicroBatchScheduler
当前许多模块通过Python绑定使用C++运行时组件。
_executor_loop
准备 (Prepare)
启动 (Launch)
ModelEngine一步。采样器 (Sampler)
_executor_loop_overlap
上图(Page 44)展示了CPU和GPU在执行器循环中的工作流。通过重叠CPU任务(准备、处理)和GPU任务(计算、采样),可以有效隐藏CPU开销,提升整体效率。
*重叠调度器的想法归功于SGLang团队: https://lmsys.org/blog/2024-12-04-sglang-v0-4/
仅用于纯生成(generation-only)步骤
ModelEngine决定是否为当前调度的请求使用CUDA Graph。
DecoderModelForCausalLM的forward()方法将被捕获和重放。将为每个预定义的批处理大小(batch sizes)[1, 2, 3, 4, ..., 32, 64, 128]捕获一个CUDA Graph。
cuda_graph_padding_enabled为True时,它将被填充到最接近的大小。加速调试过程
加速新功能启用周期
减少繁琐的检查点转换和引擎构建时间。
在 NVIDIA GPU 上实现 DeepSeek R1 SOTA 性能的基础
更多关于 DeepSeek R1 性能优化的细节将在未来的会议中分享。
上图(Page 39)展示了在8个H20 GPU上,输入和输出序列长度均为1k时的性能表现。比较了DP8EP8(数据并行)和TP8EP8(张量并行)两种配置下的"每GPU输出吞吐量"与"每用户输出吞吐量"的关系。
Commit: a4c3359513dae5694a2a01955abffb7702b004ab
*仅用于技术讨论
PyTorch 工作流为开箱即用(OOTB)用户带来了易用性
对开发者也更加友好
由于能够快速添加新功能的灵活性,其性能可以与现有的 TRT 工作流相媲美,甚至更好。
TensorRT 和 PyTorch 工作流现在将共存
新模型/功能的支持将在 PyTorch 工作流中优先考虑。
请遵循使用 TensorRT-LLM 的说明,并在 GitHub issues 上留下您的评论:
征集贡献(您将得到 TensorRT-LLM 团队的支持,将您的贡献合入 GitHub 仓库):
PyTorch 后端的特性对齐
TensorRT-LLM 中推理时计算支持的任务
GitHub仓库:
https://github.com/NVIDIA/TensorRT-LLM
加入 NVIDIA 开发者 Discord 社区