news 2026/6/10 6:48:14

长输入短输出场景下的 SGLang 推理性能实测前缀缓存、PD 分离配比与参数调优

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
长输入短输出场景下的 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开-TTFT
14363+45%2.3s0.9s
275111+49%2.7s0.8s
484188+124%5.7s1.7s
891345+277%11.7s1.9s
1695458+381%23.8s3.7s
3295614+544%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」

并发TP1TP2TP4TP8
147 / 1.7s57 / 1.1s61 / 0.8s53 / 1.0s
274 / 2.4s94 / 1.5s103 / 1.1s93 / 1.4s
4123 / 3.1s155 / 2.2s174 / 1.7s172 / 1.6s
8194 / 4.4s253 / 3.0s314 / 2.2s310 / 2.1s
16176 / 11.1s307 / 5.5s379 / 3.1s406 / 4.1s
32203 / 20.3s418 / 6.5s504 / 5.7s522 / 5.6s

两个点值得记一下。一是 TP1 单卡早早就饱和了,16 并发吞吐见顶(~200 tok/s),再加并发只堆延迟。二是 TP4 往上边际严重递减:TP4 是 504、TP8 是 522,多用一倍的卡只换来 4% 的吞吐;低并发时 TP8 甚至还不如 TP4。

为什么 TP8 堆不动?这里有个绕不开的硬件原因——5090 没有 NVLinknvidia-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
对称4+4631111883464696356.0s
对称2+2591031623043695317.0s
对称1+1488411013413414229s
非对称4+229699911412514430s
非对称2+128708910410511238s
非对称4+12871859910611138s
非对称1+224505264697359s
非对称2+424435265636864s
非对称1+4183141423644100s

所有非对称配比(prefill 和 decode 卡数不等的)全部崩盘,吞吐只有 44~144,而对称的 4+4 是 635——差距高达 4 到 14 倍。TTFT 那边更夸张:4+2 的 TTFT 是 30 秒、1+4 高达 100 秒,而 4+4 才 6 秒。

为什么非对称这么惨?翻日志找到了根因。SGLang 自己打了条警告:“非 MLA 模型用不同的 TP 大小,性能没保证”。比如 4+2,是 prefill 按 4 路切 KV、decode 按 2 路切,两边切法不一致,KV 传过去之前要做一次4→2 的重新分片。这个重分片走 PCIe(消费级卡没 NVLink),又慢又容易串行,大批请求堵在传输环节,decode 干等,吞吐和延迟一起崩。

对称配比(2→2、4→4)两边切法一致,KV 一一对应直接拷贝,没有重分片这道工序,所以顺。

所以记住一条就行:SGLang 的 PD 分离,prefill 和 decode 的 TP 必须相等,能用的就 1+1 / 2+2 / 4+4 这三种。任何不对称配比都会踩到跨 TP 重分片的慢路径,直接腰斩甚至骨折。


四、同卡数横评:PD 相对单服务的吞吐优势

光看"4+4 比 2+2 快"没意义,那是因为卡多。真正该问的是:同样的卡数,我该开单服务,还是拆成 PD?

把同卡数的两种方案摆一起(都取 32 并发):

每格是「吞吐 / TTFT」(都取 32 并发):

卡数单服务对称 PD吞吐结论
2 卡TP2 =418/ 6.5s1+1 = 142 / 29s单服务大胜
4 卡TP4 = 504 / 5.7s2+2 =531/ 7.0sPD 小胜 +5%
8 卡TP8 = 522 / 5.6s4+4 =635/ 6.0sPD 胜 +22%

(顺带提一句:4 卡、8 卡这两档,单服务的 TTFT 反而还略低一点点——PD 多了一跳 KV 传输。PD 赢在吞吐和后面要讲的解码延迟,不在首 token。)

这张表得多看两眼。

2 卡千万别拆。把 2 张卡拆成 1+1,prefill 和 decode 各自只剩 1 张卡,两端都被削成残废,吞吐 142,连 TP2 单服务 418 的零头都不到——PD 是有临界质量的,每一侧至少得 2 张卡才玩得转。4 卡算及格线,2+2 略微反超 TP4。到 8 卡,4+4 比 TP8 高 22%。

8 卡这个反超,跟上一节的 NVLink 问题是同一回事。4+4 其实是两个 TP4 的服务:每一侧只做 4 路 all-reduce,而不是 TP8 的 8 路;两侧之间只有"每请求一次"的 KV 批量传输,而不是每层都同步。换句话说,PD 把"每层全卡同步"这个 PCIe 大头给绕开了,只在该传的时候传一次。在没 NVLink 的机器上,这个通信模式上的便宜,比那 22% 数字本身更值钱。

所以规律其实挺简单:卡少(≤2)老老实实单服务,卡多(≥4)拆成对称 PD,而且卡越多 PD 越划算。这正好对上我们产线"长输入要堆卡"的需求——同样是堆卡,PD 更能把卡转化成吞吐。


五、吞吐之外:解码延迟、稳定性与成本权衡

看到这儿你可能会犯嘀咕:单服务吞吐也不差——2 卡 TP2 性价比还最高,就算到 8 卡,4+4 也才比 TP8 领先 22%。为这点提升搞 PD 这一整套,值吗?

