干翻显存黑洞:vLLM 凭什么成了大模型推理的扛把子?横评 TRT-LLM / SGLang / TGI / llama.cpp

七十亿、几百亿参数往显存里一倒,卡时如流水。更要命的是,显卡动不动就报 OOM(Out of Memory)趴窝。很多团队刚上手那会儿,以为吞吐量低是因为算力卡不够,拼命加机器。其实路走歪了。卡住脖子的,多半不是算力,是那堆被浪费得不成样子的键值缓存(KV Cache)。

大模型这玩意儿,训出来是一回事,跑线上撑住并发又是另一回事。

七十亿、几百亿参数往显存里一倒,卡时如流水。更要命的是,显卡动不动就报 OOM(Out of Memory)趴窝。很多团队刚上手那会儿,以为吞吐量低是因为算力卡不够,拼命加机器。其实路走歪了。卡住脖子的,多半不是算力,是那堆被浪费得不成样子的键值缓存(KV Cache)。

vLLM 当年一出来就杀疯了,靠的就是把操作系统的虚拟内存搬到了显卡里。

不过现在江湖早就变了。英伟达亲儿子 TensorRT-LLM 在后面狂追,专门为复杂提示词而生的 SGLang 杀将出来,还有老牌的 TGI 和跑在笔记本上的 llama.cpp

今天把这几个主流引擎扒开底子,一次性讲透。


1. 🧮 显存里到底卡着什么鬼?

搞大模型推理,逃不开两个截然不同的步骤:一个是把用户的输入一整截吞进去(Prefill / Prompt 阶段),另一个是吐字,一个词一个词往外挤(Decode 阶段)。

问题全出在挤字这一步。

每挤出一个新词,模型就得回头去翻前面所有词的键值向量。为了不重复算,这些向量全得留在显存里,这就是 KV Cache

KV 缓存(Key-Value Cache)
自回归模型每生成一个新 Token,都需要将自注意力机制中的 Key 和 Value 矩阵保存在显存里。随着对话轮数变多、并发量飙上去,KV Cache 占用的显存会迅速反超模型本身的权重文件,成为最先挤爆显存的吃卡大户。

传统做法很粗暴:只要来了一个请求,管你真正聊几句,显卡先划出一块连着的、够塞 4096 甚至 8192 个词的巨大显存地皮。

结果呢?

有人问一句“今天天气怎么样”,模型五句话答完。剩下的几千个槽位就这么干晾着。别的请求进不来,自己又占着茅坑不拉屎。以前显存的有效利用率,经常连三成都不到。全是气泡和碎渣。

#### 破局之手:PagedAttention vLLM 的这帮伯克利作者干了一件极聪明的事:把 Linux 操作系统的分页内存照搬过来了。

PagedAttention(分页注意力机制)
不再要求显存必须整块连在一起。它把 KV 缓存切成固定大小的小格子(物理块,一般是 16 或 32 个 Token)。你的对话在逻辑上连贯,在显存里随便找空位塞。调度器手里拿着一张“块表(Block Table)”,随时查随时拼。显存碎片率直接被按死在 4% 以下。

逻辑输入 (Tokens 0-31):  [ 块 0 (0-15) ] ──────► [ 块 1 (16-31) ]
                              │                        │
查找表 (Block Table):         │ 塞进物理块 #7          │ 塞进物理块 #2
                              ▼                        ▼
