大模型推理引擎对比 vLLM / TensorRT-LLM / SGLang

本文对比三款主流大模型推理引擎 vLLM 0.6.3、TensorRT-LLM 0.13.0 与 SGLang 0.4.1。从架构原理(PagedAttention 与连续批处理、编译期图优化、RadixAttention 与前端 DSL)切入,给出 Llama-3-8B 与 70B 在 A100 与 H100 上的吞吐与 TTFT 对比表,详解显存与并发关键参数,并提供选型决策表与客户端代码。

把大模型跑起来容易,把大模型跑得又快又省显存很难。HuggingFace Transformers 的朴素实现吞吐低、显存浪费大,根本无法支撑生产流量。于是专门为 LLM 设计的推理引擎成为 LLMOps 基础设施的核心一环。目前工业界使用最广的三款是 vLLM、TensorRT-LLM 与 SGLang,它们各自代表了一条技术路线。

本文先讲清三者的架构差异,再用可比的基准数据说明性能差距,最后给出参数调优与选型建议。理解这三款引擎,是做好 LLMOps 全景 中部署与成本两环的前提。

为什么通用框架撑不起生产

先量化问题。以一个 13B 模型在 A100 80GB 上的朴素推理为例:

  • 显存被 KV Cache 大量占用,且因预分配最大长度而产生严重碎片,实测可用显存利用率常常只有 20% 到 40%。
  • 请求必须等最长的那个生成完才能返回,短请求被长请求阻塞。
  • 批大小固定,无法随请求到达动态调整。

这三个问题分别对应三个工程答案:分页式 KV Cache 管理、连续批处理、动态调度。三大引擎都在这三点上做了优化,只是实现路径不同。

vLLM 架构解析

vLLM 是伯克利团队开源的推理引擎,当前稳定版本为 0.6.3。它的两大支柱是 PagedAttention 与连续批处理。

PagedAttention 显存管理

PagedAttention 借鉴操作系统的虚拟内存分页思想,把 KV Cache 切分为固定大小的 block(默认 16 个 token),通过 block table 建立逻辑序列到物理 block 的映射。

这一设计带来三个收益:

  • 显存碎片从朴素实现的 60% 到 80% 浪费,降到不足 4%。
  • 相同显存下可以容纳更多并发序列,吞吐显著提升。
  • 支持前缀共享,相同系统提示的请求可以共享物理 block。

其原理细节在 连续批处理与 PagedAttention 中有完整推导。

连续批处理与调度

vLLM 的调度器在每次迭代(iteration)级别决定哪些序列进入本批次。当一个序列生成结束,它占用的槽位立即被新到达的请求填补,而不是等待整个批次结束。这使 GPU 计算单元几乎时刻保持饱和。

调度器还实现了 preemption 机制:显存不足时,低优先级序列会被换出(swap 到 CPU)或重算(recompute),等显存释放后再恢复。

vLLM 的取舍

vLLM 的强项是易用性与通用性,几乎所有主流模型开箱即用,社区生态最大。它的短板在极端优化场景:对特定硬件的算子级调优不如 TensorRT-LLM 深入,复杂控制流(如结构化生成)需要额外集成。

TensorRT-LLM 架构解析

TensorRT-LLM 是 NVIDIA 官方推出的推理库,当前版本 0.13.0。它的核心思想是编译期优化:把模型图在部署前就编译成针对特定 GPU 架构深度优化的引擎。

编译期图优化

TensorRT-LLM 的编译流程包含:

  • 算子融合(将 LayerNorm、Attention、FFN 的多个算子合并)
  • 精度校准(FP8、INT8、INT4 的量化与校准)
  • 内核自动调优(针对具体 GPU 型号选择最优实现)
  • 内存规划(静态分配显存,减少运行时开销)

编译产出的引擎是高度特化的,换 GPU 型号需要重新编译,这是它的灵活性代价。

In-flight Batching

TensorRT-LLM 的 In-flight Batching 与 vLLM 的连续批处理目标一致,都是让批次在运行时动态重组。区别在于它构建在高度优化的 CUDA 内核之上,配合 paged KV Cache 支持,能在同等硬件上压榨出更高的利用率。

TensorRT-LLM 的取舍

