长输入短输出场景下的 SGLang 推理性能实测前缀缓存、PD 分离配比与参数调优 长输入短输出场景下的 SGLang 推理性能实测:前缀缓存、PD 分离配比与参数调优我们产线上的推理请求,几乎是清一色的长输入、短输出:几万 token 的资料或上下文喂进去,模型只吐回几百 token 的答案。RAG、长文档问答、代码库分析,本质上都是这个形状。这种形状有个很要命的特点——算力几乎全砸在 prefill(读输入)上,decode(生成)反而很轻。再加上同一份长资料常常被很多请求反复问到,所以两个优化方向几乎写在脸上:前缀缓存:相同的长前缀只算一次,后面的请求直接复用;PD 分离:把 prefill 和 decode 拆成两个独立服务,各自用最合适的资源,不互相抢。道理人人都懂,但到底配多少卡、开哪些参数、能省多少,得靠数据说话。这篇是一次把单服务 TP1~TP8、PD 全部 9 种配比都跑了一遍的完整复盘:模型 Qwen3.6-35B-A3B 的 GPTQ-int4 量化版(一个 GDN/Mamba 混合架构的 MoE),硬件 8×RTX 5090,框架 SGLang。先把 PD 分离的结构摆出来:prefill 阶段要一次性处理几万 token 的输入主要开销来自大规模矩阵乘法和 KV 计算因此更偏算力密集decode 阶段每次只生成一个 token计算规模很小但每一步都要反复读取历史 KV cache尤其在长上下文下会大量消耗显存带宽因此更偏访存密集。一、前缀缓存的性能收益:并发越高,加速越明显同一套配置,缓存开和关各跑一遍,并发从 1 扫到 32。并发关-吞吐开-吞吐吞吐提升关-TTFT开-TTFT1436345%2.3s0.9s27511149%2.7s0.8s484188124%5.7s1.7s891345277%11.7s1.9s1695458381%23.8s3.7s3295614544%43.8s6.2s低并发时缓存只省四成多,单条请求本来也没什么可复用的。但并发一上去就指数级放大:到 32 并发,开缓存是关缓存的 6.4 倍,TTFT 从四五十秒砍到六七秒。道理也简单:关缓存时每条请求都得把 4 万 token 重算一遍,prefill 端瞬间被灌死,吞吐死死卡在 95 tok/s 这条平线上,后面全在排队,延迟雪崩。开了缓存,4 万前缀全局只算一次,prefill 几乎白嫖,系统才扛得住继续往上加并发。所以对长输入这种场景,缓存压根不是要不要优化的问题,是服务能不能用的问题。后面的实验,缓存全程开着。二、单服务的吞吐天花板:张量并行的扩展性在聊 PD 之前,得先知道老老实实堆卡的单服务能跑到什么程度,这是基准线。这里先踩一个硬约束:这个模型有16 个注意力头,张量并行(TP)的卡数必须能整除 16。所以TP3、TP5、TP6、TP7 直接启动失败(报not divisible),能用的单服务只有 TP1 / 2 / 4 / 8。每格是「吞吐 / TTFT」并发TP1TP2TP4TP8147 / 1.7s57 / 1.1s61 / 0.8s53 / 1.0s274 / 2.4s94 / 1.5s103 / 1.1s93 / 1.4s4123 / 3.1s155 / 2.2s174 / 1.7s172 / 1.6s8194 / 4.4s253 / 3.0s314 / 2.2s310 / 2.1s16176 / 11.1s307 / 5.5s379 / 3.1s406 / 4.1s32203 / 20.3s418 / 6.5s504 / 5.7s522 / 5.6s两个点值得记一下。一是 TP1 单卡早早就饱和了,16 并发吞吐见顶(~200 tok/s),再加并发只堆延迟。二是 TP4 往上边际严重递减:TP4 是 504、TP8 是 522,多用一倍的卡只换来 4% 的吞吐;低并发时 TP8 甚至还不如 TP4。为什么 TP8 堆不动?这里有个绕不开的硬件原因——5090 没有 NVLink。nvidia-smi topo一看,8 张卡之间全是 PCIe 桥(PXB),没有一条 NVLink。而张量并行(TP)有个特点:每一层、每次前向,都要在所有 TP 卡之间做一次 all-reduce 同步。TP8 就是 8 张卡每层同步一次,全挤在 PCIe 那点带宽上,卡越多同步越贵,把多出来的算力收益全吃掉了。要是换成带 NVLink 的数据中心卡(H100 那种 600GB/s 的片间互联),TP8 能 scale 得好得多。所以在我们这种消费级、没 NVLink 的机器上,单服务这条路,4 张卡基本就到头了,再堆纯属浪费。三、PD 配比全景:对称与非对称的性能差异PD 分离要决定 prefill 几张卡、decode 几张卡。我把 P、D ∈ {1,2,4} 且总卡数 ≤8 的9 种配比全跑了一遍。结果非常戏剧性:输出吞吐(tok/s),从 1 并发扫到 32,最后一列是 32 并发的首 token 延迟:类型配比c1c2c4c8c16c32c32-TTFT对称44631111883464696356.0s对称22591031623043695317.0s对称11488411013413414229s非对称4229699911412514430s非对称2128708910410511238s非对称412871859910611138s非对称1224505264697359s非对称2424435265636864s非对称14183141423644100s所有非对称配比(prefill 和 decode 卡数不等的)全部崩盘,吞吐只有 44~144,而对称的 44 是 635——差距高达 4 到 14 倍。TTFT 那边更夸张:42 的 TTFT 是 30 秒、14 高达 100 秒,而 44 才 6 秒。为什么非对称这么惨?翻日志找到了根因。SGLang 自己打了条警告:“非 MLA 模型用不同的 TP 大小,性能没保证”。比如 42,是 prefill 按 4 路切 KV、decode 按 2 路切,两边切法不一致,KV 传过去之前要做一次4→2 的重新分片。这个重分片走 PCIe(消费级卡没 NVLink),又慢又容易串行,大批请求堵在传输环节,decode 干等,吞吐和延迟一起崩。对称配比(2→2、4→4)两边切法一致,KV 一一对应直接拷贝,没有重分片这道工序,所以顺。所以记住一条就行:SGLang 的 PD 分离,prefill 和 decode 的 TP 必须相等,能用的就 11 / 22 / 44 这三种。任何不对称配比都会踩到跨 TP 重分片的慢路径,直接腰斩甚至骨折。四、同卡数横评:PD 相对单服务的吞吐优势光看44 比 22 快没意义,那是因为卡多。真正该问的是:同样的卡数,我该开单服务,还是拆成 PD?把同卡数的两种方案摆一起(都取 32 并发):每格是「吞吐 / TTFT」(都取 32 并发):卡数单服务对称 PD吞吐结论2 卡TP2 418/ 6.5s11 142 / 29s单服务大胜4 卡TP4 504 / 5.7s22 531/ 7.0sPD 小胜 5%8 卡TP8 522 / 5.6s44 635/ 6.0sPD 胜 22%(顺带提一句:4 卡、8 卡这两档,单服务的 TTFT 反而还略低一点点——PD 多了一跳 KV 传输。PD 赢在吞吐和后面要讲的解码延迟,不在首 token。)这张表得多看两眼。2 卡千万别拆。把 2 张卡拆成 11,prefill 和 decode 各自只剩 1 张卡,两端都被削成残废,吞吐 142,连 TP2 单服务 418 的零头都不到——PD 是有临界质量的,每一侧至少得 2 张卡才玩得转。4 卡算及格线,22 略微反超 TP4。到 8 卡,44 比 TP8 高 22%。8 卡这个反超,跟上一节的 NVLink 问题是同一回事。44 其实是两个 TP4 的服务:每一侧只做 4 路 all-reduce,而不是 TP8 的 8 路;两侧之间只有每请求一次的 KV 批量传输,而不是每层都同步。换句话说,PD 把每层全卡同步这个 PCIe 大头给绕开了,只在该传的时候传一次。在没 NVLink 的机器上,这个通信模式上的便宜,比那 22% 数字本身更值钱。所以规律其实挺简单:卡少(≤2)老老实实单服务,卡多(≥4)拆成对称 PD,而且卡越多 PD 越划算。这正好对上我们产线长输入要堆卡的需求——同样是堆卡,PD 更能把卡转化成吞吐。五、吞吐之外:解码延迟、稳定性与成本权衡看到这儿你可能会犯嘀咕:单服务吞吐也不差——2 卡 TP2 性价比还最高,就算到 8 卡,44 也才比 TP8 领先 22%。为这点提升搞 PD 这一整套,值吗?只盯吞吐的话,这质疑有道理。但产线要的不光是吞吐,还有延迟稳不稳、长尾抖不抖、过载扛不扛得住——而这几样恰恰是 PD 的强项。同卡数对比:吞吐咬得很紧,延迟天差地别先做最公平的对比:同样的卡数,单服务和对称 PD 各跑一遍,看解码延迟(TPOT)随并发怎么走。把 32 并发那一档拎出来列个表:卡数配置吞吐解码延迟 TPOT长尾 P99-TPOT4 卡TP4 单服务50422ms34ms4 卡22 PD53113ms18ms8 卡TP8 单服务52222ms42ms8 卡44 PD63511ms18ms吞吐上 PD 只是小赢,但解码延迟差出一大截:单服务的 TPOT 随并发一路爬升,到 32 并发涨到 22ms;PD 却全程压在 ~12ms 纹丝不动。长尾更夸张——单服务的 P99 抖到 34~42ms,PD 稳在 18ms。为什么会这样?单服务里 prefill 和 decode 挤在同一个前向循环,每当一个 4 万 token 的大 prefill 块在跑,正在解码的请求就得干等它算完才能吐下一个字,TPOT 就一卡一卡地抖。PD 把 decode 拆到独立的卡上,永远不被 prefill 打断,所以解码丝滑、可预测。对流式输出、有 P99 SLA 的服务,这个差别基本是决定性的:用户看到的是匀速吐字,不是一卡一顿。其实不止解码延迟。把 8 卡这组在几个维度上整体过一遍:除了首 token 延迟(PD 多一跳 KV 传输略逊),44 在解码速度、稳定性、并发上限、单卡 KV 密度上都占优——后两项(并发上限、KV 密度)下一节的压力测试还会专门讲。成本视角:加卡到底买回了什么PD 不是白来的,它要花更多卡。把 TP2、22、44 这条加卡阶梯摆出来算笔账:配置卡数总吞吐每卡吞吐TPOTTP2 单服务241820928ms22 PD453113313ms44 PD86357911ms有意思的是,每卡吞吐最高的恰恰是最便宜的 TP2(209 tok/s 每卡),加卡反而越加越亏:22 掉到 133,44 只剩 79。也就是说,如果你的考核口径就是每张卡榨出多少吞吐,TP2 单服务才是最优解,PD 在这个口径上是吃亏的。但加卡买回来的,是延迟和稳定性:TPOT 从 28ms 一路压到 11ms 并稳住,长尾从 53ms 收到 18ms,外加下一节会讲的过载不雪崩。所以这笔账怎么算,全看你的服务有没有延迟 SLA——没有,TP2 最省;有,这卡就花得值。还有几个不太好量化、但很实在的好处故障隔离:prefill 和 decode 是两个独立进程,一端崩了不会带崩另一端,也能分别重启、灰度;资源可分级:可以给 prefill、decode 设不同的优先级、超时,甚至放到不同规格的机器上;过载不雪崩:decode 永远有富余,流量打爆时只是上游排队变长,而不是像单服务那样撞硬墙、整体崩——这点第七节用压力测试细说。六、CUDA graph:容易被忽视的解码加速开关插一段血泪教训。中途发现另一套脚本跑同样 workload,吞吐是我的整整5 倍。扒了半天配置,差别只有一个: CUDA graph的开启。我为什么关?前期调别的量化格式时撞过 CUDA graph 捕获崩溃,出于防御就全程加了--disable-cuda-graph。结果这个 int4 模型捕获得好好的,3 秒就完事,纯属过度保守。CUDA graph 把解码的内核调用序列固化下来,消掉每步的 kernel launch 开销。对这个解码本来就快的 MoE,效果立竿见影:每 token 延迟从 63ms 掉到 12ms,解码快 5 倍,吞吐随之 5 倍。两层教训:一是CUDA graph 务必开,关掉等于白白慢 5 倍,这是单点收益最大的开关;二是绝对值会被这种全局开关整体拉偏,但相对排名稳如老狗——我前面所有配比对比因为统一关了 graph,结论(对称最优、非对称崩盘、缓存 6 倍)一个没错,只是绝对数字整体低了 5 倍,重跑后排名分毫未变。控制变量做实验,就是这点好处。七、高并发压力测试:并发上限与排队行为性能数字之外,PD 在结构上还有一层硬优势。把并发往死里灌(64 / 128 / 256),盯着正在跑的请求数和排队数:TP2 单服务很干脆:不管灌多少,同时在跑的请求死死卡在81,多出来的全堆进队列。256 并发时 81 个在跑、近 200 个排队,TTFT 飙到 47 秒。这个 81 怎么来的?这模型的并发不光看显存,还看 Mamba 的状态槽(每个并发请求占一份递归状态)。单服务要同时干 prefill 和 decode、还得给缓存留 ping-pong 缓冲,每个请求占 3 个槽,243 ÷ 3 81。PD 这边完全是另一幅景象:不管灌 64 还是 256,decode 端正在解码的数量一直稳在 20 多个,排队 0,抢占 0。256 个请求,decode 里怎么才 26 个?这是排队论的常识——一个工位里此刻有几个在处理 流入速度 × 每个处理多久,和你总共发了多少无关。decode 一个请求 2.4 秒就跑完走人,上游 prefill 又喂得慢,两者一乘,decode 里永远只积着 20 多个,远够不着容量上限。那 256 个请求大部分堆在 prefill 门口排队。所以PD 不撞顶的准确含义是:decode 引擎永远有富余,不堵、不抢占,瓶颈被挪到了上游的 prefill。它没消除等待,但这个挪很值钱——decode 不被 prefill 抢,延迟稳;没有硬墙,吞吐能靠加 prefill 卡继续顶;decode 端也永远不会撞顶把算了一半的请求踢掉重来。单服务则是 prefill 和 decode 焊死在一个池子里,一灌满就整体雪崩。这才是 PD 在高并发下真正的护城河:不是峰值快那么一点,是过载了也不崩。八、参数调优:逐项消融实验确定 44 是最优配比后,系统地扫了一圈参数,一个一个改、跟基线对照。下面是 44、32 并发下逐个参数的结果(基线吞吐 622、TTFT 6.2s),真正有用的其实就两个:参数改动吞吐 (提升)TTFT结论基线—6226.2s—chunked-prefill-size2048 → 8192701 (13%)5.4s✅ 最有效mamba-scheduler-strategy默认 → extra_buffer662 (6%)5.3s✅ 有效两者组合—751 (21%)4.9s✅✅ 叠加max-prefill-tokens16384 → 32768653 (5%)5.7s⚪ 几乎无效mem-fraction 0.900.85 → 0.90626 (±0)6.1s⚪ 噪声flashinfer 采样pytorch → flashinfer619 (±0)6.1s⚪ 噪声cuda-graph-max-bs32 → 64625 (±0)6.0s⚪ 噪声chunked-prefill-size→ 16384330 (−47%)11.9s❌ 过犹不及page-size1 → 16启动崩溃—❌ 不支持chunked-prefill-size调到 8192 直击 prefill 瓶颈,13~15%;但别贪心调到 16384,会暴跌四成多。mamba-scheduler-strategy extra_buffer换更聪明的调度,稳定 6~8%。两个一起上,合计 20% 吞吐。剩下那一堆基本是噪声。还有两个直接劝退:page-size改 16 会崩(混合 Mamba 模型把它锁死成 1),16384 分块更是负优化。说白了,这模型真正能拧的就两三个旋钮,剩下的别浪费时间。九、生产环境落地配置建议对长输入短输出 高并发场景,推荐 44 对称 PD,关键启动参数:--attention-backend triton --sampling-backend pytorch --cuda-graph-max-bs 32 # CUDA graph 必开(默认即开,别手贱关掉) --mem-fraction-static 0.85 --context-length 65536 --chunked-prefill-size 8192 # 有效:13~15%,别超过这个值 --mamba-scheduler-strategy extra_buffer # 有效:6~8% # 前缀缓存默认开启,务必保留一张决策速查表:你的诉求该怎么做长前缀被反复问前缀缓存必开,高并发能到 6 倍吞吐只有 2 张卡、只看吞吐用单服务 TP2,别拆 11有 4 张及以上用对称 PD(22 / 44),卡越多越划算对延迟稳定性 / P99 有 SLA用 PD,解码 TPOT 低一半、长尾紧 2~3 倍选 PD 配比只用对称的,非对称配比吞吐腰斩到骨折千万别做非对称配比、关 CUDA graph、page-size 调大、TP 取 3/5/6/7想再榨一点chunked-prefill-size 调 8192 mamba 调度换 V2,再 20%担心高并发崩用 PD,decode 解耦后永不撞顶十、讨论与展望:NVLink 互联的潜在影响这篇所有数都是在没 NVLink 的 5090 上跑的,前面也反复提到,跨卡通信全靠 PCIe。那很自然有人会问:换上带 NVLink 的卡(H200、B300 那种),结论会不会被推翻?老实说我也很想知道,但手头暂时没那条件,只能先从原理上掰扯几句,算给以后挖个坑。关键是,NVLink 是给单服务和 PD 两边同时提速的,不是只帮一边:对单服务,它直接治的就是大 TP 的痛点。每层那个 8 路 all-reduce,在 NVLink 上是几百 GB/s,不再是 PCIe 那点可怜带宽。所以 TP8 大概率能重新 scale 起来,单服务吞吐会往上窜一截。对 PD,它能加速 prefill→decode 的那次 KV 传输(前面看到长输出、高并发时,这一跳本身会变成瓶颈)。更有意思的是,那个把所有非对称配比打趴下的跨 TP 重分片,大头也是 PCIe 慢——上了 NVLink,42、24 这些配比说不定就活过来了,只能对称这条铁律,可能就松动了。所以这是个两边都受益的事,谁涨得更多、最优配比会不会变,光靠想是想不明白的,得真跑。我的直觉(也就是个直觉):单服务在纯吞吐上会追上来不少,毕竟 NVLink 最直接修的就是它的 all-reduce 短板;但 PD 那几个结构性好处——解码延迟稳、不撞顶、故障隔离——是跟互联无关的,该有还是有。要是以后能摸到一台 H200 或者 B300,我打算把这整套矩阵原样再跑一遍:单服务的 TP scale、PD 的全配比、对称非对称,一个不落重测,到时候再写一篇对照的。手上有这条件的同行,也欢迎拿去跑,结论咱们对一对。结语这一圈跑下来,印象最深的是:一堆看着就该更好的东西,实测要么没用、要么直接骨折——非对称配比、16k 分块、TP8 堆卡、各种花哨参数,一个没幸免。真正管用的反而就那么几下:缓存打开、配比对称、CUDA graph 别手贱关掉、prefill 分块调到 8192。调性能这活儿基本就这样,拍脑袋只能定个大方向,最后还得拿数字说话。而最值钱的数字,往往是那些跟你直觉拧着来的——比如同样 8 张卡,配比选对没选对,吞吐能差 14 倍。这种东西,不跑一遍你永远不知道。实测环境:SGLang 0.5.12 torch 2.9.1cu128 sgl_kernel 0.3.21,8×RTX 5090(CUDA 12.8 驱动),模型 Qwen3.6-35B-A3B-GPTQ-Int4。workload 为 4 万 token 共享前缀 200 token 问题 200 token 输出。文中所有数字均为真机实测。