☰
大模型推理框架部署实战:吞吐、显存与延迟的工程化权衡
2026/10/20 15:39:23 网站建设 项目流程

大模型部署这个事,我见得最多的场景就是:本地单卡调试时一切正常,看起来每一次调用都能流畅生成;结果一上生产、并发一起来,要么第一个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能省下很多无意义的联调时间。

框架之间的差别,我整理成一个表,方便你直接对照:

维度vLLMSGLangTensorRT-LLMOllamaXInference
核心优势高吞吐、生态成熟前缀复用、结构化输出单卡极致低延迟开箱即用多后端统一接入
启动成本低中高极低中
并发能力很强强强中等取决于后端
模型格式HF格式/量化HF格式/量化TensorRT引擎GGUF多格式
定制深度中高高高低中
适合人群生产服务、AI应用开发复杂推理逻辑、结构化场景性能优化、固定环境个人开发者、本地验证多模型团队、异构环境

3. 部署落地怎么选:给三类典型场景的答案

每次有人问我“到底该用哪个”,我都会反问他三个问题:你的请求量有没有并发压力?你的业务对首token延迟敏不敏感?你的模型种类和硬件是不是统一的?这三个问题的答案,基本决定选型方向。

3.1 一张决策树:按你的场景走到对应框架

我习惯用一组递进判断来收敛选择:

  1. 如果你的首要目标是本机快速跑模型、模拟环境、个人学习,直接选Ollama。先用起来再说,不用纠结底层。
  2. 如果你要对外提供标准API服务,预测有并发、希望生态省心,默认选vLLM。它是一个下限很高的选择。
  3. 如果你的请求涉及大量前缀复用、JSON Structured Output、多轮工具调用,选SGLang。它能把这类业务跑出更低的成本。
  4. 如果你已经认定NVIDIA GPU环境,模型固定,业务要求极低延迟,愿意投入编译成本,选TensorRT-LLM。
  5. 如果你需要同时管理多个模型、多套硬件,不想上层为每个模型写不同客户端,选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.1

XInference更适合当你手上有多个模型要统一提供入口时使用。装好之后,你可以在管理界面上添加模型后端、指定运行资源、查看推理状态。它内部可以把不同模型调度到不同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约14GB24GB无需量化;显存小可用INT8
13B级约13B约26GB32GB/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阻塞了。

排查链路一般这样走:

  1. 查看服务日志里的请求进入时间、首个token时间,计算当前TTFT;
  2. 检查并发请求数是否远高于预期,导致所有请求都在排队;
  3. 检查是否有一个超长prompt混在batch里,导致整个batch的prefill时间被拉长;
  4. 检查是否启用了前缀缓存,如果前缀不相同,缓存命中率连10%都不到,优化效果自然不明显;
  5. 尝试调大--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、吞吐三个值。这样后面无论调参、换框架还是升版本,都有参照系,而不是凭感觉说“好像变快了”。这套工作方式,比记住任何框架的参数都更管用,也建议你从打算部署的那一天起就建立起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询