它的强项是极致性能与 NVIDIA 生态整合,尤其在 H100 上配合 FP8 能取得最佳吞吐。短板是编译流程复杂、调试困难、对非 NVIDIA 硬件不适用,且模型支持滞后于社区。

SGLang 架构解析

SGLang 当前版本 0.4.1,最初来自 LMSYS 团队。它的两大特色是 RadixAttention 与前端 DSL。

RadixAttention

RadixAttention 用一个基数树(radix tree)管理 KV Cache,使得任意长度的公共前缀都能被复用。这比 vLLM 的 block 级前缀共享更细粒度。

典型受益场景:

  • 多轮对话,历史前缀被反复复用。
  • Few-shot 提示,所有请求共享同一段示例。
  • 树状推理(如思维链分支、Beam Search),分支间共享前缀。

实测在强前缀共享场景下,SGLang 的缓存命中率与吞吐可以显著超过 vLLM。

前端 DSL

SGLang 提供了一套 Python 前端 DSL,让复杂生成流程(分支、循环、并行、约束解码)用声明式代码表达,并在运行时自动利用前缀共享。这让它在 Agent 与结构化生成场景中特别顺手。

SGLang 的取舍

强项是高并发前缀共享与复杂生成流程。短板是生态规模小于 vLLM,部分长尾模型的适配不如 vLLM 及时。

性能对比

以下数据为公开基准与实测的综合参考,硬件配置为单机 8 卡。数值随驱动、CUDA 版本、模型量化方式波动,请以自身环境实测为准。

Llama-3-8B,输入 512 token,输出 256 token:

引擎硬件并发吞吐 tokens/sTTFT P50TTFT P99
vLLM 0.6.3A100 80G64245045ms120ms
vLLM 0.6.3H100 80G64390032ms85ms
TensorRT-LLM 0.13.0A100 80G64290038ms95ms
TensorRT-LLM 0.13.0H100 80G (FP8)64520022ms60ms
SGLang 0.4.1A100 80G64260040ms100ms
SGLang 0.4.1H100 80G64410028ms72ms

Llama-3-70B,TP=4,输入 1024 token,输出 512 token:

引擎硬件并发吞吐 tokens/sTTFT P50TTFT P99
vLLM 0.6.34x A100 80G32780180ms420ms
vLLM 0.6.34x H100 80G321450120ms300ms
TensorRT-LLM 0.13.04x H100 80G (FP8)32210095ms240ms
SGLang 0.4.14x H100 80G321520115ms290ms

从数据可以看出几条规律:

  • H100 相对 A100 在同等配置下有 1.5 到 1.6 倍吞吐提升。
  • TensorRT-LLM 在 H100 加 FP8 的组合下优势最明显,可达 vLLM 的 1.3 倍以上。
  • SGLang 在前缀共享强的场景(如多轮对话)会进一步拉开差距,上表为弱共享基准。
  • 70B 模型必须张量并行,TTFT 明显高于 8B,这是显存带宽与通信开销共同决定的。

更系统的硬件与引擎基准方法可参考 推理基准测试 。

关键参数详解

无论用哪个引擎,下面这些参数都是调优的核心。以 vLLM 的命名为主,其他引擎有对应概念。

参数含义推荐值影响
gpu-memory-utilization预留给模型的显存比例0.9太高易 OOM,太低浪费并发
max-num-seqs单批次最大序列数256提高吞吐但增加 TTFT
max-model-len最大上下文长度按业务设定直接决定 KV Cache 上限
tensor_parallel_size张量并行度按显存需求跨卡切分,引入通信开销
enable-chunked-prefill分块预填充开启降低长提示对 TTFT 的冲击
max-num-batched-tokens单批最大 token 数8192与 chunked prefill 配合
block-sizeKV Cache 块大小16影响碎片与调度粒度

几个要点:

  • gpu-memory-utilization 0.9 是常用起点,但要在真实负载下压测。设太高会导致长上下文请求 OOM,设太低则并发受限。
  • max-num-seqs 256 适合中等规模并发。盲目调高会让单请求 TTFT 上升,因为批越大,每个序列分到的算力越少。
  • max-model-len 与显存强相关。8B 模型在 80GB 卡上,max-model-len 从 8K 提到 32K 会显著压缩可用并发数。
  • enable-chunked-prefill 对长提示场景几乎是必开项,它把长预填充拆成多个 chunk 插入批次,避免长提示阻塞短请求。

