2. TensorRT-LLM 基础
在深入动态批处理调优之前,必须先建立对 TensorRT-LLM 整体框架的清晰认知。TensorRT-LLM 并非一个简单的模型加载器,而是一套面向大语言模型推理的编译型执行引擎。它将用户定义的模型结构(通常来自 PyTorch 或 HuggingFace)转换为高度优化的 TensorRT 引擎,并在运行时通过一组精心设计的组件管理 GPU 资源、KV Cache 与请求调度。理解其分层架构与数据流,是后续所有调优动作(如 inflight batching、KV Cache 复用、显存池调整)的前提。
2.1 整体架构:从模型定义到引擎执行
TensorRT-LLM 的架构可以划分为三个逻辑层次:前端模型定义层、编译优化层与运行时执行层。前端层负责将模型描述为统一的 Module 结构;编译层通过 trtllm-build 命令将模型图转换为可执行的引擎文件;运行时层则加载引擎,并通过 GptSession 与 Executor 对外提供推理服务。下图展示了这一核心架构及推理请求的主要流转路径。
上图清晰地展示了三个层次之间的依赖关系。前端层将模型结构、权重与配置参数统一交给编译层;编译层经过图优化、算子融合与内存规划后,产出包含可执行 Kernel 序列的引擎文件;运行时层中的 Executor 负责接收外部请求,将其放入队列,并依据当前 GPU 资源状态决定哪些请求可以立即执行。理解这一分层有助于定位性能瓶颈:例如,若吞吐量低但 GPU 利用率不高,问题往往出在运行时调度策略;若 GPU 利用率高但单请求延迟大,则需检查编译层的 Kernel 选择是否最优。
2.2 核心组件:Executor、GptSession 与 KV Cache Manager
在运行时层中,有三个组件对动态批处理调优至关重要。它们各自承担不同职责,且相互协作方式直接影响系统整体吞吐。
- Executor(执行器):对外提供
enqueue_request()与await_responses()接口。它内部维护一个请求优先级队列,并周期性触发调度决策。Executor 是动态批处理(inflight batching)策略的实际执行者,它决定哪些请求的 prefill 阶段与 decode 阶段可以交错执行。 - GptSession(会话管理器):负责跟踪每个请求的生成状态,包括当前已生成的 token 数、KV Cache 的引用位置、采样参数(temperature、top_p)等。GptSession 将请求拆分为“上下文阶段(prefill)”与“生成阶段(decode)”,并协调引擎执行。
- KV Cache Manager(缓存管理器):这是显存利用效率的核心。它维护一个可复用的显存块池,每个块存储特定层、特定 token 位置的 Key 和 Value 张量。动态批处理要求 KV Cache 能够按需分配与释放,因此该管理器采用分页式管理(类似操作系统虚拟内存),将逻辑 KV Cache 映射到物理显存块。
max_num_tokens 参数决定了 KV Cache 管理器可分配的最大 token 总数。该值设置过小会导致显存利用率不足,设置过大则可能因预留过多显存而限制并发批大小。通常建议根据实际请求长度分布动态调整,而非简单取最大值。
2.3 推理流程:从请求进入到 Token 输出
一次完整的推理请求在 TensorRT-LLM 内部经历以下阶段。理解该流程有助于识别动态批处理插入点。
- 请求接收与校验:Executor 接收包含 prompt、采样参数、请求 ID 的请求对象,并检查其合法性(如序列长度是否超过
max_seq_len)。 - KV Cache 分配:KV Cache Manager 根据请求的预估长度(通常由
max_new_tokens决定)分配逻辑块,并映射到物理显存。若显存不足,请求将进入等待队列。 - Prefill(上下文处理):引擎对 prompt 进行并行处理,生成第一个 token 的 logits,并填充 KV Cache。此阶段计算密集,GPU 利用率高。
- Decode(自回归生成):逐 token 生成。每步仅处理一个 token,但需要读取全部历史 KV Cache。此阶段访存密集,GPU 利用率通常较低。
- 采样与终止判断:根据 logits 与采样参数选择下一个 token。若达到
max_new_tokens或触发 EOS,则结束生成。 - 资源释放与响应返回:释放该请求占用的 KV Cache 块,将完整生成序列返回给调用方。
动态批处理(inflight batching)的核心思想是:在 decode 阶段,当某些请求因等待采样或显存不足而暂停时,插入其他请求的 prefill 阶段,从而填补 GPU 计算空隙。这要求 Executor 能够精细控制每个请求在 prefill 与 decode 之间的切换时机。
2.4 典型场景与配置示例
以下展示一个最小化的 TensorRT-LLM 引擎构建与推理配置示例,帮助读者将上述组件与具体参数对应起来。
# 1. 构建引擎(编译层)
trtllm-build \
--model_dir ./llama-7b-hf \
--output_dir ./engine \
--max_batch_size 64 \
--max_input_len 2048 \
--max_seq_len 4096 \
--max_num_tokens 8192 \
--dtype float16 \
--use_inflight_batching
# 2. 运行时初始化(Python API)
from tensorrt_llm import LLM, SamplingParams
llm = LLM(
engine_dir="./engine",
max_batch_size=64, # 与构建时一致
max_num_tokens=8192, # KV Cache 池大小
enable_inflight_batching=True
)
# 3. 发送请求
prompts = ["解释动态批处理原理", "写一段快速排序代码"]
outputs = llm.generate(prompts, SamplingParams(max_new_tokens=256))
在上述配置中,max_num_tokens=8192 是动态批处理调优的关键旋钮。它表示 KV Cache 池最多容纳 8192 个 token 位置。若请求平均长度为 512 token,则理论上可同时处理约 16 个请求(8192/512)。但实际中,由于 prefill 与 decode 交错,有效并发数会动态变化。
2.5 常见误区与复盘
| 误区 | 现象 | 正确理解 |
|---|---|---|
认为 max_batch_size 越大越好 |
显存溢出或 KV Cache 分配失败 | batch 大小受限于 KV Cache 池与计算资源,需与 max_num_tokens 联动调整 |
| 忽略 prefill 与 decode 的资源差异 | GPU 利用率波动大,存在明显气泡 | prefill 计算密集,decode 访存密集,动态批处理正是为了混合两者以填满 GPU |
| 将 KV Cache 视为固定显存开销 | 长请求导致后续请求排队 | KV Cache 是分页管理的,请求结束后立即释放,可被后续请求复用 |
max_batch_size 而忽略 max_num_tokens。实际上,后者决定了 KV Cache 池的总容量,是限制并发请求数量的更直接因素。建议先通过 profiling 工具观察实际 KV Cache 峰值使用量,再反推合理的 max_num_tokens 值,避免盲目设置。