最近在尝试用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 条你这情况我太熟了,之前用vLLM跑7B也卡在类似瓶颈上。先说结论,问题大概率不是版本,而是你的平均1.5k token输入太长了,这会让prefill阶段的计算占比特别高,而vLLM的continuous batching对长prompt的调度效率会明显下降,GPU利用率自然上不去。
你可以先看看压测的时候,是不是有很多请求都挤在prefill阶段,decode阶段的batch size反而很小。如果是的话,试试把max_num_seqs调低到64或者128,同时把max_model_len限制在2048或者更短,这样能强制vLLM更激进地做抢占和调度,有时候反而能提升QPS。
另外,检查一下你的压测工具是不是并发数开得太低,比如才几十路并发,那GPU根本来不及吃满。建议把并发拉到200以上,观察下吞吐和延迟的曲线。还有个小技巧,开一下vLLM的--enable-prefix-caching,如果你的prompt有公共前缀(比如system prompt),能省不少重复计算。
如果还不行,就换一下调度策略,vLLM新版本有--scheduling-policy=priority或者--use-v2-block-manager,有时候默认策略对长序列不友好。最后实在不行,可以考虑用FP8或者AWQ量化,显存宽裕了,能塞更多batch,GPU利用率也会上去。别急着怀疑硬件,先把这些参数都试一遍再说。
显存跑满但算力闲置,大概率是prefill阶段在排队,decode阶段又在等显存释放,你试试把max_num_seqs降到64甚至32,然后把--enable-chunked-prefill打开,让prefill和decode交错起来。另外1.5k的prompt确实偏长,这会让首token延迟变高,QPS自然上不去,可以测一下短prompt对比,看看是不是主要瓶颈在这。版本方面建议至少升到0.5.x,老版本对长序列的调度优化差很多。
A100跑7B才10 QPS的话,瓶颈大概率不在vLLM参数上,而是prefill算力被长prompt吃掉了。1.5k tokens的输入,decode阶段每个token只算一次,但prefill要算1500次,GPU利用率低但显存满正好说明计算没饱和。建议先用短prompt(比如128 tokens)压测对比下,如果QPS能翻几倍那就确认是prefill问题。另外可以试试开--enable-prefix-caching,如果业务里prompt有公共前缀能省不少重复计算。还有你max_num_seqs=256配A100显存可能太小了,单卡建议128以内,不然显存碎片化也会影响吞吐。
说实话你这个问题我上个月刚踩过坑,最后发现根本不是参数的事。7B模型单卡A100跑30%利用率,大概率是prefill和decode阶段互相拖后腿了,你平均1.5k的prompt长度其实挺要命的,长prompt会让prefill占用的算力比例暴增,而vLLM默认的调度策略在长输入场景下特别容易让GPU空转。建议你先把max_num_seqs降回64试试,同时打开--enable-chunked-prefill,这个开关能显著缓解长prompt的阻塞问题。另外我注意到你显存跑满了但利用率低,很可能是因为KV cache占了太多显存导致实际batch size被限制住了,用--gpu-memory-utilization调成0.85左右,留点余量给计算。还有个小细节,如果你用的是比较老的vLLM版本(比如0.4.x),建议直接升到0.6.x,他们新版的continuous batching改进非常大,我升级后同参数下QPS直接翻倍了。最后你测压的时候并发线程数是多少?如果压测工具本身不够猛,也会让服务端看起来没吃饱。可以先跑个简单的固定并发测试,比如100并发发同样长度的请求,看看CPU线程数和GPU kernel占用情况再决定下一步怎么调。
prompt长度影响很大,1.5k tokens的prefill算力全耗在这了,试试把max_num_seqs调低到64看延迟和吞吐变化。
之前遇到过类似情况,问题大概率出在显存跑满但算力闲置——你max_num_seqs拉到256但平均1.5k token的prompt会让prefill阶段占掉大量显存,decode阶段的batch反而上不去。建议把max_num_seqs降到64左右试试,同时看看是不是vLLM版本低于0.4.0,老版本对长序列的调度优化差很多。另外可以开一下--enable-chunked-prefill,把长prompt拆块处理,GPU利用率应该能明显涨。
prompt长度这块影响确实不小,1.5k tokens的输入会让prefill阶段占比变高,而vLLM对长上下文的调度效率本来就不如短文本。你可以试试把max_num_seqs调低到64或128,同时开一下--enable-chunked-prefill,让prefill和decode交错执行,说不定能拉满利用率。另外确认下是不是用了最新版vLLM,老版本对Qwen2.5的attention优化差挺多的,我上次升级后同配置QPS翻了一倍。还有个歪招,压测时把并发数往上怼,有时候是客户端侧没打满,服务端其实还有余量。
prompt长度确实是个关键因素,1.5k tokens会让prefill阶段占比太高,而vLLM对长输入的调度效率没那么理想,试着把max_num_seqs调低到64或128,同时开启continuous batching看看,另外检查下是否开了--enable-prefix-caching,复用前缀能省不少算力。还有个细节,A100上7B模型显存跑满但利用率低,很可能是卡在CPU反序列化或tokenizer上,用--use-v2-block-manager或升级到最新版vLLM(0.6.3+)会有明显改善。你压测用的并发数是多少?如果并发线程太少,QPS自然上不去,建议多跑几轮不同并发梯度对比下。
显存跑满但算力闲置这个现象挺典型的,大概率是显存带宽或者KV cache的分配策略卡住了瓶颈。你平均prompt 1.5k tokens确实不算短,但更关键的是得看看生成的回复长度是多少,如果输出也很长,那prefill和decode阶段的计算量完全不在一个量级上,vLLM默认的调度策略可能会让GPU频繁在等待新batch和计算旧seq之间切换。另外max_num_seqs=256对7B模型来说有点激进,这会导致每个seq分到的显存碎片化,反而影响吞吐,建议先降到64或128试试,同时把--gpu-memory-utilization调到0.9以上,给KV cache留足空间。还有一个容易忽略的点是vLLM的continuous batching对请求到达模式很敏感,如果你压测用的是同步短请求轰炸,不如试试用异步客户端发不同长度的混合流量,看QPS会不会自然涨上去。版本问题也不能排除,0.6.x和0.7.x的调度器改动很大,如果还在用老版本建议直接升级最新release,顺带看看官方文档里对--max-model-len和--max-num-batched-tokens的推荐值。最后排查下是不是CPU喂数据跟不上,比如tokenizer和预处理都在同一线程里跑,这也能解释为什么GPU空转但显存满载。
这波大概率是prefill和decode混压导致的,1.5k token的prompt把算力都吃在prefill上了,试试把max_num_seqs调低点或者分开测一下。
破案了,大概率就是prompt长度的问题。1.5k tokens的输入,prefill阶段占用的算力远高于decode,vLLM虽然优化了这块但单卡A100算力就那么多,显存跑满说明KV cache占满了,但GPU没活干说明在等显存释放。你可以试着把max_num_seqs调小到64,同时开一下--enable-chunked-prefill,把长prompt拆成小块和decode阶段混跑,QPS应该能明显上来。另外确认下vLLM版本,0.4.0之前的版本对长序列支持很差,升到最新版再试。
prompt长度影响确实大,1.5k tokens相当于把并发都卡在prefill上了,试试把max_num_seqs调低或者开continuous batching看看。
显存跑满但利用率低,大概率是显存分配和KV cache没平衡好,先查下vLLM的日志看有没有swap或碎片问题。
prompt太长确实是关键,1.5k tokens会卡在prefill上,试试把max_num_seqs调低点或者开continuous batching看看。
你显存满但GPU利用率低,八成是显存带宽瓶颈了,长prompt下这很正常,换短一点的输入测测就能验证。
1.5k的prompt确实会吃掉大量prefill时间,而且显存跑满说明kv cache占了太多预算,留给decode的并发就少了。你可以试试把max_num_seqs降回64左右,同时开一下--enable-prefix-caching看有没有效果。另外QPS这事跟输出长度也强相关,你测的时候平均生成多少token?如果输出也长,那10的QPS可能真不一定是配置的锅,vLLM版本太旧也会有影响,建议升到0.6.x再对比下。
这情况我遇到过,问题大概率不在vLLM配置上,而是你的prompt太长直接把显存带宽吃满了。1.5k tokens的输入,prefill阶段计算量比decode大很多,GPU利用率看着低但实际算力都耗在等待内存读取上了。建议你试试把max_num_seqs调低到64或者32,再开一下--enable-chunked-prefill,让prefill和decode交错执行,QPS应该会有明显提升。另外确认下vLLM版本,0.4以上对长序列的调度优化差别挺大的。
显存跑满但算力闲置,大概率是显存带宽瓶颈而不是vLLM参数问题。你平均1.5k tokens的prompt确实偏长,prefill阶段会占大量显存带宽,建议看看prefill和decode的耗时占比,如果prefill占了80%以上,QPS上不去很正常。可以试试把max_num_seqs调小到64或32,同时开一下--enable-chunked-prefill,让prefill和decode交错执行,说不定能拉起来。另外确认下是不是用了最新版vLLM,老版本对长序列的调度优化差不少。
1.5k的prompt长度确实很伤,prefill阶段算力开销大但利用率上不去,你可以试着用--enable-chunked-prefill把prefill和decode拆开调度,应该能明显提QPS。另外max_num_seqs=256在长上下文下反而容易让显存碎片化,试试降到64或者128,配合--max-model-len调低点说不定有惊喜。vLLM版本老的话也建议升到0.6.x,之前修过不少长序列下的调度bug。
显存跑满但利用率低,大概率是prefill和decode阶段互相卡住了,1.5k的prompt确实偏长,建议把max_num_seqs调小到64试试,同时开一下--enable-chunked-prefill,让prefill和decode能重叠执行。另外你用的vLLM是哪个版本?老版本对Qwen2.5的支持有问题,直接升到0.6.3+会有明显改善。还有个小技巧,把--gpu-memory-utilization设成0.9,给KV cache留点余量,别让显存顶满导致recompute。
我遇到过类似情况,问题多半不在vLLM版本。显存跑满但利用率低,大概率是prefill阶段卡住了,1.5k的prompt在7B上算挺长的,可以试试把max_num_seqs调小到64左右,给每个请求多留点显存做KV cache,同时开一下--enable-prefix-caching看看。
另外你压测用的什么工具?如果是单线程发请求,QPS上限就被客户端锁死了,可以试试用wrk或ghz多并发打,不然vLLM再优化也吃不满。我上次就是栽在这上面,换了压测方式后利用率直接翻倍。
看到你说显存跑满但GPU利用率只有30%,我第一反应是卡在prefill阶段了。1.5k的prompt长度对7B来说确实不算短,你试过把max_num_seqs降下来吗?比如调到64或者32,有时候并发太高反而会让显存带宽成为瓶颈,导致计算单元在等数据搬运。
另外你用的vLLM版本是哪个?老版本对continuous batching的调度优化差很多,尤其Qwen2系列要用0.6以上的版本才行。你可以试试把--gpu-memory-utilization设到0.9,然后开--enable-prefix-caching,如果测试数据里有很多共享前缀的话效果会很明显。
还有一个容易忽略的点,压测工具本身的并发模型有没有问题?如果请求是同步等待响应再发下一个,那QPS上限就被RTT锁死了,实际服务端根本没吃满。建议用wrk或者ghz这种支持异步压测的工具,直接打满连接数看看。
最后确认下你的微调有没有改过模型结构,比如加了额外的attention层或者自定义算子,那可能会让vLLM的优化失效。我碰到过类似情况,最后是重新用官方tokenizer和配置文件导出模型才解决。先按这几个方向排查,大概率能找到原因。