用 vLLM 起服务时,你可能遇到过这种情况:镜像能拉起来,权重也加载了,但一上量就 OOM、吞吐起不来,或者换个模型就报配置不认。这时候多半不是代码问题,而是 model recipe 这几层没配到位。这篇按「先认位置、再定显存、后调并发」的顺序,把 recipe 的组成、每个参数的判断依据和排查顺序讲一遍,最后给一份可以照着走的改动清单。
用 vLLM 起服务时,最常见的翻车场景是这样的:镜像能拉起来,权重也加载进去了,单人测一下没问题,结果一上量就 OOM,或者吞吐死活上不去。换个模型更麻烦,启动直接报配置字段不认。这时候多半不是代码写错了,而是 model recipe 这几层没配到位。
这篇按「先认位置、再定显存、后调并发」的顺序讲,recipe 的组成、每个参数的判断依据、出问题时的排查顺序,最后给一份能照着走的改动清单。不给绝对结论,只给判断标准和动作。
vLLM 里的 model recipe 到底指哪一层配置
先分清三样东西,很多人把这三个混着叫 recipe。第一样是模型自带的 config 文件,跟着权重走的,描述层数、头数、词表这些结构信息。第二样是 vLLM 侧的启动参数,端口、并行度、上下文长度、显存分配比例这些。第三样才是日常说的 recipe:社区或团队沉淀下来的一套「这个模型在这类卡上该怎么跑」的推荐配置组合。
- recipe 的作用是把「这个模型在这张卡上该怎么跑」固定下来,避免每次换机器都从头试参数
- 同一份权重在不同卡型、不同并行度下,合适的 recipe 并不相同,所以它是跟硬件绑定的,不是一次性设定
- Recipe 不改变模型本身,它只决定服务怎么占资源、怎么分批、怎么并行
- 别人给你的 recipe 只能当起点,显存不一致时直接照抄,大概率起不来或者跑不满
判断自己需不需要一份正式 recipe,看两个信号:一是这台机器上不只跑这一个模型,二是团队里不止一个人会去改启动参数。满足任意一条,就该把生效参数从终端历史里搬出来,变成跟着镜像走的文件。
一份 recipe 通常包含哪些字段
把 recipe 拆开看,字段可以归成四类。分类的意义在于:调参时一次只动一类,出问题也容易定位是哪一类在越界。
| 类别 | 典型字段 | 决定什么 |
|---|---|---|
| 显存相关 | 最大上下文长度、KV 缓存占用比例、单卡并发序列数 | 同样显存下能塞下多少请求 |
| 并行相关 | 张量并行度、流水线并行度、跨机拆分方式 | 卡数用得上用不上、通信开销多大 |
| 精度相关 | 权重量化格式、激活量化格式 | 质量和显存之间的取舍 |
| 服务相关 | 端口、鉴权方式、批处理等待策略、日志级别 | 上线后稳不稳、好不好排障 |
这四类里,显存那三项是互相挤压的。上下文长度、KV 缓存比例、并发序列数,动一个就要重算另外两个。很多人调参失败的根源就是把它们当成独立旋钮,一个个往上拧,直到撞墙。
并行和精度属于第二优先级,只有在显存那三项调完之后还有缺口,才轮到它们。服务相关字段跟性能关系不大,但上线前必须固定,否则每次重启配置都变,出了问题没法复现。
先算显存,再定参数:判断顺序不能反
顺序只有一条:权重占用 → KV 缓存预留 → 剩下的才给并发和上下文。反过来先调并发,几乎必然 OOM,因为你把最不可压缩的部分留到了最后。
- 算权重占用按参数量和量化档位估一遍。层数、隐藏维度这些从模型 config 里能读到,不用猜。
- 留 KV 缓存预算KV 缓存跟上下文长度、并发数、层数都成正比。这部分预留不足,表现是短请求能跑、长请求一进来就崩。
- 剩余显存分给并发和上下文把剩下的空间换算成并发上限。换算拿不准时,先按保守值跑,看监控里的显存水位再往上加。
- 用测算工具先过一遍去站内的部署测算页按参数量和量化档位走一遍,拿到估算区间,再回到 recipe 里填具体数字。
上下文长度和并发数是乘数关系。把其中一个翻倍,另一个基本要砍半,别指望两个一起涨。这条换算在多数场景成立,但有一种情况要放松:你是超长上下文、请求稀疏的场景,这时候宁可牺牲并发保上下文。
至于这张卡到底能扛多少、换算出来的数字对不对,别凭记忆,到站内的部署测算页面核对当时的数字再定。
上下文长度与批处理策略怎么配
上下文长度不要按历史请求的最大值设。先去看真实请求的长度分布,按分位数设上限,留一点余量就行。按最大值设,等于为极少数请求付了全部显存成本。
批处理等待时间是个吞吐和延迟的交换。调大,单次能塞更多请求,吞吐上去;但首字延迟跟着涨。交互式场景和离线批处理应该分两套配置,不要共用一份。
| 场景 | 批处理等待 | 上下文上限 | 优先级 |
|---|---|---|---|
| 在线对话 | 调小 | 按分位数留余量 | 首字延迟优先 |
| 离线批量 | 调大 | 可以贴近最大值 | 吞吐优先 |
| 长文摘要 | 中等 | 必须留足 | 质量优先,别为吞吐砍上下文 |
| 混合流量 | 按峰值请求类型定 | 折中 | 先分开再合,别一开始就统一 |
- 前缀缓存对多轮对话、固定系统提示词的场景收益明显,但会额外占显存,开了之后要重新留 KV 预算
- 判断依据很简单:如果监控里首字延迟比吞吐更重要,就别为了把显存跑满去堆并发
- 上下文和批处理都调完之后再开前缀缓存,顺序反了会把预算算乱
- 换业务类型时要重看这几项,聊天和摘要对上下文的需求差别很大
并行度与精度档位怎么选才不白花钱
单卡放得下就别上多卡。多卡通信开销在中小模型上经常直接吃掉并行带来的收益,卡多了吞吐反而没涨,延迟还高了。
必须拆分时,优先卡内拆分。跨机拆分只在单机显存实在不够时才考虑,而且上线前要测卡间带宽,带宽不够的话拆了也是白拆。
- 精度选择先看质量容忍度,再看显存缺口。质量不能忍,就别为了塞下更长上下文去压精度
- 量化带来的质量差异要在自己的任务上验证,别人说没问题不代表你的任务没问题
- 并行度和精度都动过之后,前面的显存换算要重做一遍,变量变了结论就变了
- 并发与显存、吞吐之间的换算思路,站内已经有几篇讲透了,这里只讲 recipe 里怎么落字段
有个容易被忽略的点:并行度和 batch 策略是耦合的。张量并行度提高,单卡上的有效 batch 会变小,批处理等待时间可能要跟着调整。调完并行度不回头看一眼批处理,等于只做了一半作业。
换模型或换卡时的 recipe 迁移清单
recipe 不是写完就锁死的,换模型、换卡、换框架版本这三件事都会让它失效。与其每次现场重调,不如按清单走。
| 变化 | 要重算什么 | 容易漏的坑 |
|---|---|---|
| 换模型 | 显存三项、并行度 | 新模型 config 里的字段当前版本可能不认 |
| 换卡型 | KV 缓存和并发 | 直接沿用旧参数,显存不够或跑不满 |
| 升框架版本 | 并行度和批处理 | 行为变了但参数照旧,延迟悄悄回退 |
| 换量化档位 | 显存预算、质量验证 | 只看了显存,没在任务上验质量 |
- 换模型先查兼容性看新模型的结构字段当前 vLLM 版本是否支持。不支持的字段,要么等版本,要么在启动参数里覆盖掉。
- 换卡先重算显存显存容量变了,KV 缓存和并发上限都要重算,不要直接搬旧数字。
- 换版本先小流量跑升级后先放小流量,对比首字延迟和吞吐,确认没有回退再放量。
- 旧 recipe 存档出问题能在分钟级切回旧配置,而不是现场重调。存档至少要包含完整启动参数和当时的卡型、驱动、框架版本。
起不来或者跑不快时的排查顺序
排查最怕乱动参数。按固定顺序走,能把范围一步步缩小,比凭感觉试快得多。
- 先分加载失败还是运行失败加载失败多半是权重格式或者版本不匹配;运行失败多半是显存和上下文设置超标。这一步决定后面往哪个方向查。
- 参数全压最小值试跑把上下文和并发都压到最小,能起来说明是配置问题不是环境问题,方向就确定了一半。
- 逐步加回参数一次加一项,记录每次改动后的显存水位,找到第一个越界的参数。这个参数就是瓶颈所在。
- 最后才碰并行度和精度前面问题没排除之前不要动这两项,否则变量太多,分不清是哪个起作用。
把 recipe 变成可复现的东西
调好之后,recipe 如果不落成文件,等于没调。下次换机器、换人接手,全都得重来一遍。可复现的最低要求是:参数在文件里,环境信息一起记。
- 把生效的启动参数写成文件跟着镜像走,别只留在某个人的终端历史里
- 记录卡型、驱动版本、框架版本,换环境时这三项对不上,参数就得重算
- 每次调参只改一个变量并留一次结果,形成一条能回看的记录
- 对外发布的配置模板要标适用条件,比如显存下限和并发区间,避免别人照抄后跑不动
- 团队里指定一个人维护 recipe 文件,否则谁都能改,问题就没人说得清
想让别人也能用你的 recipe,光给参数不够,还要给适用边界。显存少于多少别用、并发超过多少需要重算,这些都写上,比参数本身更有价值。
如果这套服务要对外提供接口,recipe 里的鉴权和限流字段也得一并固定下来。这部分不算性能调优,但属于上线前必须收尾的事,漏了会直接暴露在公网上。
下一步把你现在这台机器上正在跑的启动参数对照上面的清单过一遍,缺的字段补上;如果拿不准显存能扛多少,先去站内的部署测算页核算当时的数字,再回来改 recipe。→常见问题
model recipe 是 vLLM 官方定义的固定名词吗?
不完全是。官方主要提供模型支持列表和启动参数,recipe 更多是社区和团队把「某模型在某硬件上的一套推荐配置」叫顺口的说法。所以你在文档里搜不到统一格式,重点应放在搞清楚每一项参数管什么,而不是找一个标准模板照抄。
照着别人的 recipe 配了,为什么还是 OOM?
最常见的是显存和上下文口径不一致:对方可能单卡、短上下文、低并发,而你请求长度更长或并发更高,KV 缓存需求直接翻倍。另外卡型、驱动和框架版本不同也会改变实际占用。建议按权重、缓存、余量的顺序在自己机器上重算一遍,别直接复用数值。
上下文长度设大一点是不是更保险?
不是。上下文上限直接参与 KV 缓存的显存预算,设得越大,能同时处理的请求越少,首字延迟也容易被拖长。正确做法是看真实请求长度的分位数,按略高于常见长度设定,超出部分让业务侧截断或走别的通道,而不是一律拉满。
调 recipe 时应该先动并发还是先动精度?
先动并发和上下文这类可以被重新计算的参数,因为它们有明确的换算关系;精度动了会影响输出质量,属于不可逆的取舍。只有在显存缺口怎么调并发都补不上、且已验证量化后质量可接受时,再考虑降精度。
多卡是不是一定比单卡快?
不一定。多卡要把模型拆开并频繁通信,卡间带宽不够时,多出来的通信时间可能超过并行带来的收益,小模型或低并发场景尤其明显。只有当单卡显存确实放不下,或者吞吐需求已经压满单卡时,多卡才有意义。