这几年聊到大模型推理部署,绕不开的就是 vLLM、SGLang、TensorRT-LLM、Ollama、XInference 这几个名字。我最早看各种框架对比时也犯过迷糊,以为这只是不同团队做了同类工具,直到自己把一个 7B 模型从裸脚本切成 vLLM 服务,又陆续折腾过 SGLang 和 TensorRT-LLM,才算想明白:它们的差异不在“谁更快”,而在“解决同一个问题的方式不一样,适用场景也不一样”。这篇文章就是从零开始的完整梳理,我会把每个框架的核心思路、关键参数、适合场景写清楚,最后给一套可以直接照抄的部署示例。刚接触部署的同学可以按顺序读,已经在用 Ollama 但想往高性能方向走的同学,可以直接跳到第五节和第六节。收藏这份指南,等真正要部署服务时再翻出来对照,你会发现很多坑其实是可以提前避开的。
1. 先搞清楚部署这层到底在忙什么
很多人第一步想到的是“把模型 load 起来,然后写个循环 generate”。这个路子做原型验证没问题,但放到生产环境基本撑不住。原因不复杂:大模型推理是硬件密集任务,你的瓶颈不只是算力,还有显存、内存带宽和任务调度。不理解这层,后面调任何框架参数都是在瞎猜。
1.1 模型装得下,不等于服务跑得起
我们先算一笔账。以一个 7B 参数量的开源对话模型为例,假设用 FP16 精度加载,参数量乘上 2 字节,光权重就是大约 14GB。但推理时真正吃显存的大头往往不是权重,而是 KV Cache——每一层都要为当前请求缓存已经算过的 Key 和 Value 矩阵。假设并发 8 个请求、上下文长度 4096、隐藏层维度 4096、层数 32,那 KV Cache 的占用大概是这样:8 个请求 × 4096 个 token × 4096 维 × 32 层 × K/V 两份 × 每个元素 2 字节,约等于 16GB。
你看,权重 14GB 加 KV Cache 16GB,已经 30GB 起步,这还没算激活值和中间临时张量。所以一台 24GB 显存的消费级显卡跑 7B 模型会非常紧张,40GB 左右显存的卡才算比较从容。这也是为什么部署这件事从来不是“能加载就是能跑”,显存池子的水位,直接决定你能开多大并发、能容忍多长上下文。把这些数字记在脑子里,后面看所有框架文档都会轻松很多。
1.2 吞吐、时延和显存,三个指标互相拉扯
部署里最常提的三个数字:吞吐、单请求延迟、显存占用。它们不是独立指标,而是互相拉扯的三角关系。想提高吞吐,最直接的办法是把多个请求拼成一个 batch 一起算,但 batch 变大后,KV Cache 立刻膨胀,显存先告急。想降低单请求延迟,可以缩短序列长度、用更激进的量化,或者上多卡并行,但吞吐和精度又会受牵连。
我习惯用一个餐厅的类比:显存就是桌位数。每一桌(请求)占着位置,厨房(GPU)同时在炒好几桌的菜。桌位越多翻台越快,但桌位是有限的,你不可能无限加桌;每桌待的时间越长,能接待的新客就越少。部署框架要做的就是“在有限桌位下,让翻台率最高”,同时保证每桌的菜不能炒糊。
1.3 框架真正优化的是 KV Cache 和任务调度
裸脚本最大的痛点有两个:第一个,每个请求会按最大可能长度预留一整块连续的 KV Cache 显存,不管实际用不用得完,都会造成浪费和碎片;第二个,一个 batch 要等里面所有请求全部生成结束才会释放,慢请求会拖住快请求。vLLM、SGLang、TensorRT-LLM、Ollama、XInference 这几种框架,核心工作基本就是围绕这两点展开的,只是手段各有侧重:有的优化显存分配方式,有的优化计算图执行效率,有的把调度和前缀缓存做得很极致。弄明白这一层,你在看后面具体框架时就不会被术语绕晕。
2. vLLM 与 SGLang:优先考虑吞吐的两个引擎
如果想把开源模型变成高并发线上服务,vLLM 和 SGLang 会是你最先碰到的两个名字。它们共同点是都在“运行时调度”上做文章,不像编译型方案那样需要把模型静态编译一遍,部署起来相对轻量。但它们在性能侧重点上又有明显区别。
2.1 vLLM 的 PagedAttention 和连续批处理
vLLM 最出名的两个机制是 PagedAttention 和连续批处理。PagedAttention 的思路和操作系统内存分页很像:传统方案给每个请求分配一整块连续显存当 KV Cache,请求长度不确定,预分配多了浪费,少了又不够用。vLLM 把 KV Cache 切成固定大小的块,按需分配,页面之间不要求物理连续。这样显存碎片少了,利用率高了,同一个模型能塞进去的请求数自然就多了。
连续批处理则是把“翻台率”这件事做到了极致。传统批处理必须等一个 batch 里所有序列全部生成完,才释放显存并接收下一批请求;如果其中一个请求特别长,整批都被拖住。vLLM 的做法是序列级别的动态调度:某个序列生成了结束符,立刻把它的显存 slot 释放,新请求马上插进来,不用等整批完成。这两个机制合在一起,就是 vLLM 高吞吐神话的主要来源。
2.2 vLLM 启动参数如何影响显存与并发
vLLM 真正上手时,有三个参数最值得花时间理解。
第一个是--gpu-memory-utilization。它不是给权重预留的比例,而是整个推理进程最多能用多少显存。默认通常是 0.9,也就是把 90% 的显存都纳入管理,分给 KV Cache 的池子越大,并发上限越高。但如果同一台机器上还跑着别的服务,这个值设太高,很容易把整个显存占满,导致其他进程直接 OOM。
第二个是--max-model-len。它同时决定两件事:单请求允许的最大上下文长度,以及 KV Cache 池子的预留上限。长度设得越大,模型能处理的输入输出越长,但预留的缓存池也越大,能承载的并发数反而下降。不少新手只盯着上下文长度,忽略了它和并发之间的交换关系。
第三个是--tensor-parallel-size。当单张卡装不下权重时,这个参数把模型切成多份分到多张卡上并行计算。要注意它不是越大越好,卡间通信会吃掉不少收益,能单卡跑就不要强行多卡。
2.3 SGLang 的 RadixAttention 和结构化输出
SGLang 的核心卖点之一是 RadixAttention。简单说,它把 KV Cache 组织成一棵前缀树,当多个请求拥有相同的公共前缀时,比如同一段很长的系统提示词、同样的 few-shot 示例、甚至相同的多轮历史对话,这些前置内容对应的 KV 缓存可以直接复用,不用重新计算。你可以把它理解成浏览器缓存:公共资源只下载一次,之后所有页面都直接命中缓存。
这个机制在两类场景里收益特别明显:一类是多轮对话,因为后续轮次的请求天然共享前面的历史;另一类是批量跑同样指令模板的抽取任务,公共前缀越长,复用的计算量越大。SGLang 还支持约束解码,也就是在生成过程中强行保证输出符合 JSON 格式或某种语法结构。对做信息抽取、工具调用、结构化数据生成的同学来说,这能省掉大量后处理代码。
2.4 两者怎么选:看前缀复用场景和生态
说实话,vLLM 和 SGLang 并不是谁完全替代谁的关系。vLLM 的优势是社区大、模型兼容面广、踩坑资料多,遇到问题时更容易搜到解决方案;SGLang 在公共前缀复用和结构化输出上做得更极致,多轮对话或者大量同模板请求下,收益会非常明显。我个人的习惯是:通用 Chat 服务先用 vLLM 跑通,如果业务里有大量 JSON 输出、工具调用,或者系统提示词特别长,再重点试 SGLang。两个底层思想是相通的,学会一个,另一个上手会很快。
3. TensorRT-LLM:为低延迟和极致硬件利用而生
如果说 vLLM 和 SGLang 是在运行时调度上做文章,那 TensorRT-LLM 走的是另一条路:把模型预先编译成针对特定 GPU 高度优化的引擎。这条路能带来更低延迟和更高的硬件利用率,代价是灵活度下降。
3.1 编译优化、算子融合和批量维度
TensorRT-LLM 做的事情,本质上是“静态编译”。它会拿到你的模型结构、权重、目标 GPU 型号,然后对计算图做大量优化,包括算子融合、kernel 选型、内存复用等。算子融合可以这样理解:原来的计算图里可能一串小算子依次执行,每一步都要读写一次显存;融合后它们变成一个更大的 kernel,数据在寄存器或片上缓存里直接接力,减少了和显存之间的来回搬运。这个优化对延迟的影响非常直接。
同样重要的还有批量维度的处理。传统框架在运行时动态拼 batch,TensorRT-LLM 在编译时会针对常见 batch 大小和序列长度做专门优化,让 GPU 的算力尽量吃满。它的单请求延迟在几个框架里通常是最低的一档,尤其适合那种“每次响应时间都必须压得很低”的业务。
3.2 量化级别怎么定
TensorRT-LLM 部署基本离不开量化。量化就是把权重的精度降下来,用更少的 bit 表示同样的参数。FP16 下 7B 模型权重约 14GB,换 INT8 大概 7GB,换 INT4 大概 3.5GB,显存压力完全不是一个量级。实践里的常用做法是 Embedding 和 LayerNorm 这类敏感层保持高精度,主要线性层做量化,市面上很多量化方案就是按这个思路来的。
这里要特别提醒一句:量化是有精度损失的,而且损失主要体现在长尾输出、多轮逻辑推理这类场景上。评估量化模型好不好,不能只看公开榜单,拿你自己的真实业务数据跑一遍、人眼扫一批输出,才是靠谱的做法。如果你是第一次上 TensorRT-LLM,建议从 INT8 开始,不要一上来就 INT4,降维太多容易让你分不清是量化损失还是代码 bug。
3.3 什么时候值得上 TensorRT-LLM
我的判断原则是:只有当你的业务明确卡在“单请求延迟”或“单卡算力极限”上,才值得上 TensorRT-LLM。它适合三类场景:线上要求响应特别快、模型固定下来不打算频繁更换、团队有足够耐心处理编译和调试问题。反过来,如果你还在频繁试不同模型,处于开发验证阶段,或者显存小、量化经验也不足,那它大概率会变成你的时间黑洞。
3.4 工程上需要接受的三个约束
选择 TensorRT-LLM 前,有三个约束必须心里有数。第一,兼容性:不是所有开源模型都能顺利转成 engine,经常要处理算子不支持的报错,有些模型还需要改代码才能编译过。第二,版本绑定:编译出来的 engine 和运行时版本强绑定,升级框架或换 GPU 驱动后,很可能要重新编译。第三,调试成本高:报错信息偏底层,新手容易在日志里绕半天出不来。所以我的建议是,除非目标很明确,否则别把它当成默认选项,先把自己熟悉的框架用透再说。
4. Ollama 与 XInference:从一台笔记本到多模型管理的轻量路线
前面说的几个框架大多面向服务端工程部署,但还有一波用户的需求更朴素:我就想在自己电脑上快速跑一个模型,或者给团队内部搭一个能同时试好几个模型的工具。这时候 Ollama 和 XInference 是绕不开的选项。
4.1 Ollama 让“下载即用”成为现实
Ollama 最大的价值是把模型、依赖、运行参数打包成一套极简命令。比如ollama pull可以拉模型,ollama run可以直接进入对话界面,不用自己装 Python 环境、不用管 CUDA 版本、不用写推理脚本。对很多非工程师来说,这是最低门槛的本地模型体验方式。
别因此小看 Ollama——它的底层调度并不差,也有自己的并行和显存管理策略,只是暴露给使用者的可调参数很少。它的定位就是“开箱即用”,你在这里找不到细粒度的显存利用率控制,也不需要它。想快速验证一个模型效果好不好、给业务同事演示一下能力边界,Ollama 往往是最快路径。
4.2 Modelfile:像写 Dockerfile 一样定制模型
Ollama 容易被忽略的一个功能是 Modelfile。你可以像写 Dockerfile 一样,指定基础模型、系统提示词和默认采样参数,然后构建一个属于自己团队的定制模型。举个例子:
FROM qwen2.5:7b SYSTEM """你是一个严谨、简洁的技术助手。""" PARAMETER temperature 0.7 PARAMETER num_ctx 8192保存为 Modelfile 后执行ollama create my-assistant -f Modelfile,之后一条ollama run my-assistant就能用上定好人设和参数的模型。这个功能特别适合团队内部统一行为:大家拉同一个自定义模型,提示词、温度、上下文长度都是一致的,不会出现“明明同一个模型,结果五花八门”的场面。
4.3 XInference 的多模型托管和界面化
XInference 的定位更像是轻量级模型服务管理平台。它能在同一个进程里同时加载多个模型,自带一个简单的交互界面,启动之后不用写一行代码就能选模型对话。同时它也向外界提供标准的聊天补全接口,常见工具链可以直接接进来。
团队内部做模型对比时,XInference 很省事:比如你想看看几个不同体量的开源模型对同一批问题的回答差异,直接在界面上切换就行,不用为每个模型单独起服务。不过要注意,不同框架对同一模型的加载格式可能不同,别以为一个模型 ID 到哪儿都能直接拉下来,有时候需要先转换模型格式,再放到指定目录。
4.4 轻量路线什么时候需要换引擎
我自己用 Ollama 跑过不算低的并发,但它的主要问题不是性能,而是缺少生产级控制。当你开始在意并发上来之后首 Token 是否变慢,想监控显存使用和每个请求的耗时,或者希望同一套配置稳定跑 24 小时时,基本就到了该迁移到 vLLM 这类引擎的时候。好消息是迁移成本很低,因为大家最终都支持同一套标准聊天补全接口,上层代码改一个 base_url 就能切过去。
5. 选型不只是比跑分,还要对照你的业务约束
我见过不少人拿着 GitHub 上的 benchmark 数据选框架,结果在自己的业务里表现完全不是那么回事。原因是跑分通常只测特定模型、特定并发、特定硬件,而你的业务约束往往更复杂。这一节把选型的维度摊开讲一讲。
5.1 五个框架放在同一张表里比什么
选型时至少要看六个维度:核心优化手段、显存利用水平、单请求延迟、并发吞吐、部署难度、适用场景。把它们放在同一张表里会清楚很多:
| 框架 | 核心优化手段 | 显存利用 | 单请求延迟 | 并发吞吐 | 部署难度 | 适合场景 |
|---|---|---|---|---|---|---|
| vLLM | 分页缓存 + 连续批处理 | 高 | 中低 | 高 | 中 | 大多数线上 Chat 服务 |
| SGLang | 前缀缓存 + 结构化输出 | 高 | 中低 | 高 | 中 | 多轮对话、JSON 输出、工具调用 |
| TensorRT-LLM | 编译优化 + 量化 | 较高 | 低 | 高 | 较高 | 延迟敏感、模型固定 |
| Ollama | 开箱即用 | 中 | 中 | 中 | 低 | 本地体验、小型内部工具 |
| XInference | 多模型管理 + 界面化 | 中高 | 中 | 中高 | 低 | 多模型对比、快速试点 |
这张表只是参考,真实选型一定结合你自己的硬件和业务特点。比如你的显卡只有 24GB,那 vLLM 的高显存利用率优势就不是“锦上添花”,而是“能不能跑”的问题。
5.2 典型业务怎么组合选型
实际项目中,很少会从头到尾只用一种框架。我见过比较多的一种组合方式是这样的:个人电脑上快速看效果,用 Ollama;团队内部做 Demo 给业务方看,用 XInference,因为能同时挂几个模型并给出界面;正式上线的通用对话 API,用 vLLM;如果后来模型固定了、延迟要求又极其严格,再考虑把核心模型迁到 TensorRT-LLM。
这个组合的优势是每一层都能平滑升级,上层接口不用大改。你不需要一开始就赌某个框架,先让业务跑起来,按压力增长逐步演进,是更稳妥的路径。
5.3 显存上限和最大并发其实是同一个问题
部署时真正要记住的一条经验是:可容纳的最大并发数,约等于“推理进程可用显存减去模型权重占用”,再除以“每条请求平均需要的 KV Cache”。所以决定并发的不是某个参数,而是显存池子和请求平均长度之间的比例。想让并发变高,要么把gpu-memory-utilization调大,要么限制max-model-len,要么用量化把权重占用降下来。很多 OOM 并不是模型本身装不下,而是你给 KV Cache 留的池子太小,并发一高就爆。搞清楚这个公式,后面调参会比盲试高效得多。
6. 从零跑通一个部署示例:vLLM 把开源模型变成服务
前面讲了不少原理,这里给一条可以照抄的 vLLM 部署路线。我会以常见的开源 7B 对话模型为例,从环境准备一直讲到并发验证,几乎每一步都踩过,尽量帮你省掉来回试错的成本。
6.1 环境准备和模型下载
先准备一个干净的 Python 虚拟环境,建议 Python 3.10 或 3.11。然后安装 vLLM:
python3 -m venv .venv source .venv/bin/activate pip install vllm有 NVIDIA GPU 的话,第一次装好后建议先拿一个 1B~3B 的小模型跑通链路,再上 7B 正式模型。小模型启动快、显存占用低,如果环境有问题,你很快就能看到报错;直接跑大模型遇到问题,你会分不清是显存不够还是框架配置不对。模型下载不用手动管,vLLM 支持直接从模型仓库拉取模型文件,启动服务时传入模型 ID 即可。
6.2 启动服务的完整命令与参数解释
一个适合生产起步的启动命令大概是这样的:
vllm serve Qwen/Qwen2.5-7B-Instruct \ --served-model-name my-qwen7b \ --max-model-len 8192 \ --gpu-memory-utilization 0.85 \ --tensor-parallel-size 1 \ --port 8000逐个解释一下。--served-model-name是对外暴露的模型名,客户端请求时填这个字段,将来切换后端模型时可以保持不变,对上层应用无感。--max-model-len设为 8192,表示允许单请求最大上下文 8192 个 token,它会直接影响 KV Cache 预留。--gpu-memory-utilization设 0.85,是给推理进程一个显存上限,第一次建议不要顶满 0.95。--tensor-parallel-size在本机单卡场景就是 1,多卡且单卡放不下权重时才需要调大。启动成功后会看到类似的日志,服务监听在0.0.0.0:8000。
6.3 客户端请求和并发验证
服务起来之后,用 curl 最快验证:
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "my-qwen7b", "messages": [{"role": "user", "content": "大模型部署需要注意什么?"}], "max_tokens": 256 }'返回的 JSON 里choices[0].message.content就是模型输出。先跑单请求,确认接口和模型行为都正常,再开始测并发。开几个终端同时发请求,观察不同请求的返回时机,你能直观感受到 vLLM 的排队机制:请求不是同时返回,而是按计算资源动态调度。有条件的话,同时开一个显存监控命令,看并发上升时显存水位的变化,这对你理解参数非常有用。
6.4 实践中的三类常见问题与排查链路
第一类问题是启动时显存溢出。日志里出现“CUDA out of memory”时,不要急着改一堆参数。排查链路是先确认机器上有没有其他进程占着显存,如果显存本身是满的,先释放;然后单独调低--gpu-memory-utilization到 0.7;还不行就把--max-model-len降到 4096;最后仍然不够,再考虑换量化版本模型。每一步只改一个变量,定位完瓶颈再做下一步,这样你才知道是池子问题还是权重问题。
第二类问题是请求返回 400,提示超出最大上下文长度。这通常是因为输入和输出相加超过了--max-model-len。解决办法是把长度调大,但必须意识到调大后并发能力会下降,两者需要一起权衡。
第三类问题是第一次启动特别慢。这很正常,vLLM 启动时要编译 CUDA kernel、加载并检查权重,有些模型还要做量化预处理。后续启动会走缓存,速度快很多。我见过有人第一次启动等了两分钟就以为卡死,强杀进程,反而把事情搞得更复杂。它不是在卡,是在干活。
7. 服务跑起来之后的那些事:接入、超时和统一入口
模型部署跑通只是第一步,真正接入业务、扛住线上流量,还有一堆工程细节。这一节聊三个我最常被问到的话题。
7.1 为什么/v1/chat/completions成了事实标准
因为这套聊天补全接口设计简单,现在几乎所有的推理框架都默认兼容它。客户端只需要拼一个 HTTP 请求,传model、messages、max_tokens等字段,就能从不同后端拿到格式一致的响应。这个“接口即标准”的局面,对团队最大的价值是切换成本极低:后端在 Ollama、vLLM、SGLang 之间换,上层应用基本不用动,只是改一下 base_url 和模型名。你完全可以在代码里封装一个固定的请求函数,参数永远这一套,后面换引擎不会伤筋动骨。
7.2 并发、超时、重试和限流的工程细节
一个很容易踩的坑是超时时间设太短。vLLM 这类引擎会把新请求放进队列排队,一并发高,某个请求在队列里等十几秒甚至几十秒很正常。如果客户端 HTTP 超时设成 5 秒,就会出现“服务端还在好好排队,客户端已经疯狂重试”的恶性循环。正确的做法是把超时时间设到足够覆盖排队时间,比如 120 秒,同时给请求设置合理的max_tokens,防止个别输出特别长的请求长期占着资源。
另一个值得提前想的是限流。如果服务要对多个团队开放,一定要在入口加一层按用户维度的 QPS 限制,否则某个调用方一失控,整个服务的队列都会被拖垮。限流可以在代码里写一个简单计数器,也可以用现成网关来实现,关键是要有,而不是等到出问题再补。
7.3 多引擎并存时,怎么设计统一入口
实际团队里很容易出现“主模型跑 vLLM,辅助小模型跑 Ollama 或 XInference”的情况。这时最好的做法是做一个统一入口,只暴露一个地址给前端,入口根据请求里的model字段把流量转发到对应引擎。
实现思路不复杂:拿到请求体,读model,按预设路由映射找到后端地址,转发请求,再把响应原样返回。这样业务层永远只连一个地址,切换模型、增加后端都只是改配置。这个入口还能顺手做统一日志、统一限流和统一监控,成本很低,收益很大。如果没有它,你会发现每个后端都要单独维护一套接入规则,团队协作成本会成倍上升。
最后说一个我自己的小习惯:任何新框架到手,先拿一个 1B~3B 的小模型把全链路走通,再上真正的目标模型。我见过太多人直接跑大模型,遇到环境或依赖报错后,分不清是显存不够、驱动不对,还是框架参数配错。小模型能快速暴露这些基础问题。另外一个经验是,不要在思路不清晰时反复切换框架,最稳妥的路线是:先用 Ollama 跑通业务逻辑,再用 vLLM 把并发扛住,等确实出现延迟瓶颈时再研究 TensorRT-LLM。你的时间应该花在真正影响业务结果的地方,而不是在“选哪个框架”这件事上过度内耗。