HF-24工程级15 min

SGLang vs vLLM vs llama.cpp:单用户速度之外,你真正该选谁

从单用户与服务器吞吐量的本质差异出发,对比 SGLang / vLLM / llama.cpp 的核心武器:Continuous batching、PagedAttention、RadixAttention、Prefix caching。给出本地 Agent、RAG 客服、多 Agent 编排三类场景的选型清单。

SGLangvLLMllama.cppContinuous batchingPagedAttentionRadixAttentionPrefix caching高并发

你有没有发现一个很奇怪的现象:同一个大模型,用 llama.cpp 跑一个人聊天,一秒能跑 50 个 token;换成 vLLM 或 SGLang,单用户可能还是 50 个 token,甚至有时候还没 llama.cpp 快。可一旦走进企业服务器、AI Agent、RAG 多人聊天的场景,社区反而清一色倒向 vLLM 和 SGLang。

因为这里真正比的,已经不是一个人能跑多快,而是 100 个人同时来的时候,GPU 还能不能高效率地干活。

单用户速度和服务器吞吐量,是两回事。

一、先把概念摆正:Latency vs Throughput

先想象一个饭店。llama.cpp 很像一个优秀的小炒厨师:你一个人点菜,他专门服务你,炒得又快又好。所以个人电脑、本地模型、单用户 Agent 体验上它常常无可挑剔。

但当突然来了 20 桌客人,你不能让厨师第一桌炒完再炒第二桌——服务器需要的是同时把 20 桌订单组织起来,让厨房流水线一直满载运转。

vLLM 和 SGLang 从一开始就是按这种推理服务器的思路设计的。这里的核心指标也由此切换:

Latency vs Throughput

Latency 是一个用户等多久才能拿到第一个 token;Throughput(吞吐量)是整台机器每秒或每分钟能产出多少 token。多人并发场景下,后者才是真正决定业务的指标。个人聊天时你盯着首字延迟,多人服务时你盯着整张卡的 token/min。

二、第一个关键武器:Continuous Batching

假设有三个用户同时在线:A 正在生成第 100 个 token,B 刚刚输入了一篇 5000 token 的文章等待 prefill,C 正在生成第 20 个 token。最简单的做法是一个一个来,A 没结束,B、C 就排队。但这样 GPU 利用率并不好。

Continuous batching(连续批处理)的做法像公交车:不用等第一批乘客全部到终点、下一批才能上车,而是有人下车,新的乘客立刻补进来。

连续批处理就像公交车——有人下车,新的人立刻补进来。

这时你会发现一个反直觉的现象:多用户一起跑,并不等于把一个人的速度简单除以 N。因为 GPU 非常擅长并行计算,一个用户生成时大量 SM 单元根本没吃满。把多个请求组成 batch,反而能让 GPU 同时计算多个序列。于是单个用户可能慢了一点,但整台服务器每秒产出的 token 总数反而大幅提高。

这就是 vLLM 和 SGLang 运行时调度器最基本的形态——让 GPU 持续保持满载,而不是等某个长请求拖着所有人。

三、高并发的真正瓶颈:不是算力,是 KV Cache

很多人以为并发上不去是 GPU 算力不够,其实限制并发的是显存。具体来说,是 KV Cache。

Transformer 在推理时每一层都要为当前序列缓存 K/V 张量,序列越长、用户越多,KV Cache 占用的显存就线性增长。

一个具体的算账:48GB 显卡

假设你部署 Qwen-32B 这种规模的模型,模型权重就要占掉 30GB 显存,剩下 18GB。一段普通对话可能需要 1GB KV Cache:

1 个用户聊天需要 1GB;10 个用户就是 10GB;20 个用户,18GB 显存很快就要告罄。

高并发真正麻烦的地方不是 GPU 算不动,而是这么多人的 KV Cache 到底怎么放。

四、PagedAttention vs RadixAttention:显存怎么放

vLLM 的核心武器是 PagedAttention。传统做法是给每个用户划一块连续的大显存——A 预留 4GB,B 预留 4GB,C 预留 4GB。但 A 实际只用了 800MB,剩下的位置别人不方便利用,时间一长显存就出现大量碎片和浪费。

