大模型部署这个事,我见得最多的场景就是:本地单卡调试时一切正常,看起来每一次调用都能流畅生成;结果一上生产、并发一起来,要么第一个token等半天,要么直接显存溢出,要么输出内容开始莫名奇妙地抽风。很多人这时候才意识到,“能跑通”和“能落地”之间,其实隔着一条巨大的工程化鸿沟。vLLM、SGLang、TensorRT-LLM、Ollama、XInference这些主流推理部署框架,解决的正是这条鸿沟里的核心问题——吞吐、延迟、显存管理、并发调度、前端易用性。这篇文章我就围绕这几个框架,把它们的定位差异、适合场景、部署方式、显存计算逻辑,以及我实际踩过的坑,一次性讲透。内容偏工程实践,适合已经跑通过模型、正准备把模型真正交付出去的开发者。
1. 先搞清楚:推理框架到底在替我们解决什么问题
很多新手容易把推理框架当成“又一个模型运行工具”,觉得无非是换个方式加载模型、调一下输出。如果你抱着这种理解去选型,后面一定会被按在地上摩擦。推理框架解决的不是“能不能推理”,而是“在真实负载下能不能稳定、高效地推理”。
1.1 “能跑通”和“能上线”之间的差距
直接用Transformers库写一个加载脚本、循环调用模型生成,这算“能跑通”。但这个方案在并发场景下几乎不可用:每次请求都独立申请显存,batches之间没有复用,显存浪费严重;连续批处理(continuous batching)这类调度能力几乎为零;一旦请求排队,后面的请求就会无限等待。
推理框架做的事,就是把上面这些底层能力补上:
- 把多个并发请求动态拼成batch,有请求结束就插入新请求,保持GPU计算不空转;
- 用分页式的KV Cache管理替代整块显存预留,把显存利用率拉上去;
- 提供高效的tokenizer、采样参数校验、请求队列、超时控制,做成一套完整的服务化接口;
- 在底层做算子融合、量化、投机解码、前缀缓存等优化,把生成速度往上推。
所以当你选型时,本质上你在选的是:这套底层调度能力做得好不好、跟你业务对不对得上。框架之间的差别,往往不在“谁能跑”,而在“同样一张卡、同样一个模型,谁能在并发下把延迟和吞吐控制得更好”。
1.2 三个必须认识的指标:TTFT、ITL、吞吐量
判断框架好坏,不能只看demo里“生成得快不快”。生产环境里,我一般盯三组数字:
- TTFT(Time To First Token):从请求到达服务端到生成第一个token的时间。它决定了用户“第一反应”。首token迟迟不出,用户会直接感觉服务卡死。
- ITL(Inter-Token Latency)/ TPOT(Time Per Output Token):每个输出token之间的间隔。它决定了生成式AI“打字机”效果是否流畅。
- Throughput(吞吐量):单位时间整个服务能生成多少个token,一般用tokens/s衡量。高吞吐是框架内核调度能力的直接体现。
这三个指标在选型时各有侧重。内部系统交互频繁、希望“答得稳”,重点看吞吐;面向用户实时流式输出,重点看TTFT和ITL;两者都要兼顾,就得看框架对prefill阶段和decode阶段的调度策略。后面讲框架差异时,我会反复提到这三个指标。
2. 五个框架的正确打开方式:先分清定位再谈对比
把它们放在一起对比的前提,是承认它们各自的定位根本不同。vLLM把自己做成服务化推理引擎,SGLang主打结构化生成和复杂调度的灵活性,TensorRT-LLM追求单卡极致延迟优化,Ollama解决的是本机一键体验,XInference则更像一个面向模型的统一网关。定位不同,比胜负没意义,比匹配度才有意义。
2.1 vLLM:把高吞吐服务化变成默认选项
vLLM是目前大模型服务化落地里最稳妥的默认选项。它核心的PagedAttention机制,把KV Cache切成固定大小的块,按需分配,显存碎片问题大幅缓解。配合continuous batching,它能同时处理大量请求,吞吐量在同类框架里一直是很能打的。
我推荐它的理由还有一个:OpenAI兼容API做得非常标准,/v1/chat/completions、/v1/completions、/v1/embeddings这些接口基本可以直接复用现有服务端代码。无论是自研应用接入,还是给上层Agent套壳,都很方便。vLLM也支持Prefix Caching、投机解码、LoRA服务、多模态模型、量化模型加载,属于“默认选项”里能力最均衡的一个。
2.2 SGLang:擅长结构化生成和多模态负载调度
SGLang早期给我的印象是“编译器思路做推理调度”。它对请求进行前端运行时(frontend runtime)抽象,把复杂prompt和多轮调用做统一管理。它的RadixAttention能在多个请求之间复用KV Cache前缀,在处理大量相似系统提示词、多轮对话、few-shot样例时优势非常明显。
如果你做过结构化输出(JSON Schema约束、函数调用、工具调用),会感觉SGLang对这类场景的处理格外顺手。它原生支持正则、JSON Schema等约束解码,能在采样过程中强制保持输出格式。对多模态输入,SGLang也能做不同模态数据的调度对齐,这是很多框架做得不够细的地方。
不过不要把SGLang当成“vLLM换皮”,它对服务化生态的集成度和周边工具没有vLLM那么多,团队里没有熟悉它的人时,上手成本会高一些。
2.3 TensorRT-LLM:NV生态下的极致延迟优化
TensorRT-LLM的思路完全不同:它把模型编译成TensorRT引擎,在NVIDIA GPU上通过算子融合、精度校准、kernel自动调优等方式,把单卡推理延迟压缩到极致。
它的优势场景是——你已经确定了模型、确定了运行环境,希望模型在特定GPU上跑出最低延迟和最高吞吐。为了达到这个目标,你需要接受一套更重的工作流:先转模型格式,再做量化校准,再编译成engine,最后编写对应运行时。换一个模型、换一张卡,可能整套流程要重来。这跟“拉起来就能跑”的体验差得很远。
实际看下来,TensorRT-LLM比较适合对延迟极度敏感的线上推理服务,或者你在成本模型里已经把GPU规格定死了、需要榨干单卡性能的场景。如果你只是想快速验证一个模型,暂时不用碰它。
2.4 Ollama:单机场景的开箱即用
Ollama是个人电脑和开发机上非常顺手的工具。它底层使用llama.cpp系列推理后端,支持GGUF格式量化模型,通过Modelfile可以做简单的模型配置。它的杀手锏是极低的使用门槛:安装后拉模型、运行、调用API,三分钟之内能跑通。
“开箱即用”听起来轻量,但Ollama并不仅仅是玩具。它的并发请求管理、模型常驻内存、Prompt缓存加速做得都能用,很多开发者选择直接在本地开发环境里用Ollama模拟线上推理接口。缺点同样明显:调度内核的可控性弱,对大规模并发、复杂多模态负载支持有限,想深调性能时会发现上限很低。
2.5 XInference:面向异构环境的API兼容层
XInference给大模型部署加了一个“中间层”视角。它不是重写推理内核,而是把vLLM、llama.cpp、TensorRT-LLM等不同后端统一在一个服务里,暴露OpenAI兼容接口,同时提供模型下载、资源监控、多模型管理能力。
这个定位在实际团队里非常有用。比如你一部分模型跑在国产加速卡上,一部分模型跑在NVIDIA卡上,一部分模型跑在CPU上,直接用XInference统一接入,上层应用不需要感知后端差异。对“模型种类多、硬件不统一、但不想维护多套服务接口”的团队,XInference能省下很多无意义的联调时间。
框架之间的差别,我整理成一个表,方便你直接对照:
| 维度 | vLLM | SGLang | TensorRT-LLM | Ollama | XInference |
|---|---|---|---|---|---|
| 核心优势 | 高吞吐、生态成熟 | 前缀复用、结构化输出 | 单卡极致低延迟 | 开箱即用 | 多后端统一接入 |
| 启动成本 | 低 | 中 | 高 | 极低 | 中 |
| 并发能力 | 很强 | 强 | 强 | 中等 | 取决于后端 |
| 模型格式 | HF格式/量化 | HF格式/量化 | TensorRT引擎 | GGUF | 多格式 |
| 定制深度 | 中高 | 高 | 高 | 低 | 中 |
| 适合人群 | 生产服务、AI应用开发 | 复杂推理逻辑、结构化场景 | 性能优化、固定环境 | 个人开发者、本地验证 | 多模型团队、异构环境 |
3. 部署落地怎么选:给三类典型场景的答案
每次有人问我“到底该用哪个”,我都会反问他三个问题:你的请求量有没有并发压力?你的业务对首token延迟敏不敏感?你的模型种类和硬件是不是统一的?这三个问题的答案,基本决定选型方向。
3.1 一张决策树:按你的场景走到对应框架
我习惯用一组递进判断来收敛选择:
- 如果你的首要目标是本机快速跑模型、模拟环境、个人学习,直接选Ollama。先用起来再说,不用纠结底层。
- 如果你要对外提供标准API服务,预测有并发、希望生态省心,默认选vLLM。它是一个下限很高的选择。
- 如果你的请求涉及大量前缀复用、JSON Structured Output、多轮工具调用,选SGLang。它能把这类业务跑出更低的成本。
- 如果你已经认定NVIDIA GPU环境,模型固定,业务要求极低延迟,愿意投入编译成本,选TensorRT-LLM。
- 如果你需要同时管理多个模型、多套硬件,不想上层为每个模型写不同客户端,选XInference。
注意,这不是“只能选一个”的问题。我在实际项目里见过很多组合:开发机用Ollama、生产用vLLM、特定优化节点用TensorRT-LLM、统一网关用XInference。框架之间的接口都在向OpenAI API对齐,切换成本并没有想象中高。
3.2 最小可用部署:用vLLM把服务跑起来
以vLLM为例,最小可用部署非常简单。安装时推荐使用GPU镜像或用pip安装,注意版本匹配和CUDA版本。启动服务只需一条命令:
vllm serve meta-llama/Llama-3.1-8B-Instruct \ --host 0.0.0.0 \ --port 8000 \ --tensor-parallel-size 1 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9然后就能通过OpenAI风格的接口调用:
from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="EMPTY") resp = client.chat.completions.create( model="meta-llama/Llama-3.1-8B-Instruct", messages=[ {"role": "system", "content": "你是数据中心运维助手。"}, {"role": "user", "content": "解释一下分布式系统的脑裂问题。"} ], temperature=0.3, max_tokens=512, stream=True, ) for chunk in resp: delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True)这里有个很容易被忽略的点:--gpu-memory-utilization不是越高越好。我见过有人设成0.98导致KV Cache和模型加载阶段出现隐性问题。建议从0.85到0.92之间开始调,确认稳定后再逐步往上提。
3.3 用SGLang时容易被忽略的调度优势
SGLang的启动方式和vLLM很像,也有OpenAI兼容接口:
python -m sglang.launch_server \ --model-path meta-llama/Llama-3.1-8B-Instruct \ --host 0.0.0.0 \ --port 8001它真正拉开差距的场景是:大量请求共享同一个系统提示词,或者请求历史记录高度相似。由于RadixAttention会把前缀KV Cache缓存下来,new request直接复用,TTFT能下降得很明显。
如果你的业务是同一个Agent在不同用户会话里反复调用同一组工具描述,建议做个简单测试:先用SGLang部署,再用vLLM部署,分别压一组“共享前缀”的请求。你会发现两者在Cache命中率、平均首token时延上差别非常明显。这类优化不在代码层面,而在于框架对“重复计算”的消除能力。
3.4 TensorRT-LLM的编译流程:重,但有回报
TensorRT-LLM的上手路径和前面几个框架完全不一样,核心在“编译引擎”。一个常规流程大致是:
# 转换模型权重 convert_checkpoint.py \ --model_dir /path/to/hf-model \ --output_dir /path/to/trt-checkpoint \ --dtype float16 # 编译并优化引擎 trtllm-build \ --checkpoint_dir /path/to/trt-checkpoint \ --output_dir /path/to/engine \ --gemm_plugin float16 \ --max_batch_size 64 \ --max_input_len 4096 \ --max_seq_len 8192编译完成后,才用生成的engine文件启动推理服务。每一步都耗时,而且参数配置空间很大。我的建议是:除非你有明确的性能需求,否则先别碰;如果你的场景确实需要,优先研究以下几个参数:--max_batch_size、--max_input_len、--max_seq_len、--gemm_plugin、量化校准方式。这些参数直接决定了引擎的内存占用和运行时效率,改错了不会报错,只会默默变慢。
3.5 Ollama和XInference作为快速验证工具的使用心得
Ollama适合做本机冒烟测试。启动服务后,也是一套标准API:
ollama run llama3.1XInference更适合当你手上有多个模型要统一提供入口时使用。装好之后,你可以在管理界面上添加模型后端、指定运行资源、查看推理状态。它内部可以把不同模型调度到不同GPU甚至CPU上,对资源混杂的开发机非常友好。
从快速验证的角度,我的习惯是:先用Ollama确认模型交互效果,再用XInference把多个模型统一暴露出来做对比测试,最后按生产要求选定一个重框架做服务化部署。这样能避免一上来就编译TensorRT-LLM、结果发现模型压根不满足业务效果的情况。
4. 显存算术:在你选框架之前先算清这笔账
很多部署翻车案例,根子不在框架,而在显存预算没算明白。你问“为什么一个大模型部署后很卡”,我第一反应不是优化参数,而是先算一算显存够不够。
4.1 模型权重到底占多少显存
有一个很简单的估算公式:模型权重显存 ≈ 参数量 × 每参数字节数。
- FP32是4字节
- FP16/BF16是2字节
- INT8是1字节
- INT4是0.5字节
一个7B模型,FP16权重裸显存大约14GB;13B模型约26GB;70B模型约140GB。这还只是权重部分,没算激活值、优化器状态和KV Cache。
所以在选框架之前,先拿这张表做一次“能不能跑”的初筛:
| 模型规模 | 参数量 | FP16权重显存 | 建议最低显存 | 量化建议 |
|---|---|---|---|---|
| 7B级 | 约7B | 约14GB | 24GB | 无需量化;显存小可用INT8 |
| 13B级 | 约13B | 约26GB | 32GB/40GB | 建议INT8/INT4 |
| 70B级 | 约70B | 约140GB | 多卡并行 | 必须INT4/AWQ等多卡 |
| 多模态大模型 | 视结构而定 | 可能混合精度 | 按实际计算 | 视主干模型而定 |
注意,这是“模型权重”的账,不是“部署”的账。部署时KV Cache会再吃掉一块不小的显存,框架能给你的是把这块显存用得更高效。
4.2 KV Cache与连续批处理:显存中的“隐形大户”
KV Cache大小和三个变量直接相关:序列长度、层数、并发请求数。公式可以简化为:
KV Cache显存 ≈ 2(K和V两个矩阵) × 层数 × 每层KV维度 × 序列长度 × batch_size × 每个元素字节数
这也是为什么vLLM和SGLang都在“挖掘KV Cache的复用空间”。vLLM的PagedAttention按页管理KV Cache,避免为每个请求预留一整块最大长度的显存;SGLang的RadixAttention则是把公共前缀缓存共享给多个请求。两者从不同方向解决同一个问题:让显存不再因KV Cache被白白浪费。
实际配置时,--max-model-len也非常关键。如果你只有24GB显存,却把支持长度拉到32K,KV Cache会瞬间膨胀,后面再多的调度优化都补不回来。对大多数业务而言,8K长度已经能覆盖大量场景,没必要盲目追求“模型宣传的最大长度”。
4.3 量化:用精度换显存,但别只看精度
落地显存不够时,常见解法是量化。AWQ、GPTQ、FP8、INT8、GGUF这些名词会大量出现。我的经验是:7B模型在FP16下,精度和效果最稳,不优先做量化;13B模型在24GB卡上,做INT8量化是比较稳妥的;70B模型要做INT4/AWQ量化,或者多卡并行,单卡硬扛会非常吃力。
量化后的模型可能出现输出质量下降,尤其对数学推理、代码生成、长文本连贯性影响更明显。因此在量化选型上,不能只看“显存够不够”,还要做一轮业务侧的效果回归。框架本身对量化的支持也有差异:vLLM支持多种量化格式,SGLang的量化兼容性持续在增强,TensorRT-LLM则是有自己的量化校准工具链,Ollama主要依赖GGUF量化。如果你必须量化,建议在最终框架上做定型测试,不要只看格式名称相同就认为效果一致。
5. 我踩过的坑:生产环境里常见的五个异常信号
再说下去就是纯理论了。这里挑五个我实际处理过的高频问题,每个背后都有真实的排查逻辑,你可以直接按这个思路去定位。
5.1 首token迟迟不出现,却没有报OOM
现象:服务没挂,也没有显存溢出,但是用户请求发出去后,首token等了很久才出来。我先不急着调参,先看两个数据:排队中的请求数和最近一个batch的填充情况。很大概率是prefill阶段太长,或者某个超长请求把当前batch阻塞了。
排查链路一般这样走:
- 查看服务日志里的请求进入时间、首个token时间,计算当前TTFT;
- 检查并发请求数是否远高于预期,导致所有请求都在排队;
- 检查是否有一个超长prompt混在batch里,导致整个batch的prefill时间被拉长;
- 检查是否启用了前缀缓存,如果前缀不相同,缓存命中率连10%都不到,优化效果自然不明显;
- 尝试调大
--max-num-seqs或限制单请求max_tokens,看差异。
结论往往是:不是GPU太弱,而是调度参数不适应你的请求分布。
5.2 并发升高后,吞吐反而下降
另一个常见信号是:并发请求从10涨到100,吞吐量不升反降。问题通常出在batch内部的计算碎片化和显存争抢。如果每个请求的输入、输出长度差异极大,动态batching的优势会被拉平,GPU计算利用率下降。
处理办法有几种:限制单请求最大长度、把超长请求单独走一条通道、开启chunked prefill(如果框架支持)把长prefill拆分成多个小块、甚至在前端做请求排队,避免一次性把过多请求打给推理服务。重点在于,并不是并发越高越好,找到你业务请求长度分布下的“最优并发区间”才是关键。
5.3 生成结果格式对,但内容出现不连贯
这个坑更隐蔽。模型回答看起来格式正确,但内容在某个位置突然跳变,像是拼了两段话。排查后发现,问题出在tokenizer:服务端在热更新模型配置后,tokenizer的缓存没有刷新,导致prompt拼接错误。处理方式是:更新模型后必须完整重启推理服务,同时确认请求侧和推理侧的tokenizer版本一致,尤其是用XInference这类多模型网关时,tokenizer映射错误特别容易发生。
5.4 量化模型输出“抽风”
现象:换用AWQ或GPTQ量化模型后,偶尔出现明显不符合逻辑的回答。这时不要先怪框架,先回退到FP16跑同一批prompt,对照输出。如果FP16正常,问题基本锁定在量化校准数据集与你的业务分布差异过大。重新用贴近业务的语料做量化校准,或换更保守的量化方案,通常能解决。
5.5 前缀缓存开启后,性能反而雪崩
你以为开了Prefix Caching就能“复用一切”,实际发现缓存命中率低、管理Cache的开销比收益还大。原因通常是请求之间的公共前缀长度太短,或者输入顺序变化频繁。SGLang和vLLM都支持前缀缓存,但需要你的业务天然具备高前缀复用特征。判断方法很简单:统计一下请求里系统提示词占比,如果占比低于30%,就别指望缓存带来量级提升。
最后把这几个问题归纳成一张表,方便你排查时对照:
| 异常信号 | 优先怀疑对象 | 第一步动作 |
|---|---|---|
| TTFT高 | prefill过长、排队严重、缓存未命中 | 检查请求长度分布和并发数 |
| 并发升高吞吐下降 | batch碎片化、显存争抢 | 限制单请求长度、调并发区间 |
| 输出内容跳变 | tokenizer不一致 | 刷新tokenizer配置并重启服务 |
| 量化后输出抽风 | 量化方案与数据不匹配 | FP16对照实验 |
| 缓存开启性能下降 | 公共前缀占比低 | 统计前缀复用率,考虑关闭 |
6. 这轮对比之后,我的选型倾向与后续扩展思路
跑完一圈之后,如果让我对一个“不知道用什么框架”的团队给出结论,我会这么说:默认选项用vLLM,复杂结构化场景和大量共享前缀业务用SGLang,定了固定NVIDIA环境且追求单卡极限性能时再上TensorRT-LLM,个人开发机或快速验证用Ollama,多模型多硬件的统一接入用XInference。
不过框架选择不是终点。部署稳定之后,值得继续投入优化的方向还有三个:一是投机解码,用一个小模型做草稿模型加速大模型生成,能显著降低ITL;二是把推理服务纳入统一可观测系统,至少追踪TTFT、ITL、吞吐、显存利用率这四类指标;三是如果请求量继续增长,优先考虑把多实例推理服务放到负载均衡后面,而不是无限调高单实例并发。
最后再分享一个我自己的实际操作习惯:每次部署新框架,我都会先压一组固定prompt、固定并发、固定输出长度的基线数据,记录TTFT、TPOT、吞吐三个值。这样后面无论调参、换框架还是升版本,都有参照系,而不是凭感觉说“好像变快了”。这套工作方式,比记住任何框架的参数都更管用,也建议你从打算部署的那一天起就建立起来。