2、核心架构差异:TensorRT的图优化引擎 vs TensorRT-LLM的流式批处理引擎

好,咱们直接切入正题。很多人刚接触这两个东西时,第一反应是:“TensorRT-LLM不就是TensorRT加了个大模型支持吗?”

嗯,这个想法其实挺危险的。我刚开始也这么以为,结果在项目里踩了不少坑。说白了,它们的核心架构设计哲学完全不同。一个是为静态图做极致优化,另一个是为动态生成做流式处理。今天我就把这两个引擎的底裤扒开,给你看个明白。

2.1 TensorRT的图优化引擎:静态图的“雕刻家”

TensorRT的核心,我习惯叫它“图优化引擎”。它的工作方式,你可以想象成一个雕刻家——拿到一块完整的石头(计算图),然后一刀一刀地雕琢,直到变成完美的艺术品。

具体来说,它做了这几件事:

  • 层融合(Layer Fusion):把连续的卷积、BN、ReLU合并成一个kernel。我在项目中遇到过,一个ResNet50的模型,光这一步就能减少30%的显存访问。
  • 精度校准(INT8/FP8):通过校准数据集,把FP32的权重映射到INT8。嗯,这里要注意,校准集选不好,精度会崩。
  • 内核自动调优(Kernel Auto-Tuning):针对你的GPU型号,暴力搜索最优的kernel配置。我记得有一次,同一个模型在A100和V100上,TensorRT选出来的kernel完全不同。

核心特点:整个优化过程是离线完成的。你输入一个ONNX模型,它输出一个序列化的plan文件。推理时,直接加载plan文件执行,不再做任何优化。

你想想看,这种架构的好处是什么?确定性。一旦plan文件生成,每次推理的路径都是固定的,延迟非常稳定。但坏处也很明显——它假设你的输入形状是固定的。如果你的batch size或者序列长度变了,对不起,重新优化一遍吧。

2.2 TensorRT-LLM的流式批处理引擎:动态生成的“指挥家”

到了大模型时代,情况完全变了。LLM的推理不是一次性的前向传播,而是一个自回归生成过程:每次生成一个token,然后把新的token拼回去,再跑一次。

这就带来了两个核心挑战:

  1. 变长序列:每个请求的输入长度和输出长度都不一样。
  2. 动态批处理:不同请求可能处于生成的不同阶段,有的刚进来,有的快结束了。

TensorRT-LLM的流式批处理引擎,就是专门为这个场景设计的。它不像TensorRT那样把整个图优化好再执行,而是在推理过程中动态调度

我举个例子你就明白了:

// TensorRT-LLM的流式批处理伪代码
while (有未完成的请求) {
    // 1. 收集当前所有活跃请求的输入
    batch = gather_active_requests(requests);
    
    // 2. 动态填充(Padding)到当前batch的最大长度
    padded_batch = dynamic_padding(batch);
    
    // 3. 执行一次前向传播
    outputs = model.forward(padded_batch);
    
    // 4. 对每个请求,采样下一个token
    for each request in requests {
        next_token = sample(outputs[request]);
        request.append(next_token);
        
        // 5. 如果请求生成了终止符,把它移出batch
        if (next_token == EOS) {
            requests.remove(request);
            // 释放显存,给新请求用
        }
    }
    
    // 6. 如果有新请求进来,加入batch
    new_requests = accept_new_requests();
    requests.extend(new_requests);
}

看到了吗?每一步都在动态调整batch的组成。这就是流式批处理引擎的核心——它不是一个固定的执行计划,而是一个持续运行的调度循环。

个人经验:我在部署一个70B的模型时,刚开始用静态batch,显存利用率只有40%。换成流式批处理后,利用率直接飙到85%以上。说白了,静态batch是在“等”最慢的那个请求,流式batch是在“抢”每一个空闲的计算单元

2.3 核心差异对比:一张表说清楚

维度 TensorRT 图优化引擎 TensorRT-LLM 流式批处理引擎
优化时机 离线(编译时) 在线(推理时)
输入形状 固定(需要指定max batch/size) 动态(支持变长序列)
批处理策略 静态批处理(所有请求同时开始,同时结束) 动态批处理(请求随时加入/离开)
显存管理 预分配固定大小的显存池 按需分配,支持KV Cache的Page管理
适用场景 CV、传统NLP(BERT等) 大语言模型(GPT、LLaMA等)
延迟特性 低且稳定 受batch组成影响,有波动

2.4 架构对比图:一图胜千言

下面这张SVG图,我画了两个引擎的核心流程对比。左边是TensorRT的静态图优化,右边是TensorRT-LLM的流式批处理。你一看就明白。

核心架构差异对比 TensorRT 图优化引擎 输入:ONNX模型(静态图) 离线优化阶段 层融合 → 精度校准 → 内核调优 输出:序列化Plan文件 推理时:直接加载Plan执行 特点:固定输入形状 所有请求同时开始/结束 TensorRT-LLM 流式批处理引擎 输入:多个请求(变长序列) 在线调度循环(持续运行) 收集活跃请求 → 动态Padding 前向传播 → 采样 → 移出/加入 动态批处理:请求随时进出 KV Cache Page管理 按需分配显存 特点:支持变长序列 每个请求独立生成

2.5 避坑指南:我曾经踩过的坑

最后,分享几个我在实际项目中遇到的坑,希望能帮你少走弯路:

坑1:用TensorRT跑LLM

我曾经试过把GPT的整个计算图导出成ONNX,然后用TensorRT优化。结果呢?模型文件大了10倍,而且每次输入长度变化都要重新编译。折腾了两周,最后放弃了。说白了,TensorRT不适合自回归生成场景

坑2:忽略流式批处理的显存碎片

刚开始用TensorRT-LLM时,我发现随着服务运行时间变长,显存碎片越来越严重。后来才知道,KV Cache的Page管理需要合理配置。我建议你根据最大并发数,提前预留好Page池的大小。

我的建议:如果你在做CV或者BERT类的模型,直接用TensorRT就好,简单高效。但如果你在搞LLM,别犹豫,直接上TensorRT-LLM。它的流式批处理引擎,就是为这个场景量身定做的。

好了,这一章的核心内容就这些。记住一句话:TensorRT优化的是“图”,TensorRT-LLM调度的是“流”。理解了这个本质区别,后面的内容你就知道该怎么选了。


公众号:蓝海资料掘金营,微信deep3321