你跟我说要搞大模型推理,不管是做AI Infra的、做算法优化的、还是准备上生成式AI业务的,最后都会卡在同一个问题上:这台GPU到底能扛多少并发?为什么一到长文本就慢得离谱?加钱换卡到底能不能解决问题?这问题我也纠结过很久,直到自己动手搭推理服务、逐帧扒性能数据之后才真正想明白:其实只要算清楚三个数字,绝大多数性能判断都能在脑子里直接给出来。这篇文章就是把我常用的三个公式掰开揉碎讲清楚,看完你至少能回答三个问题:一次前向推理的理论算力需求是多少?权重要不要从显存里读出来、读一次要多久?以及当前场景到底卡在算力还是带宽上。
这套东西是入门AI Infra最核心的一块拼图,适合刚接触大模型推理优化的工程师、想评估GPU选型的架构师,也适合那些被“吞吐上不去、延迟降不下来”折磨的部署同学。我会直接用7B模型作为例子,把数值一步一步算给你看,再结合我实际调nano-vllm和CUDA推理加速时踩过的坑来讲,保证不是干巴巴的公式堆砌。
1. 先搞清楚三个基本量:模型参数、单Token计算量、显存读写
理解算力上限和带宽瓶颈,第一步不是背公式,而是先把大模型推理这件事拆成“每生成一个Token需要做多少事”。这个“事”本质上就两件:第一,把权重从显存搬到计算单元;第二,做一堆矩阵乘法和激活计算。前者受限于带宽,后者受限于算力。你把这两个量分别摸清楚,瓶颈自然浮出水面。
1.1 参数规模才是所有估算的起点
任何估算都得从模型参数量开始。比如7B模型就是70亿个参数,如果是标准的FP16存储,每个参数占2字节,那么光权重就要占70×10^8 × 2 = 14GB显存。如果是BF16也一样是2字节,FP32是4字节,INT8是1字节。这个数在你脑子里要有概念,因为不管是算显存容量、算权重的IO开销,还是算各种量化方案的效果,全部都要从参数量出发。
我之前遇到过一种很常见的误判:有人觉得显存只要比权重大就行,结果一上服务就OOM。原因很简单,除了权重你还要算上KV Cache、激活值、CUDA context以及推理框架自身的开销。KV Cache这东西在后面会专门讲,它跟并发数和序列长度强相关,往往比权重复杂得多。所以先把参数量这个基数记牢了,后面的推导才有意义。
1.2 一次前向计算量:为什么人们常说“2×参数量×Token数”
大模型一次前向推理的计算量,业界共识是每个Token大约需要2 × 参数量的FLOPs。这个2不是拍脑袋来的,它来源于Transformer里Linear层的矩阵乘法:一次矩阵乘涉及两个操作——乘法和加法,对每个参数来说,一次前向刚好对应约2次浮点运算。这个规律对所有基于Transformer架构的模型都成立,不管你是7B还是70B,不管你是Llama还是Qwen。
所以对于一个7B模型,生成一个Token的理论计算量就是:
FLOPs_per_token = 2 × 7 × 10^9 = 14 GFLOPs = 1.4 × 10^10 FLOPs那如果序列长度是2048个Token呢?我们得把Prompt阶段和生成阶段分开。处理Prompt(prefill阶段)是一次性把输入的2048个Token全部做前向,所以计算量是2048 × 14 GFLOPs ≈ 28.7 TFLOPs。而生成阶段(decode阶段)是逐个Token生成,每生成一个Token就做一次前向,生成长度为128个Token时就是128 × 14 GFLOPs ≈ 1.8 TFLOPs。
这两个阶段计算量差一个量级,它们对算力和带宽的需求形态完全不一样。很多人做推理优化时只盯着峰值算力,结果在decode阶段怎么优化都达不到预期,就是因为decode阶段根本不吃算力,它吃的是带宽。这个区别后面会反复提到,它是整篇文章的分水岭。
2. 第一个公式:算力上限——理论FLOPs与吞吐天花板
算力上限的意思很直接:GPU在一秒钟内最多能做多少次浮点运算。如果你知道了单Token的计算量,又知道了GPU的峰值算力,理论上两者一除,就能得到每秒钟最多能处理多少个Token,这就是吞吐量的理论上限。实际当然不可能跑满,但这个上限就像公路的最高限速,你不先知道它,就没法判断当前的车速到底还有没有提升空间。
2.1 GPU峰值算力怎么查:以A100和H100为例
以NVIDIA A100 80G为例,FP16算力(含Tensor Core加速)约为312 TFLOPS,也就是每秒3.12 × 10^14次浮点运算。H100 SXM的FP16算力约为989 TFLOPS(带稀疏化能到1979 TFLOPS,但我们做推理通常看稠密算力,也就是989)。4090大概在330 TFLOPS左右(FP16,Tensor Core)。注意,这些数字都只是“理论峰值”,实际应用中因为内存搬运、算子调度、访存冲突等问题,能跑到峰值的50%已经是相当不错的水平。
有了这个数字,算力上限的计算就很简单。以A100跑到理论峰值为例:
Token_per_second_A100 = 3.12 × 10^14 / (1.4 × 10^10) ≈ 22285 Token/s也就是说,如果A100的算力被100%用上,那么7B模型每秒钟最多能生成约2.2万个Token。实际当然跑不到这个数,实测一般在30%-50%利用率区间,也就是6000到11000 Token/s左右。注意这里没有任何框架层面的技巧,纯粹的物理上限。
2.2 prefill阶段:算力最容易成为瓶颈的地方
prefill阶段一次性处理长Prompt,计算量随Prompt长度线性增长,此时数据量很大、权重也能被反复复用,计算密度很高,因此这个阶段GPU的算力通常能跑得比较饱满。如果Prompt长度是2048个Token,前面算过FLOPs约为28.7 TFLOPs,那么在A100上理论耗时就是:
28.7 × 10^12 / (3.12 × 10^14) ≈ 0.092秒也就是说理想情况下,A100处理一个2048 Token的Prompt只需要92毫秒。但如果你用nano-vllm这类框架实测,往往会发现耗时要更长,原因在于实际解析、采样、注意力计算、框架调度等环节都会消耗时间,而不仅仅是矩阵乘。不过这个数字至少给了你一个基准线:如果实测超过200毫秒说明还有优化空间,如果超过500毫秒那基本说明算子或框架层存在问题。
prefill阶段的关键观察是:计算量跟序列长度成正比,但权重只需要从显存读一次。因此Prompt越长,计算密度越高,算力越容易饱和。这也是为什么长Prompt场景下,算力上限是首要关注对象。
2.3 decode阶段的算力远不是瓶颈
你可能注意到了,如果单看生成阶段,每生成一个Token只有14 GFLOPs,A100一秒能算2.2万个Token。这数字看起来高得离谱,但实际生成速度往往只有几十到几百个Token每秒,为什么?因为decode阶段每个Token的FLOPs太少,数据搬运的时间远远超过了计算时间。本质上,GPU每算一个Token不仅要搬权重,还要搬运KV Cache和中间激活,这些搬运操作占用了绝大部分时间。
说得直白点,decode阶段你把A100换成H100,算力翻了三倍,但生成速度可能只提升10%-20%。这不是在浪费钱,而恰恰说明你想错了方向——decode阶段拼的是带宽,不是算力。明白这件事之后,就该轮到第二个公式上场了。
3. 第二个公式:带宽瓶颈——权重要从显存读一遍才算得起来
如果在decode阶段算力不是瓶颈,那真正的瓶颈是什么?是显存带宽。GPU的计算单元和显存之间的数据通道宽度是有限的,而每次前向推理都必须把全部的权重从显存读出来。7B模型在FP16下是14GB,你要把14GB的数据搬进计算单元,内存带宽决定了这个搬运动作需要多少时间。在decode阶段,每个Token只做一次前向,每次都要完整读一遍权重,这就是瓶颈所在。
3.1 带宽需求公式:每个Token都要付的“过路费”
第二个公式比第一个还要简单,直接乘就行:
每Token带宽需求 = 权重字节数 = 参数量 × 每个参数的字节数7B模型FP16就是14GB。每生成一个Token,GPU至少要从显存搬运14GB数据。这里不考虑复用优化,因为decode阶段的矩阵乘是一行一行地做,权重没办法留在缓存里反复使用,每Token都得重新读。于是每生成一个Token,在A100 80G的2TB/s带宽下,理论耗时是:
14GB / 2TB/s = 0.007秒 = 7ms于是这台机器的decode理论上限就是1/0.007 ≈ 142 Token/s。这个数字是不是跟你在真实部署中看到的更接近了?是的,因为这才是decode阶段的真瓶颈。A100的算力上限能到每秒2.2万个Token,但带宽上限只有142 Token/s。哪个数字决定了你能不能把数字发出去?当然是小的那个。
3.2 用数据感受一下带宽对生成的钳制
这里多展开一点,因为很多人对这个7ms没有直感。假设你部署了一个7B FP16模型,用A100服务一个用户,理想情况下每个Token需要7ms,一秒钟能生成约142个Token。看起来日子挺好过的对吧?但如果同一时刻来了4个用户,权重还是那一份,每生成一个Token还是要读一遍14GB,带宽是共享的,四路请求都会挤这2TB/s。算下来每个用户分到的带宽就少了,全局吞吐依然是142 Token/s左右,分到每个用户头上就剩35 Token/s左右,肉眼可见地变卡。
我还真遇到过这样的场景:系统监控显示GPU利用率和显存占用都不高,但并发一上去生成慢得像蜗牛。排查了很久才发现是带宽被打满了,CPU侧看得见、GPU侧看不见,非常隐蔽。所以判断一个推理服务是不是带宽瓶颈,你就看一个指标:GPU的计算单元利用率高不高,如果计算单元利用率低但token生成慢,那十有八九是带宽卡脖子。
3.3 量化为什么能立竿见影
既然瓶颈在搬运14GB权重,那最直接的优化就是把权重体积缩小。INT8量化后权重变成7GB,带宽时间直接减半;INT4量化后变成3.5GB,带宽时间再减半。对应地在A100上理论生成速度从142 Token/s提升到285 Token/s再到570 Token/s,这是实打实的翻倍效果,而且不是靠“优化技巧”,是靠减小了搬运量。这也是为什么业界做推理服务普遍采用INT8或INT4量化的底层逻辑。
但量化不是没代价的。精度损失是一个隐性成本,更关键的是,很多算子在低精度下的CUDA Kernel效率不一定能达到FP16的峰值。我在用nano-vllm做量化推理实验时发现,INT8实际吞吐提升大概在1.6-1.8倍,达不到理论上2倍,原因就在这里。所以你可以理解成:量化帮你减轻了带宽压力,但算子的实际效率打了折扣。不过即便如此,带宽瓶颈场景下量化依然是最值得先做的优化之一。
3.4 多卡并行怎么改变带宽公式
这里有个很有趣的延展。你如果把模型切成两半,放到两张卡上,每张卡只需要读7GB权重,带宽压力直接减半。这就是张量并行降低带宽瓶颈的数学原理。但如果用流水线并行,每张卡都存完整的权重副本,那带宽压力并不会减小,只是把参数分布到不同阶段而已。所以你很常看到推理框架中张量并行用来解决大模型带宽瓶颈,而流水线并行更多是为了把模型放进显存。
选择并行方案时,记住一个要点:张量并行是直接削减单卡带宽压力的,流水线并行则更适合超大模型放不下的场景。前者是“把路拓宽”,后者是“把货分车”。不过要注意,张量并行会带来卡间通信开销,如果卡的互联带宽不够,可能得不偿失。一般两卡以内收益很可观,四卡以上就要用NVSwitch或者高带宽互联来支撑了。
4. 第三个公式:Roofline视角——算术强度帮你判断瓶颈方向
前两个公式分别告诉你算力上限和带宽上限,但你实际操作时往往想知道的是:当前这个场景到底是算力瓶颈还是带宽瓶颈?这时候就要用到第三个公式,它来自经典的Roofline模型。简单概括就是:把算力上限和带宽上限画在同一个坐标轴上,通过一个叫“算术强度”的指标来判断当前负载处于哪个区域。
4.1 算术强度的定义与计算
算术强度的定义是:单位数据量上执行的浮点运算次数,单位是FLOPs/Byte:
算术强度 = FLOPs / Bytes对于一个7B模型,decode阶段每个Token搬运14GB权重、执行14 GFLOPs,算术强度就是:
14 × 10^9 / (14 × 10^9) = 1 FLOPs/Byteprefill阶段,每个Token同样搬运14GB权重,但执行14 GFLOPs,算术强度也恰好是1,因此这个值对不同阶段是一样的。但要注意,这个1 FLOPs/Byte的数字只是个凑巧的恒定值,它会随量化位宽和上下文长度变化。如果上下文特别长,KV Cache的读写也会增加Byte数,算术强度会进一步降低。
4.2 算力拐点怎么算:一个除法解决
在Roofline模型里,判断一个负载是算力受限还是带宽受限的转折点,就是峰值算力除以峰值带宽。A100的算力和带宽分别是312 TFLOPS和2TB/s,所以:
转折点 = 312 × 10^12 / (2 × 10^12) ≈ 156 FLOPs/ByteH100的算力989 TFLOPS,带宽3.35TB/s,转折点大概是295 FLOPs/Byte。如果负载的算术强度远大于这个值,说明算力不够用、带宽有富余,属于算力受限;如果算术强度远小于这个值,说明带宽不够用、算力有富余,属于带宽受限。
而我们刚刚算过的7B模型算术强度是1 FLOPs/Byte,跟156和295比差了两位数量级,所以结论很明确:7B模型在A100/H100上做decode是极度带宽受限的。这也完美解释了一个现象:为什么你用FP16的7B模型在H100上,感觉跟A100差不多快。因为两者带宽仅差1.7倍,而算力差了3倍多,但你根本没用到那份额外的算力。
4.3 prefill为什么跟decode相反
如果你把prefill阶段也代入这个模型,情况会变得不一样。假设输入是2048个Token,算术强度的分子是28.7 TFLOPs,分母仍然是14GB权重,约等于2 FLOPs/Byte,还是远小于转折点,看起来也是带宽受限。但别急着下结论,prefill阶段的特殊性在于GPU可以把多个计算同时在流水线里跑,每个权重读出来不是只做一个Token的乘加,而是同时服务于2048个Token,相当于每个Token一次计算、但权重的读取被多次复用。
计算prefill时我们需要把批大小考虑进来,如果batch为1,2048个Token的prefill对应28.7 TFLOPs,除以14GB约等于2 FLOPs/Byte。但如果并发Batch达到16,这个算术强度直接变成32 FLOPs/Byte,虽然还是低于转折点,但已经接近算力受限的区间了。这就是为什么prefill阶段通常能跑出较高的算力利用率的原因:因为有batch复用,指令密度高了,数据搬运占比降低了。
5. 实操修正项:利用率、KV Cache、并发与长文本的连锁反应
公式是理想情况,实际部署还有几个修正项,尤其是GPU利用率、KV Cache显存、并发数和上下文长度,它们会一起改变最终的性能表现。如果你直接拿理想公式去做容量规划,一定会翻车。下面把这些修正项一个一个讲清楚。
5.1 GPU利用率为什么永远到不了100%
我一开始做推理加速时也天真地以为,理论算力上限就是目标,后来发现连50%都难。这里的核心原因有几个。算子层面,矩阵乘确实能跑到很高利用率,但非矩阵乘算子比如归一化、激活函数、注意力计算,它们的算术强度低,效率差。框架层面,Python调度、张量内存分配、图模式切换都会有延迟。硬件层面,SM的调度、缓存命中率、访存模式也会限制实际吞吐。
于是实践中我们引入了一个概念:MFU或CFU,也就是模型浮点利用率。正常优化的推理服务,MFU能到40%-60%就算非常优秀。所以你拿理论算力上限除以实际MFU,得到的才是可规划的吞吐值。举个例子,A100跑7B模型,理论每秒2.2万Token,按MFU 50%算,实际约1.1万Token/s;但如果考虑decode阶段带宽瓶颈,这个数值还会进一步被拉低。结论就一句话:先算带宽,再算算力,然后用MFU打折,三步走。
5.2 KV Cache才是隐藏的显存杀手
除了权重,KV Cache是另一个决定并发上限的大变量。它的计算公式是:
KV Cache显存 = 2 (K和V) × 层数 × 注意力头数 × 每头维度 × 序列长度 × 精度字节 × 并发数以7B模型为例,假设32层、32个注意力头、每头128维,FP16下2字节,序列长度2048,并发8,KV Cache约为:
2 × 32 × 32 × 128 × 2048 × 2 × 8 = 8.5GB8个并发就要吃掉8.5GB,而权重接下来还要占14GB,一张80GB显存的A100能装下的并发数也没那么宽裕。KV Cache的带宽消耗还会跟权重抢带宽,进一步拖慢生成速度。所以长上下文场景下,KV Cache不仅抢显存,还抢带宽,经常出现“显存明明还有,但新请求就是慢”的诡异现象。
5.3 长上下文和并发会让带宽瓶颈雪上加霜
一个容易被忽略的事实是,KV Cache的读写也会占用带宽。随着并发数和上下文长度增加,KV Cache的读写量会迅速增大,最终甚至超越权重读取的带宽需求。比如序列长度从2K提升到8K,KV Cache的带宽占用直接翻四倍,这时候decode的token生成速度可能从100降到40,哪怕你的权重大小完全没变。
我在做一次长文本推理压测时,把上下文从2K拉到8K,QPS掉了将近60%,起初百思不得其解,后来一测才发现带宽已经满了,而且其中相当大的一部分是KV Cache读写贡献的。所以规划长文本服务时,单纯看权重带宽指标是不够的,必须把KV Cache访存也估算进去。
5.4 Continuous Batching为什么能救命
既然并发会加剧带宽竞争,那为什么推理服务还需要大批量并行?因为对单个请求来说带宽浪费太严重。权重读出来14GB,只为一个Token服务,算术强度太低。Continuous Batching的核心思路是把多个请求拼在一起,共享权重读取、一起前向计算,这样14GB权重的读取成本被多个用户平摊,算术强度提升,GPU真正的算力才被利用起来。
用公式看更清楚:Batch为1时,decode算术强度1 FLOPs/Byte;Batch为16时,变成16 FLOPs/Byte。虽然这个值距离开论点的156还差得远,但至少大幅逼近算力区,吞吐能得到数量级提升。这也是为什么现代推理框架几乎都支持动态批处理,因为它直接让带宽瓶颈从根上缓解,而不是只在表层优化算子性能。
6. 从公式到工程决策:选卡、选量化、配并发该怎么落地
前面都是理论推演,最后把这些公式落到实际的工程决策上。你拿到一个推理任务,面对无休止的选型和参数调优,这3个公式正好能帮你快速砍掉不靠谱的选项。即便你不想深入CUDA开发和算子优化,这套估算方法也能让你在方案评审时腰杆挺直。
6.1 用一张表看不同方案的带宽与算力增减
我平时会做一张小表,把想对比的GPU型号、精度、参数量填进去,然后算每Token推理的带宽耗时、算力耗时和瓶颈状态。下面是一个7B模型、FP16与INT8对比的实际案例:
| 配置 | 权重大小 | 单Token带宽耗时(A100) | 单Token算力耗时(A100) | 瓶颈 |
|---|---|---|---|---|
| 7B FP16 | 14GB | 7ms | 0.045ms | 带宽 |
| 7B INT8 | 7GB | 3.5ms | 0.045ms | 带宽 |
| 7B FP16 + Batch=8 | 14GB | 7ms(共享) | 0.36ms | 带宽 |
| 13B FP16 | 26GB | 13ms | 0.084ms | 带宽 |
这张表的结论很清楚:7B模型无论怎么选,瓶颈几乎都是带宽;13B的带宽耗时直接翻倍,说明换大模型在单卡上的主要代价不是算力,而是带宽导致的速度下降。所以做选型时,你不能只看参数量翻倍算力需求翻倍,更要看权重体积翻倍导致的带宽瓶颈加倍。
6.2 结论:先减带宽、再升算力、最后才换规模
根据这套公式,我总结了一个铁律:大模型推理优化首先要解决的是“每次前向要搬运多少字节”的问题,然后才是“有多大算力”的问题。最优的操作路径是:
- 优先降精度:能INT8就别FP16,能INT4就别INT8,带宽瓶颈直接砍半再砍半。
- 再做批处理:通过Continuous Batching把请求拼起来,让权重复用次数多起来,提高算术强度。
- 再考虑换卡:如果上面两步做完还是慢,换更高带宽的卡往往比换更高算力的卡更有效。
- 最后才换模型:模型的参数量是你唯一的自由参数,但一般没人愿意降模型规格,但在吞吐上确实是立竿见影。
有一次我帮朋友调一个生成式服务的性能,他一开始想换4张H100来解决问题。我劝他先用INT4量化加动态批处理跑了一轮压测,结果成本翻了倍不假,但吞吐只用原来1/5的预算就达到了目标。公式只要算一遍,你就会发现换卡很多时候是最后手段,而不是第一选择。
6.3 长期主义视角:用nano-vllm验证你的估算
如果你想亲手验证这套估算模型,我强烈建议拿nano-vllm这类轻量级推理框架练手。它的优势是代码简单、结构透明,比直接在vLLM里debug更容易理解底层逻辑。你可以在nano-vllm里写一个小脚本,跑一个7B模型,分别在prefill和decode阶段计时,核对算力公式和带宽公式的预测值。
我当时就是在这个框架里改了量化位宽,观察了不同batch下的吞吐变化,还手动统计了KV Cache增长带来的带宽压力。这套实验做完,你对那些公式的印象会比看十篇文章都深。毕竟纸上得来终觉浅,AI Infra这个领域更是如此。
总的来说,算力上限和带宽瓶颈这套估算体系,核心就是:先背下参数量,再算每Token计算量和字节数,然后用Roofline判定阶段瓶颈,最后用MFU和KV Cache修正到实际值。这套方法我反复用,从选卡、配量化、调并发到长文本规划都没出过偏差。你把它记住,以后听到“吞吐上不去”的第一反应,不应该再是去调代码,而是先在脑子里过一遍公式,再动手排查。