选型决策

选型不是选最强,而是选最合适。下表给出决策路径:

场景推荐引擎理由
快速上线、模型种类多vLLM开箱即用,社区支持最好
追求极致吞吐、只用 NVIDIATensorRT-LLM编译优化与 FP8 收益最大
多轮对话、Agent、结构化生成SGLangRadixAttention 前缀复用强
需要 FP8 且硬件是 H100TensorRT-LLM 或 vLLM两者都支持,前者更优
需要跨云或非 NVIDIA 硬件vLLM兼容性最好
已有大量自定义 CUDA 算子TensorRT-LLM编译期融合更彻底

一个常见的实践是混合部署:主流量走 vLLM 保证稳定,性能敏感的核心路径走 TensorRT-LLM。但混合部署会带来运维复杂度,中小团队应谨慎。

实战 启动命令与客户端

下面给出 vLLM 与 SGLang 的启动命令,以及一个通用的 OpenAI 兼容客户端。

vLLM 启动 Llama-3-8B,单卡,开启 chunked prefill:

python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Meta-Llama-3-8B-Instruct \
  --served-model-name llama-3-8b \
  --tensor-parallel-size 1 \
  --gpu-memory-utilization 0.9 \
  --max-num-seqs 256 \
  --max-model-len 8192 \
  --enable-chunked-prefill \
  --max-num-batched-tokens 8192 \
  --port 8000

vLLM 启动 Llama-3-70B,四卡张量并行:

python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Meta-Llama-3-70B-Instruct \
  --served-model-name llama-3-70b \
  --tensor-parallel-size 4 \
  --gpu-memory-utilization 0.92 \
  --max-num-seqs 128 \
  --max-model-len 16384 \
  --enable-chunked-prefill \
  --port 8000

SGLang 启动,利用 RadixAttention:

python -m sglang.launch_server \
  --model-path meta-llama/Meta-Llama-3-8B-Instruct \
  --tp-size 1 \
  --mem-fraction-static 0.88 \
  --max-running-requests 256 \
  --context-length 8192 \
  --port 30000

TensorRT-LLM 的典型流程是先构建引擎再启动服务:

trtllm-build --checkpoint_dir ./llama3-8b-ckpt \
  --output_dir ./llama3-8b-engine \
  --gemm_plugin float16 \
  --use_paged_context_fmha enable \
  --max_batch_size 256 \
  --max_input_len 8192 \
  --max_seq_len 10240

python -m tensorrt_llm.serve \
  --engine_dir ./llama3-8b-engine \
  --port 8000

统一的 OpenAI 兼容客户端,可同时指向任意一个引擎:

import time
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")