显存池 (GPU RAM):         [ 物理块 #7 ] ······ [ 物理块 #2 ] ······ [ 随便哪个空位 ]

数学公式写出来看着吓人,骨子里其实就是带索引的查表点积:

\[A_{ij} = \frac{\exp\left( \frac{Q_i K_j^T}{\sqrt{d_k}} \right)}{\sum_{t \in \mathcal{K}_{\text{paged}}} \exp\left( \frac{Q_i K_t^T}{\sqrt{d_k}} \right)}, \quad K_j = \text{Lookup}(j, \text{BlockTable})\]

显存不用再整块空等。吞吐量当场翻了两到四倍。

#### 顺手解决的另一个痛点:连续批处理 以前批处理叫静态 Batch。八个人一起问,必须等废话最多的那个说完,大家才能同时退场,蠢得很。

连续批处理(Continuous / In-Flight Batching)
把颗粒度拆到单个生成步骤(Iteration)。哪个请求蹦出结束符了,立马踢出去结账,腾出空位直接把排队的新人塞进来。流水线一秒钟都不空转。

2. ⚔️ 五大门派切磋:直接上硬核对比

光吹原理没用,工程落地看的是实打实的取舍。

在硬件通用性、极致吞吐量与开发自由度的权衡中,五大主流推理引擎展现出截然不同的架构偏向。

核心维度vLLM (伯克利)TensorRT-LLM (英伟达)SGLang (LMSYS)TGI (Hugging Face)llama.cpp (纯C游侠)
底层硬功夫Python + C++ / Triton 内核纯 C++ / CUDA (暴力榨取)Python + FlashInfer / TritonRust 前端 + Python/CUDA纯 C / C++ (毫无依赖)
KV 显存管理PagedAttention (物理分页)专有 In-Flight KV 分页池RadixAttention (基数树前缀)PagedAttention 移植版静态环形缓冲
多轮前缀复用APC (哈希单链匹对)静态 Prompt Cache多路前缀树分支共享 (独一档)基础前缀匹配靠手动拼接对齐
手写算子融合FlashAttention-2/3, Triton手写 CUTLASS (天花板级别)FlashInfer, Triton, FlashAttnFlashAttention, AWQ自研 CPU SIMD / ggml
并发吞吐峰值⭐️⭐️⭐️⭐️ (极高,通用王者)⭐️⭐️⭐️⭐️⭐️ (极值,英伟达原生)⭐️⭐️⭐️⭐️½ (超高,复杂提示词更猛)⭐️⭐️⭐️½ (中规中矩)⭐️⭐️ (单机/边缘低并发)
首字时延 (TTFT)⭐️⭐️⭐️⭐️ (很快)⭐️⭐️⭐️⭐️⭐️ (快到变态)⭐️⭐️⭐️⭐️⭐️ (命中树节点时起飞)⭐️⭐️⭐️½ (偏稳)⭐️⭐️⭐️ (CPU 算力所限)
上手部署难度🟢 舒服 (pip 一装直接当 OpenAI 用)🔴 痛苦 (编译折腾,踩坑无数)🟢 顺手 (Pythonic,好改好调)🟡 中等 (Docker 镜像拉起即用)🟢 极爽 (单文件扔进去就能跑)
非 N 卡异构能力AMD ROCm, Intel Gaudi, TPU 都能跑买英伟达全家桶才给伺候AMD ROCm 适配中ROCm, Gaudi 部分支持纯跨界 (Mac Metal, CPU 通吃)

3. 🥊 贴脸硬刚:三场关键战役

脱离开实际场景谈引擎优劣,纯属耍流氓。

#### 第一战:vLLM vs. TensorRT-LLM 这俩本质上是“软件工程效率”“硬件极致性能”的决斗。

TRT-LLM 是英伟达自家人写的。这帮人对 GPU 底层寄存器、共享内存摸得太透了。他们把算子拼了命地融在一起(Kernel Fusion),省去了一大堆在显存来回倒腾数据的开销。

算子融合(Kernel Fusion)
把前后好几道工序(比如矩阵乘法算完紧接着算偏置和激活函数)揉进同一个 GPU 计算核里干完。不用把半成品写回大显存再重新读出来。省下的访存带宽,全变成了真金白银的速度。

在固定的英伟达显卡集群上跑成熟模型,TRT-LLM 确实能比 vLLM 多榨出 15% 到 35% 的吞吐量。

可惜,代价太大了。

配置复杂,编译繁重。模型架构要是稍微冷门点,或者开源社区今天刚蹦出一个新结构,TRT-LLM 根本跑不起来。改动一行代码,重新编译能让你喝半天咖啡。

vLLM 就讨喜得多。跟 Hugging Face 贴得死紧,社区一出新模型,过两天甚至当天下午就能无缝拉起。改源码调参更是轻轻松松。

#### 第二战:vLLM vs. SGLang 这一仗非常精彩。

vLLM 当初把精力全扑在“怎么把单条请求切碎放好”上了。它的自动前缀缓存(APC)就是单线哈希,对付普通多轮对话还行。

但在做 Agent 循环、思维链推演、少样本(Few-shot)或者硬性让模型输出 JSON 格式时,请求根本不是一条直线,而是一张到处分叉的大树。

SGLang 直接祭出了基数树(Radix Tree)。

RadixAttention(基数树注意力缓存)
把所有跑过的内容用前缀树(Radix Tree)管起来。十个不同的 Agent 共享同一个上万字的长设定,SGLang 在内存里只留一份公共根节点。后续生成各自长出枝丫;显存吃紧时,按 LRU 算法先砍掉最末梢的树叶,树干永远留在显存里。

在多 Agent 和结构化生成场景下,SGLang 靠着惊人的缓存命中率,把首字延迟(TTFT)打得极低。实测比 vLLM 还要顺滑得多。

#### 第三战:vLLM vs. llama.cpp 完全不是一条道上的选手。

llama.cpp 简直是单机与本地极客的福音。纯 C++ 撸穿一切,配合量化好的 GGUF 文件,往 MacBook 或普通台式机上一放,不吃显存也能跑得飞快。

但它从设计之初就没打算扛住机房里成千上万的高并发请求。

数据中心成排的机器要打满利用率,还得老老实实上 vLLM 这种重型装甲车。


4. 📐 账本算一算:为什么算力账单总超标?

很多架构师做预算,往往只按模型参数来算显存。大错特错。

全系统实际消耗的 KV Cache 显存容量,是由下面这个公式死死钉住的:

\[M_{\text{KV}} = 2 \times L_{\text{layer}} \times N_{\text{head}} \times D_{\text{head}} \times S \times B \times P_{\text{bytes}}\]

这里头:

  • \(L_{\text{layer}}\) 是模型层数;
  • \(N_{\text{head}}\) 是注意力头数量;
  • \(D_{\text{head}}\) 是每个头的维度;
  • \(S\) 是上下文平均长度;
  • \(B\) 是同一秒扛住的并发数;
  • \(P_{\text{bytes}}\) 是精度(FP16 占 2 字节,FP8 占 1 字节)。
看明白了吧?并发数 \(B\) 和上下文长度 \(S\) 只要往上翻番,KV Cache 的开销就呈线性暴涨。

显卡总显存就那么大,扣掉模型权重和临时计算空间,剩下的地盘全留给 KV 缓存池了。

显存碎片惩罚因子
传统系统里由于内存无法分页,必须乘上一个高达 1.6 到 2.0 的碎片浪费系数(\(\epsilon_{\text{frag}}\));vLLM 把这个系数降到了 1.04。也就是说,同样的物理硬件,vLLM 能把安全并发上限 \(B_{\max}\) 凭空往上推两到三倍。

这就是省钱的全部秘密。


5. 🎯 怎么选?一张图说明白

别跟风,看你的真实业务长在哪:

  • 图省心、保通用、团队节奏快:闭眼选 vLLM。坑最少,社区最热闹,文档最齐。
  • 做复杂 Agent、结构化抽取、长上下文多轮互动:果断切 SGLang。试过基数树缓存的人基本都回不去了。
  • 业务规模庞大、模型几个月都不换一次、手里全是 H100/H200:咬着牙上 TensorRT-LLM。这才是压低单 Token 边际成本的终极武器。

📚 参考文献与真实凭据

1. Kwon, W., Li, Z., Zhuang, S., Sheng, Y., Zheng, L., Yu, C. H., ... & Stoica, I. (2023). *Efficient memory management for large language model serving with PagedAttention*. In Proceedings of the 29th ACM Symposium on Operating Systems Principles (SOSP '23), pp. 611-626. (vLLM 原理奠基之作). 2. Yu, G. I., Jeong, J. S., Kim, G. W., Kim, S., & Chun, B. G. (2022). *Orca: A distributed serving system for Transformer-Based generative models*. In 16th USENIX Symposium on Operating Systems Design and Implementation (OSDI '22), pp. 521-538. (迭代级调度 Continuous Batching 开山论文). 3. Zheng, L., Yin, L., Xie, Z., Sun, C., Huang, C. H., Yu, C. H., ... & Stoica, I. (2024). *SGLang: Efficient Execution of Structured Language Model Programs*. arXiv preprint arXiv:2312.07104. (RadixAttention 基数树前缀缓存规范). 4. NVIDIA Corporation (2023–2026). *TensorRT-LLM Architecture and In-Flight Batching Developer Guide*. NVIDIA Docs. 5. Gerganov, G. (2023–2026). *llama.cpp: High-performance inference of LLaMA model in pure C/C++*. GitHub.

👍 1

想参与讨论或点赞?登录后使用完整功能

讨论回复(0)

暂无回复,登录后可参与讨论
合作

智谱 GLM-5 已上线

在智谱开放平台 BigModel.cn 打造 AI 应用。新一代旗舰模型 GLM-5 在推理、代码、智能体综合能力达到开源模型 SOTA。

领取 2000万 Tokens