最近在尝试用vLLM部署一个微调过的Qwen2.5-7B模型,单卡A100,开了tensor parallel=1,max_num_seqs设到了256。压测的时候发现QPS只能到10左右,GPU利用率一直在30%晃荡,显存倒是跑满了。我看别人都说7B能跑到30-50 QPS的,是不是我参数设置有问题?还是vLLM版本太旧?或者是不是我的prompt太长(平均1.5k tokens)导致的?有没有大佬能指点一下,该从哪里排查?感谢!
用vLLM部署7B模型,QPS上不去,GPU利用率才30%怎么回事?
全部回复
共 134 条看到你说显存跑满但利用率才30%,我第一反应是prefill和decode阶段的比例问题。你平均1.5k的prompt长度,在batch size大的时候,prefill计算量会占掉大量时间,但这时候GPU的算力利用率其实是不饱和的,因为attention部分的内存带宽瓶颈更明显。可以试试把max_num_seqs降到64或者128,同时看看是不是有大量请求同时进来导致显存里塞了太多中间状态,反而限制了并发调度。
另一个我踩过的坑是vLLM的continuous batching策略,如果你的请求到达不是均匀分布,而是突发性的,那默认的调度参数可能不够激进。建议观察一下压测时的token吞吐量(tokens per second),如果这个数值也不高,那问题大概率在kernel层面,比如是否用了FlashAttention2(vLLM里要显式开启),以及你的CUDA版本和PyTorch版本是不是匹配。
还有个小细节,A100上虽然显存够大,但7B模型如果用了fp16其实只占14G左右,你显存跑满说明KV cache可能占了很多。可以算一下max_model_len和max_num_seqs的乘积,如果KV cache预留太大,反而会挤占有效计算的空间。另外你确认过是否开了--enforce-eager模式吗?有时候用CUDAGraph能提升不少小batch的利用率。
最后问个实际的问题,你那10 QPS是用什么工具压测的?如果是简单循环发请求,可能线程数不够导致请求排队,而不是模型本身跑不动。我之前用wrk和自定义脚本测出的结果能差一倍。建议先用vLLM自带的benchmark脚本跑一下,排除掉外部网络和客户端瓶颈,再回头调参数。
显存跑满但算力闲置,这个现象其实挺典型的,大概率不是vLLM版本的问题,而是你的请求特征跟max_num_seqs的配置不匹配。1.5k tokens的prompt加上7B模型,prefill阶段的计算量其实很大,但decode阶段又很轻,如果并发请求没把prefill阶段填满,GPU就只能在等待中摸鱼。你可以试试把max_num_seqs调低到64或者32,同时把--max-model-len设成4096,别让显存被无效的padding占满,有时候这反而能提升吞吐。另外,压测工具那边也别用那种一次性发完所有请求的方式,改成持续注入请求,保持队列里始终有活干,不然GPU会周期性空转。还有个小细节,检查下--gpu-memory-utilization是不是默认值0.9,如果设太高导致KV cache频繁换出,也会拖累QPS。我之前遇到过类似情况,最后发现是输出长度限制设太短,模型不停触发早停,反而浪费了prefill的算力。你先用vllm的benchmark脚本跑一下官方配置对比看看,排除掉微调模型本身有没有引入奇怪的操作,比如自定义attention之类的东西。
这题我熟,先别急着赖vLLM版本,你列的几个嫌疑点里,prompt长度其实是最大变量。1.5k tokens的输入,prefill阶段计算量是decode的好几倍,而且vLLM的continuous batching在这种长输入场景下,调度效率会明显下降,GPU利用率低恰恰说明计算和显存搬运没重叠好。你可以试试把max_num_seqs降回64或32,同时开一下--enable-chunked-prefill,让prefill和decode交错执行,很多情况下这招能直接拉满利用率。另外QPS 10如果是纯generation(输出token数)的话,那还要看输出长度,如果输出也有几百token,那这个数其实不算离谱。还有个小坑,A100上7B模型如果没开--gpu-memory-utilization到0.95以上,显存跑满但实际可用KV cache反而可能不足,导致频繁抢占。最后建议你装个nvtop或者用nvidia-smi dmon盯一下SM占用和显存带宽,如果SM活动低但带宽高,那就是prefill瓶颈,如果是SM活动高但等待多,那就是调度问题。先改这几个参数跑一轮,大概率能翻倍。
这情况我遇到过,1.5k的prompt确实是关键瓶颈,decode阶段还好,但prefill部分会吃掉大量算力,你试试把max_num_seqs调低到64,同时开一下--enable-chunked-prefill,应该能明显改善。另外检查下vLLM版本,0.4.2之前对长序列支持很差,建议升到0.5.x以上。还有个思路,如果业务允许,把prompt做下精简或改用更快的tokenizer,有时候输入长度砍一半,QPS能翻倍。
说实话你这个问题大概率卡在显存跑满但算力闲置上,max_num_seqs=256对7B来说太激进了,KV cache把显存吃光后batch反而排不进去,建议降到64左右再试试。另外prompt平均1.5k也不短,prefill阶段会明显拉低吞吐,你可以看下vLLM的日志里prefill和decode耗时占比,如果prefill占大头就考虑开chunked prefill或者把max_model_len调小。版本的话尽量升到0.6以上,老版本对continuous batching支持差很多,不过我猜你主要问题还是batch没跑起来。
1.5k的prompt长度确实是个坎,prefill阶段计算量上去了但decode阶段又吃不满,建议先看下vLLM的server端日志里prefill和decode耗时占比,大概率prefill卡脖子了。另外max_num_seqs拉到256对7B来说有点激进,反而容易造成显存碎片化,试试降到64或者128,同时把--max-model-len调小到2k,让KV cache留更多余量。版本太旧也可能有影响,至少升到0.5.x以上,老版本对长上下文的调度优化差不少。如果还不行,开个--enable-chunked-prefill试试,能缓解长prompt导致的burst阻塞。
显存跑满但算力闲置,典型的prefill和decode争抢资源,试试把max_num_seqs降到64看曲线变化。
显存跑满但算力闲置大概率是prefill太长卡住了batch,试试把max_num_seqs调低到64再配个chunked prefill。
prompt长确实吃显存带宽,试试把max_num_seqs调低到64,同时对比下短prompt的吞吐差异。
说实话你这情况我太熟了,之前我部署13B的时候也卡在同样的问题上,后来发现是max_num_seqs设太高反而起了反作用。你想想,虽然vLLM号称能管理256个序列,但每个序列的KV cache都要占显存,显存跑满说明都花在缓存上了,真正留给计算的batch反而没那么多,GPU自然就闲着了。我建议你把max_num_seqs降到64试试,同时看看是不是没开continuous batching的开关,有些旧版本默认是关的。另外你提到prompt平均1.5k tokens,这确实是个大坑,prefill阶段的计算量是随长度线性增长的,而且prefill和decode混在一起会让调度更吃紧,你可以试试用--enable-chunked-prefill把长prompt切块处理。不过最让我怀疑的还是你的压测方式,是不是用的固定并发数?如果是的话,可能你压测客户端本身就成了瓶颈,建议用wrk或者ghz这类工具扫一下不同并发下的QPS曲线,看看是不是到某个并发度就上不去了。还有个小细节,你检查过vLLM日志里有没有显示“GPU KV cache usage”之类的警告吗?如果缓存分配策略不对,也会导致利用率上不去。我先说这些,你回去调一下参数,有结果了告诉我一声。
大概率是prompt长度拖累的,1.5k tokens会让prefill占比过高,试试缩短到512看QPS能涨多少。
显存跑满但利用率低,像是max_num_seqs太大导致排队等待,调成64或128看看吞吐有没有改善。
1.5k的prompt长度确实是个关键点,prefill阶段会吃掉大量算力,但GPU利用率低更可能卡在显存带宽上。你可以试试把max_num_seqs降到64,同时开一下--enable-chunked-prefill,让prefill和decode交错执行,往往能明显拉高利用率。另外确认下vLLM版本,0.4.2之前有个调度bug,更新到0.5.x+会有很大改善。我上次遇到类似情况,最后发现是磁盘加载检查点太慢拖了首token延迟,你顺手测下TTFT是不是偏高。
说实话你这个情况我太熟了,之前用vLLM跑13B也踩过一模一样的坑,GPU利用率上不去但显存满了基本就是卡在prefill阶段了。平均1.5k tokens的prompt确实偏长,7B模型prefill的计算量在这种长度下会吃掉大量时间,而decode阶段反而占比很小,导致QPS被拖垮。你可以先试试把max_num_seqs调小一点,比如64或者32,因为256个并发序列同时排队,每个都要做长prefill,反而会造成GPU算力碎片化。另外看看vLLM版本,老版本对continuous batching的调度策略很蠢,建议至少升到0.6.x以上,新版有chunked prefill选项,能显著改善长prompt场景下的吞吐。还有个可能被忽略的点,你的压测工具是不是真的在并发发请求?如果客户端有阻塞或者keep-alive没配好,可能实际并发数远低于预期。我建议你开一下vLLM的--verbose日志,看看每个step的耗时分布,如果prefill占比超过60%,那就基本确认是这个原因了。实在不行可以试试把prompt截断到512或者用更小的max_model_len,虽然会影响效果但至少能验证方向。
这情况大概率不是版本问题,7B在A100上理论算力绰绰有余,瓶颈基本卡在显存带宽上。你1.5k的prompt是主要嫌疑,生成阶段每个token都要把所有KV cache读一遍,长上下文直接拖慢吞吐。建议把max_num_seqs调低到64试试,同时用--enable-chunked-prefill把prefill和decode拆开,大概率能提一波。另外看下是不是开了--enable-prefix-caching,如果请求前缀重复不多,这功能反而会占额外显存。
说实话我觉得大概率不是vLLM版本的问题,你这个场景更像是prefill和decode阶段严重不平衡导致的。平均1.5k tokens的prompt相当长了,在A100上prefill阶段算力是够的,但decode阶段是典型的memory-bound,两个阶段混在一起调度,GPU利用率自然上不去。你可以试试把max_num_seqs调低一点,比如32或者64,虽然看着吞吐会降,但实际QPS可能反而上来,因为减少了显存碎片和调度开销。另外你确认一下是不是开了continuous batching的开关,有些老版本默认没开,这个对并发场景影响特别大。还有一个思路,如果你prompt长度分布比较集中,可以试试用--enable-chunked-prefill,把长prompt切成小段跟decode混排,这样能显著提升GPU利用率。我之前用7B模型跑128并发、512 tokens的prompt,QPS能到40多,但换成2k tokens直接掉到15,所以你这个数据其实不算离谱。最后建议你开一下vLLM的metrics看下TTFT和TPOT的分布,如果TTFT特别高,那就是prefill排队问题,如果TPOT高,那就是decode吞吐瓶颈,排查方向完全不一样。
显存跑满但利用率低,大概率是显存带宽瓶颈了,1.5k tokens的prompt会让prefill阶段占比特别高,而vLLM对长输入的prefill优化一般,你可以试试把max_num_seqs调低到64看看,同时用--enable-chunked-prefill开一下分块预填充,应该会有明显改善。另外确认下你压测的并发请求数是不是真的打满了,有时候客户端并发不够也会让GPU空转。如果还不行,可以换最新版vLLM试试,他们最近对长序列场景做了不少优化。
显存跑满但利用率低大概率是prefill和decode互相挤占,加上1.5k的prompt让显存被KV cache吃光了,试试把max_num_seqs降到64或者128,同时开一下--enable-prefix-caching看有没有改善。另外确认下vLLM版本,0.4以上的调度逻辑差别挺大的,旧版本确实容易卡在30%左右。我上次遇到类似情况是prompt里带了很多重复前缀,把cache开了之后QPS直接翻倍。
这情况大概率不是vLLM版本问题,是你这prompt长度把显存带宽吃满了,1.5k tokens在7B上算下来prefill占比太高,GPU利用率自然上不去。你可以试试把max_num_seqs调低到64或者32,再开个continuous batching看看,另外压测别用单并发,多开几条线程模拟真实负载。我之前跑13B都遇到过类似瓶颈,最后是换成了更长上下文的模型才解决,你这QPS目标如果硬性要求,不如直接上量化版本或者换张H100。
你提到的prompt长度确实是个关键点,1.5k tokens对prefill阶段压力很大,QPS卡在10不奇怪。建议先试试把max_num_seqs调低到64或者128,有时候并发太高反而会让调度开销拖慢整体吞吐。另外可以看看vLLM版本,老版本对continuous batching的优化差不少,升到0.6.x以上通常有明显改善。如果方便的话,用固定短prompt(比如200 tokens)压测对比一下,能快速定位是不是输入长度的问题。
看到这个现象我第一反应是显存跑满但利用率才30%,大概率不是算力瓶颈而是调度瓶颈。你max_num_seqs调到256但平均prompt有1.5k tokens,这意味着prefill阶段的计算量被拉得很高,而decode阶段又因为序列太长导致显存被占满,batch size实际能塞进去的样本数反而被压缩了,GPU就在prefill和decode之间来回切换,利用率自然上不去。vLLM版本确实有影响,但更建议先看看是不是paged attention的block大小没调好,或者你的压测工具是不是并发请求数不够,导致vLLM的continuous batching没机会充分生效。另外可以试试把max_num_seqs降到64或128,同时把--max-model-len调小一点,比如4096,强制它更高效地利用显存来增加并发度,而不是把空间都留给长上下文。我之前跑13B模型遇到过类似情况,后来发现是prompt里重复前缀太多,加了个--enable-prefix-caching直接让QPS翻倍,你那个场景如果请求有公共系统提示词的话值得试一下。还有个小细节,A100上如果用的是PCIe版本而不是SXM,显存带宽也会限制decode速度,不过你利用率低应该不是这个主因。建议先用vLLM的benchmark脚本跑一下纯短prompt对比,如果短prompt能到40+ QPS,那问题就锁死在长文本处理上了。