def bench(prompt: str, n: int = 32) -> dict:
    ttfts, tps = [], []
    for _ in range(n):
        t0 = time.perf_counter()
        stream = client.chat.completions.create(
            model="llama-3-8b",
            messages=[{"role": "user", "content": prompt}],
            stream=True,
            temperature=0.0,
            max_tokens=256,
        )
        first = None
        out = 0
        for chunk in stream:
            if chunk.choices and chunk.choices[0].delta.content:
                if first is None:
                    first = time.perf_counter()
                out += 1
        ttfts.append((first - t0) * 1000)
        tps.append(out / (time.perf_counter() - t0))
    ttfts.sort()
    return {
        "ttft_p50_ms": round(ttfts[len(ttfts) // 2], 1),
        "ttft_p99_ms": round(ttfts[-1], 1),
        "tokens_per_s": round(sum(tps) / len(tps), 1),
    }


if __name__ == "__main__":
    print(bench("用三句话解释 PagedAttention 的原理。"))

这段客户端对三个引擎通用,因为 vLLM、SGLang 与 TensorRT-LLM 都提供 OpenAI 兼容接口。这也意味着切换引擎的成本主要在下游部署,而非上游应用代码。

进阶优化技术

三大引擎在基础调度之上,还共享一批进阶优化。理解这些技术有助于判断某个引擎是否适合你的场景。

投机解码

投机解码(Speculative Decoding)用一个小的草稿模型先快速生成若干候选 token,再由大模型并行验证。由于大模型的前向是并行的,验证多个候选的成本接近验证一个,因此在草稿命中率高时能显著加速。

  • 草稿模型质量是关键,命中率低于 60% 时收益可能为负。
  • vLLM 支持 --speculative-model 参数指定草稿模型。
  • 另一种变体是 n-gram 或 EAGLE 草稿,无需额外模型。

前缀缓存

前缀缓存让相同前缀的请求复用已计算的 KV Cache。三个引擎的实现粒度不同:

  • vLLM 以 block 为粒度做前缀共享。
  • SGLang 的 RadixAttention 以任意长度公共前缀为粒度。
  • TensorRT-LLM 在 paged KV Cache 上支持前缀复用。

在系统提示固定、少样本示例固定的应用中,前缀缓存能省掉大量预填充计算,直接降低 TTFT。

分块预填充

长提示的预填充会一次性占用大量算力,阻塞正在解码的短请求。分块预填充(Chunked Prefill)把长预填充拆成多个小块,与解码请求混批执行,从而平滑 TTFT 的长尾。vLLM 的 --enable-chunked-prefill 即此功能。

量化推理

量化把权重与激活从 FP16 降到 FP8 或 INT8、INT4,减少显存占用并提升算力利用率。FP8 在 H100 上有原生支持,收益最大。INT4 主要用于显存极度受限的场景,需要评估质量损失。

精度显存占用(相对 FP16)典型加速质量影响
FP16100%基线无
FP850%1.3 到 1.8 倍极小
INT850%1.2 到 1.6 倍较小
INT425%1.5 到 2.0 倍需评估

量化细节可参考 FP8 推理专题。

预填充解码分离

预填充阶段是算力密集的,解码阶段是显存带宽密集的。把两者放在同一张卡上,会互相干扰。预填充解码分离(PD Disaggregation)把两个阶段部署到不同的实例,各自用最优配置,从而同时提升吞吐与降低 TTFT。这是当前大厂在生产环境的主流方向,但实现复杂,中小团队可暂缓。

生产部署形态

引擎选定后,部署形态同样影响最终表现。

单机单卡

最简单,适合 8B 级别模型与中小流量。瓶颈是显存与单卡算力。

单机多卡张量并行

70B 级别模型的标配。张量并行把权重切到多卡,代价是每层都要做 AllReduce 通信。卡间带宽(NVLink 远优于 PCIe)直接决定扩展效率。

多副本横向扩展

单实例吞吐到顶后,横向加副本,前接负载均衡。注意副本间的前缀缓存不共享,若要共享需要外部 KV 缓存服务。

与 Kubernetes 结合

生产环境通常用 Kubernetes 编排推理服务,配合 GPU 调度、健康检查与自动扩缩容。这部分涉及 GPU 共享与弹性伸缩,属于另一个专门的工程领域。

部署形态对比

形态适用规模优点缺点
单卡8B 以下简单、无通信开销显存与算力受限
张量并行70B 级单实例可跑大模型通信开销、扩展性受限
多副本高并发线性扩展吞吐缓存不共享、成本线性增长
PD 分离大规模生产吞吐与延迟兼优架构复杂、运维成本高

硬件与引擎匹配

引擎的能力上限受硬件约束。同一引擎在不同 GPU 上的表现差异,往往大于不同引擎在同一 GPU 上的差异。

GPU显存适用模型规模引擎建议备注
A100 40G40GB7B 到 13BvLLMFP8 支持有限
A100 80G80GB13B 到 34BvLLM、SGLang性价比高
H100 80G80GB34B 到 70BTensorRT-LLM、vLLMFP8 原生支持
H200 141G141GB70B 以上TensorRT-LLM大显存优势明显
L40S 48G48GB7B 到 13BvLLM推理性价比之选

匹配原则:

  • 显存决定单卡能放多大的模型与多长的上下文。
  • 算力(FLOPS)决定预填充速度,影响 TTFT。
  • 显存带宽决定解码速度,影响 TPOT。
  • 卡间带宽(NVLink 与 PCIe 的差距可达数倍)决定张量并行的扩展效率。

很多团队在升级到 H100 后期待吞吐线性增长,但若模型瓶颈在显存带宽而非算力,提升会低于预期。做容量规划时,先判断当前瓶颈在哪一类资源。

常见坑清单

坑一 只看吞吐不看 TTFT

吞吐与延迟是一对矛盾。把 max-num-seqs 拉到很大可以刷高吞吐,但单请求 TTFT 会明显变差。生产环境必须两个指标一起看。

坑二 gpu-memory-utilization 设得过高

0.95 以上的设置看似能榨出更多显存,实则把 OOM 风险留给了长上下文请求。建议从 0.9 起步,压测后再微调。

坑三 忽略 KV Cache 显存预算

max-model-len 设得过大,会让 KV Cache 上限吃光显存,导致并发数骤降。要根据业务真实上下文长度设定,而不是一律拉满。

坑四 张量并行度拍脑袋

张量并行度应与模型大小和卡数匹配。70B 用 TP=8 在 8 卡机上可行,但若换成 TP=4 会 OOM;反过来 8B 用 TP=4 纯属浪费,还引入不必要的通信开销。

坑五 用错量化格式

FP8 在 H100 上收益显著,但在 A100 上支持有限。INT4 能省显存但可能损害质量。量化选择要与硬件和精度要求一起评估,可参考本组的量化与显存专题。

坑六 不做前缀缓存规划

如果应用有大量共享系统提示,却没开启前缀缓存,等于白白浪费算力。多轮对话场景尤其要注意,可参考本组的 KV Cache 专题。

坑七 忘记压测真实流量分布

基准测试常用均匀的输入长度,而真实流量是长尾分布。用真实流量回放压测,才能暴露长提示阻塞等调度问题。

坑八 忽视版本升级的行为变化

推理引擎版本迭代快,升级可能改变默认调度策略或量化行为,导致性能与输出发生非预期变化。升级前应在影子环境跑一遍回归评测。

坑九 把基准数字当成承诺

厂商标称的吞吐往往基于最优输入长度与最大批。把它当作容量规划的依据会严重高估。容量规划应基于自身流量分布的实测值。

版本演进与兼容性

推理引擎的版本迭代速度远快于传统中间件,理解版本差异对稳定性很重要。

引擎本文版本关键能力节点升级注意
vLLM0.6.3连续批处理成熟、支持 chunked prefill注意调度参数默认值变化
TensorRT-LLM0.13.0FP8 与 paged KV 完善引擎需随驱动与 CUDA 重编
SGLang0.4.1RadixAttention 与前端 DSL 稳定前端 DSL 偶有破坏性变更

升级策略建议:

  • 锁定版本,避免生产环境被自动升级。
  • 建立升级回归流程,覆盖性能基准与质量评测。
  • 关注引擎的发布说明,尤其是默认参数与 API 的变化。
  • 对 TensorRT-LLM,把编译产物纳入版本管理,保证可复现。

小结

三大引擎代表三条技术路线:vLLM 以 PagedAttention 与连续批处理实现通用高效,易用性与生态最好;TensorRT-LLM 以编译期图优化与 In-flight Batching 追求极致性能,代价是编译复杂与硬件绑定;SGLang 以 RadixAttention 与前端 DSL 在强前缀共享与复杂生成场景中占优。

选型的关键是把场景说清楚:模型种类多、要快速上线选 vLLM;性能极致、硬件统一选 TensorRT-LLM;多轮对话与 Agent 选 SGLang。无论选哪个,参数调优的核心都是吞吐与延迟的平衡,以及显存预算的精确规划。

理解了引擎层之后,下一步应该深入 PagedAttention 与连续批处理的内部机制,见本组的连续批处理专题;显存与量化的进一步优化见量化与显存专题。

最后要提醒的是,引擎只是整条链路的一环。再强的引擎,如果被上游的低效检索或下游的串行调用拖累,端到端体验依然糟糕。把引擎选型放在整体架构中权衡,而不是孤立地追求单点性能,才是正确的做法。

换句话说,推理引擎的优化上限由架构决定,而架构的合理性由业务场景决定。先想清楚业务,再谈引擎。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「LLMOps」更多文章

  1. 语义缓存与 Prompt 缓存
  2. 结构化输出与函数调用
  3. 多智能体编排与工作流引擎