PagedAttention 的思路跟操作系统虚拟内存非常像:不要一次给你一整栋楼,而是一页一页分,你用多少我给多少。

PagedAttention 的思路跟操作系统虚拟内存非常像——不要一次给你一整栋楼,而是一页一页分。

KV Cache 按 block 分页管理后,显存利用率大幅提升。同样 48GB,原来只能勉强塞 10 个人,现在能更高效地塞下更多人。所以 PagedAttention 最重要的意义,不是让一个人从 50 token 变成 100 token,而是让显存能容纳更高的并发。

SGLang 的 RadixAttention:跨用户共享 KV

SGLang 在 PagedAttention 之上又多了一招——RadixAttention。它解决的是:不同用户之间有没有重复的 KV Cache 可以直接共享?

案例:企业 RAG 客服的 8000 token system prompt

假设公司部署 RAG 客服系统,100 个客户进来,前面的 system prompt 全都一样——"你是某某科技的 AI 客服"+ 产品说明 + 售后规则 + 知识库摘要,假设一共 8000 tokens。用户 A 问价格,B 问配置,C 问售后,问题不同,但前面 8000 tokens 高度相同。

普通做法是 A prefill 一遍,B 再 prefill 一遍,C 再 prefill 一遍——大量重复计算。RadixAttention 把公共前缀组织成一棵基数树,相同部分只缓存一份 KV,分叉之后再各自走各自的分支。

这 8000 tokens 我刚刚算过,别算第二遍。

五、Prefix Caching 与调度器:腾出来的 GPU 才是真省钱

Prefix caching 的价值不只是让单次请求快一点。Prefill 本身非常吃计算资源——假设你有 50 个 Agent,每个都挂着 2 万 token 的公共上下文,每次请求都重新 prefill 的话,GPU 大量时间都在重复阅读同样的东西。

公共 KV Cache 能复用的话,很多请求只需要计算新增的那一小段,GPU 腾出来了,同一张卡自然能服务更多请求。

还有一个容易忽略的核心:调度器

服务器里 30 个请求正在 decode,5 个 4 万 token 的长 prompt 在排队,10 个还在等待——如果调度器很笨,突然塞进一个 4 万 token 的 prefill,正在 decode 的用户会瞬间被卡住。

所以高性能框架会做 chunked prefill、请求调度优先级,甚至把 prefill 和 decode 分离到不同的实例上。这已经不是把模型加载到显卡里跑,而是在做 GPU 资源调度。

六、三大框架的定位与选型

那么 llama.cpp 是不是就不适合多并发?并不是。现在的 llama.cpp server 也有 parallel slots、continuous batching、prompt caching,多个序列同时运行不是不行——只是三者定位不同。

一句话总结:

llama.cpp 像一辆灵活的性能车,一个人开很爽,哪里都能去;vLLM 像公交系统,重点不是一个乘客跑多快,而是同一段时间里能运多少人;SGLang 是在公交系统上又加了一套智能调度中心——哪些乘客路线相同、哪些路可以复用、哪些请求先跑。

三大场景的选型清单

┌─────────────────┬──────────────────────┬─────────────────────────────────┐
│ 场景            │ 推荐框架             │ 关键理由                        │
├─────────────────┼──────────────────────┼─────────────────────────────────┤
│ 单用户本地 Agent│ llama.cpp            │ 模型门槛低、量化成熟、硬件兼容广│
│                  │                      │ (CPU/NVIDIA/Apple Silicon)      │
├─────────────────┼──────────────────────┼─────────────────────────────────┤
│ 多人 RAG 客服   │ vLLM / SGLang        │ PagedAttention 撑并发,前缀共享 │
│                  │ (SGLang 更优)        │ 进一步省 prefill                │
├─────────────────┼──────────────────────┼─────────────────────────────────┤
│ 多 Agent 编排   │ SGLang               │ RadixAttention 共享 system      │
│                  │                      │ prompt + 智能调度                │
└─────────────────┴──────────────────────┴─────────────────────────────────┘
当你从自己本地聊天,进入几十个 Agent、几十个用户同时调用同一个模型,游戏规则就彻底变了。你要优化的不再是单用户速度,而是 batch、scheduler 和 KV Cache——这正是 vLLM 和 SGLang 真正擅长的地方。