只盯吞吐的话,这质疑有道理。但产线要的不光是吞吐,还有延迟稳不稳、长尾抖不抖、过载扛不扛得住——而这几样恰恰是 PD 的强项。

同卡数对比:吞吐咬得很紧,延迟天差地别

先做最公平的对比:同样的卡数,单服务和对称 PD 各跑一遍,看解码延迟(TPOT)随并发怎么走。

把 32 并发那一档拎出来列个表:

卡数配置吞吐解码延迟 TPOT长尾 P99-TPOT
4 卡TP4 单服务50422ms34ms
4 卡2+2 PD53113ms18ms
8 卡TP8 单服务52222ms42ms
8 卡4+4 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 传输略逊),4+4 在解码速度、稳定性、并发上限、单卡 KV 密度上都占优——后两项(并发上限、KV 密度)下一节的压力测试还会专门讲。

成本视角:加卡到底买回了什么

PD 不是白来的,它要花更多卡。把 TP2、2+2、4+4 这条"加卡阶梯"摆出来算笔账:

配置卡数总吞吐每卡吞吐TPOT
TP2 单服务241820928ms
2+2 PD453113313ms
4+4 PD86357911ms

有意思的是,每卡吞吐最高的恰恰是最便宜的 TP2(209 tok/s 每卡),加卡反而越加越"亏":2+2 掉到 133,4+4 只剩 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 在高并发下真正的护城河:不是峰值快那么一点,是过载了也不崩。


八、参数调优:逐项消融实验

确定 4+4 是最优配比后,系统地扫了一圈参数,一个一个改、跟基线对照。

下面是 4+4、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 分块更是负优化。说白了,这模型真正能拧的就两三个旋钮,剩下的别浪费时间。


九、生产环境落地配置建议

对"长输入短输出 + 高并发"场景,推荐 4+4 对称 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,别拆 1+1
有 4 张及以上用对称 PD(2+2 / 4+4),卡越多越划算
对延迟稳定性 / 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,4+2、2+4 这些配比说不定就活过来了,"只能对称"这条铁律,可能就松动了。

所以这是个两边都受益的事,谁涨得更多、最优配比会不会变,光靠想是想不明白的,得真跑。我的直觉(也就是个直觉):单服务在纯吞吐上会追上来不少,毕竟 NVLink 最直接修的就是它的 all-reduce 短板;但 PD 那几个结构性好处——解码延迟稳、不撞顶、故障隔离——是跟互联无关的,该有还是有。

要是以后能摸到一台 H200 或者 B300,我打算把这整套矩阵原样再跑一遍:单服务的 TP scale、PD 的全配比、对称非对称,一个不落重测,到时候再写一篇对照的。手上有这条件的同行,也欢迎拿去跑,结论咱们对一对。


结语

这一圈跑下来,印象最深的是:一堆"看着就该更好"的东西,实测要么没用、要么直接骨折——非对称配比、16k 分块、TP8 堆卡、各种花哨参数,一个没幸免。真正管用的反而就那么几下:缓存打开、配比对称、CUDA graph 别手贱关掉、prefill 分块调到 8192。

调性能这活儿基本就这样,拍脑袋只能定个大方向,最后还得拿数字说话。而最值钱的数字,往往是那些跟你直觉拧着来的——比如同样 8 张卡,配比选对没选对,吞吐能差 14 倍。这种东西,不跑一遍你永远不知道。


实测环境:SGLang 0.5.12 + torch 2.9.1+cu128 + sgl_kernel 0.3.21,8×RTX 5090(CUDA 12.8 驱动),模型 Qwen3.6-35B-A3B-GPTQ-Int4。workload 为 4 万 token 共享前缀 + 200 token 问题 + 200 token 输出。文中所有数字均为真机实测。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 6:39:40

群体智能建模:用鱼群规则挖掘时空轨迹中的自然分群

1. 项目概述:当鱼群游过数据海洋,我们钓到了什么?“Fishing for Insight”这个标题乍看像一句俏皮的双关语——既指字面意义上观察鱼群行为,又暗喻在庞杂数据中打捞真正有价值的认知。我第一次读到Kal Lemma这篇发表在Towards AI上…

作者头像 李华
网站建设 2026/6/10 6:35:32

跨模态检索技术:高效处理生物多样性多模态数据

1. 项目概述:文本驱动的野生动物观测检索系统在生物多样性监测领域,我们正面临一个前所未有的数据爆炸时代。全球范围内的公民科学项目、自动相机陷阱和声学监测设备每天产生数以百万计的野生动物观测记录。以iNaturalist平台为例,其数据库已…

作者头像 李华
网站建设 2026/6/10 6:25:24

业务逻辑层(BLM):数据湖可信落地的核心基建

1. 项目概述:当数据湖遇上“业务逻辑层”——为什么BLM才是真正的底层基建你有没有遇到过这样的场景:花了大价钱搭起一套Spark Delta Lake Presto的数据湖架构,数据接入、存储、查询样样不落,结果业务部门提个“上个月华东区高净…

作者头像 李华