第 3 章 batching:总吞吐涨了,单条为什么更慢
先修:第 1–2 章 · 深度:L1 · 预计阅读:25 分钟
一句话:batch 摊薄的是权重读取,不是任何一条请求的等待。
14 条请求,一条一条跑要多久
第 2 章算出来,80GB 卡配 7B、8K,最多并 14 条。假设每条输出 512 token。
一条一条跑:生成一个字要读 13 GiB 权重加 4 GiB 缓存,共 17 GiB,除以 2,039 GB/s 约 8.3 毫秒。512 个字就是 4.3 秒。14 条乘下来 60 秒。
十四条一起跑:权重只读一遍,缓存各读各的,一步读 13 + 56 = 69 GiB,约 34 毫秒,但这一步同时给 14 条各出一个字。
总时间 512 × 34 毫秒 ≈ 17 秒。吞吐从 120 token/s 涨到 411 token/s,三倍半。
单条的出字间隔,从 8.3 毫秒变成 34 毫秒,慢了四倍。
秘密只有一句
每生成一个 token,GPU 都必须把全部权重从显存搬一遍。这个动作躲不掉,但可以共享。
读一次 13 GiB,服务一条请求是浪费,服务十四条就是把成本摊成十四分之一。
batching 不改任何一条请求的计算量。它只是让同一次读取,多喂几条。
把每一步读多少列出来
| 部分 | batch 1 | batch 4 | batch 14 |
|---|---|---|---|
| 权重 | 13 GiB | 13 GiB | 13 GiB |
| 缓存 | 4 GiB | 16 GiB | 56 GiB |
| 每步读取 | 17 GiB | 29 GiB | 69 GiB |
| 每步耗时 | 8.3 ms | 14.2 ms | 34 ms |
| 单条 TPOT | 8.3 ms | 14.2 ms | 34 ms |
| 总吞吐 | 120 token/s | 281 token/s | 411 token/s |
看第一行和最后一行。权重不动,吞吐涨;缓存跟着涨,延迟也跟着涨。
batch 从 1 到 4,吞吐涨 2.3 倍;从 4 到 14,只再涨 1.5 倍。加得越多,边际收益越小。
收益停在哪里
两个上限,谁先到谁说话。
容量上限:batch 再加,56 GiB 的缓存就撑爆了。装不下的时候,多出来的请求去排队,或者触发抢占。
收益上限:batch 14 时权重只占每步读取的 19%,已经接近可以忽略。再加 batch,每步耗时几乎按倍数涨,吞吐基本不动,延迟继续涨。这时候 batching 已经没用了,只是在拿延迟换一个不存在的吞吐。
所以 batch 不是越大越好,加到"权重不再是大头"就够。
尾延迟从哪里来
平均值好看,不代表没人难受。
队列里二十条,一次只能跑十四条。剩下六条要等前面跑完,这段等待全算进它们的首字时间。
前面的请求跑得快,第六个等的人可能只多等半秒;前面有两条 4K 输出的,他等三秒。平均值还是 1.2 秒。
压测只报平均延迟,等于把这批人藏起来。P99 才是有人真的在等的证据。
什么时候 batching 不管用
只有一个人在用时,batch=1,没有东西可以摊薄。
prefill 阶段也不怎么吃这套。那是算力瓶颈,拼 batch 只是把更多 token 塞进同一次矩阵乘,收益远小于 decode。
显存已经贴顶时更危险。再拼就是抢占,被扔掉的请求要重算 prefill,代价可能比省下的那点吞吐还大。
经验:先分清是没拼上,还是拼满了
吞吐上不去,先看两个数:running 和 waiting。
running 小、waiting 大,说明容量不够,请求在排队。这时候该省显存:量化、缩短上下文、换 GQA 模型。
running 到顶、GPU 利用率接近 100%,说明带宽或算力吃满了。这时候该换硬件,或者砍负载。
两种情况的解法相反,判断错了就是白花钱。
练习
六道题,先闭卷,再翻 ANSWERS.md。
- batch=4 时每步读多少 GiB?TPOT 多少?总吞吐多少?
- 为什么 batch 从 1 加到 8,总吞吐涨了,首字延迟却可能更差?
- 一条请求的输出从 512 涨到 2K,batch 的收益会变大还是变小?
- 找错:"batch 越大,服务越快。"
- running 一直很小、waiting 一直在涨,你该先改什么?给出两个方向。
- 用 llama.cpp 的
-np或 vLLM 的并发参数,batch 1、4、8 各压一次,记录吞吐、TPOT、P99。
这一章就记一句
batching 让多条请求共享一次权重读取:权重只读一遍,缓存各读各的。batch 从 1 到 14,吞吐涨三倍半,单条出字间隔慢四倍。收益停在"权重不再是读取大头"那个点。
真只记一句:吞吐和延迟是同一个旋钮的两端,batch 就是那个旋钮;调它之前,先说清你要的是总产能还是单条体感。
下一章解决一个 batching 自己带出来的问题:二十条请求长短不一,谁先跑、谁等着、显存不够时扔掉谁。