最近在折腾本地部署,机器是4090 24G,想用VLLM跑Qwen2.5-7B-Instruct做API服务。参考了官方文档,设置了--max-model-len 4096,--gpu-memory-utilization 0.9,还用--enforce-eager关闭了CUDA graph。但启动后只要并发请求一多(大概5-6个),显存直接拉满然后OOM,日志里报的是torch.OutOfMemoryError。试过降低max-num-seqs到2,虽然不崩了,但吞吐量感觉还不如直接用transformers。我看别人说7B模型24G挺轻松的,是不是我的KV Cache配置有问题?或者该用AWQ量化版本?求有经验的大佬指点一下,先谢过了。
VLLM跑Qwen2.5-7B一直OOM,是我的显存策略有问题吗?
全部回复
共 15 条说个可能被忽略的点,你虽然设了max-model-len 4096,但VLLM默认会按这个长度预分配KV cache,7B模型在24G上其实余量没那么大。我试过把--max-num-seqs降到1,再把--max-model-len砍到2048,并发还是能撑到6-7个不崩,吞吐比transformers强不少。另外你检查过是不是有别的进程占显存吗?比如桌面环境或者监控工具,有时候就差那几百MB。
4090跑7B按理说确实不该这么紧张,你试试把--gpu-memory-utilization降到0.8或者干脆别设,让vllm自己算KV cache的容量。还有个坑是vllm默认会给每个seq预留很多KV cache空间,你并发5-6个时可能缓存碎片化严重,可以加--max-num-batched-tokens限一下batch总token数。另外确认下是不是用了最新的vllm版本,旧版本对Qwen2.5的padding长度计算有bug,会多占不少显存。
4090跑7B按理说真不该这么憋屈,我怀疑问题出在vllm默认给每个seq预留的KV cache上,你试试把--kv-cache-dtype改成fp8,或者手动设个--max-num-batched-tokens,别让它自动算。另外并发5-6个就炸有点反常,看看是不是pytorch版本和vllm不匹配,我之前升到2.4.0之后OOM频率明显低了。如果还不行,干脆用--enable-chunked-prefill,虽然单请求慢点但至少不崩,吞吐比transformers还是强不少的。
4090跑7B按理说真不该这么难受,不过你试试把--gpu-memory-utilization降到0.8以下,VLLM默认会预留一部分给CUDA context和torch本身,0.9反而容易在峰值时炸。另外--max-model-len设4096其实偏保守,但KV cache是按token数动态分配的,你并发5-6个时每个序列的长度可能远超预期,建议用--max-num-seqs 4配合--max-num-batched-tokens 4096看看。还有个坑是VLLM对Qwen2.5的attention实现可能没完全优化,可以开个--kv-cache-dtype fp8试试,能省不少显存。
试试把--kv-cache-dtype改成fp8,能省不少显存,我这边同样配置跑8B都没炸过。
重点看下max-num-seqs和KV cache的显存分配,24G跑7B不OOM的话试试把gpu-memory-utilization降到0.85,留点余量给并发峰值。
4090跑7B按理说真不该OOM,你这配置看起来问题不大,倒是--gpu-memory-utilization 0.9配--enforce-eager组合有点怪,eager模式本身显存碎片化更严重,试试把utilization降到0.8或者干脆不设,让vllm自己算。另外并发5-6个才崩,大概率是prefill阶段峰值显存爆了,可以开--enable-chunked-prefill,把长请求拆成小块,能平滑显存占用。至于max-num-seqs降到2吞吐反而不如transformers,正常,因为vllm优势在连续批处理,序列太少发挥不出来,建议还是保留4左右,配合chunked prefill试试。
24G跑7B按理说确实不该这么狼狈,我怀疑问题不是出在显存总量,而是你那个gpu-memory-utilization设太高了。0.9意味着VLLM会预先吃掉21.6G作为KV cache的预留池,但Qwen2.5-7B的权重加激活本身就要占掉差不多13-14G,剩下给KV cache的余量其实很紧,并发一上来每个sequence分到的cache块就少得可怜,反倒容易触发碎片化OOM。我之前用3090试过类似配置,把utilization降到0.75,max-num-seqs保持4,反而稳定很多,吞吐也没掉太多。另外你既然关了CUDA graph,那enforce-eager带来的显存节省其实很有限,主要省的是内存带宽,不如考虑开个--enable-chunked-prefill,把prefill阶段的长序列拆开,能有效降低瞬时峰值。还有个容易忽略的点,Qwen2.5-7B的默认rope_theta很大,长上下文下KV cache会膨胀得比预期快,你试试把max-model-len再砍到2048看看,如果业务允许的话。transformers的吞吐低是正常的,毕竟动态图逐token前向,但VLLM的continuous batching优势必须建立在cache池够用且块分配合理的基础上,你现在明显是池子撑太满导致OOM,而不是模型本身吃不下。
4090跑7B按理说确实不该这么惨,你试试把--max-model-len再砍到2048,同时把--gpu-memory-utilization降到0.85,给PyTorch留点喘息空间。另外VLLM的KV cache会按最大序列长度预分配,你并发5-6个请求时每个都占满4096的话,显存瞬间就爆了,这比transformers动态分配要激进得多。还有个坑是--enforce-eager反而可能让显存碎片化更严重,我上次关掉它之后OOM反而少了,你可以对比下。
24G跑7B按理说确实不该这么憋屈,但你这情况我太熟了,八成不是显存策略的锅,是VLLM默认的KV Cache预留和PagedAttention的block粒度在捣鬼。你设了0.9利用率,但VLLM会先按max-model-len把整块显存预分配给KV cache池,而Qwen2.5的注意力头多,每个token的KV占用比想象中大,5-6个并发seq同时满长度生成时,池子瞬间就爆了。我建议你把gpu-memory-utilization降到0.75试试,别让KV cache吃太满,留点余量给激活值,同时把--max-model-len调到2048看看,虽然输出长度受限,但吞吐反而可能上来——7B模型本身生成不了多长代码,很多场景2048够用。另外别用enforce-eager,这玩意儿虽然省显存但会让计算图每次重新编译,反而拖慢速度,不如开着CUDA graph然后把max-num-batched-tokens调成2048,让VLLM自己调度batch。你提到transformers反而更稳,我猜是因为它默认用梯度检查点,VLLM为了极致吞吐缓存了所有中间激活,这是设计取舍。最后检查下是不是装了最新版VLLM,之前有几个版本对Qwen2.5的sliding window attention支持有bug,会多算一倍KV,升到0.6.3+可能直接解决。
4090跑7B理论上确实不该这么吃力,但vllm对显存的占用大头往往在KV cache预留上,你试过手动指定--kv-cache-dtype fp8或者调小--max-num-batched-tokens吗?另外并发5-6个请求时如果每个请求的prompt都很长,即使max-model-len设了4096也可能因为实际sequence长度波动触发峰值显存暴涨。我之前遇到过类似情况,最后是把--block-size改成8才稳住,你可以对比下不同block-size下的显存曲线。还有个小细节,--enforce-eager虽然省了graph内存但会降低计算效率,反而可能让单请求占用更高,建议开回默认再叠加--num-gpu-blocks手动限一下看看。
我之前也遇到过类似情况,问题大概率不在KV Cache,而是VLLM默认会给每个sequence预留的显存空间偏大,尤其并发一多就炸。你可以试试把--max-num-batched-tokens调低点,比如4096,同时配合--swap-space 8,给CPU offload留点余地。另外如果只是做API服务,其实用--quantization awq加载4bit量化版能省不少显存,吞吐反而可能更高。你那个max-num-seqs=2感觉确实太保守了,不如先查一下nvidia-smi看看到底是活跃分配还是内存碎片。
4090跑7B按理说确实不该这么惨,你试试把--gpu-memory-utilization降到0.85,然后给--max-num-seqs设成4,同时确认下是不是pytorch版本和vllm不匹配导致显存碎片化。我之前也遇到过类似情况,换了最新版vllm之后,同样的配置直接能扛住8个并发,而且把--swap-space设成8也能缓解一下。另外你检查下是不是Qwen2.5的attention实现默认用了GQA,这个在7B上反而可能更吃显存,可以试着加个--kv-cache-dtype fp8看看效果。
4090跑7B按理说真不该这么憋屈,我怀疑问题不全在显存策略上。你那个--max-model-len 4096看着没问题,但Qwen2.5的Attention实现有GQA,KV cache占用其实比MHA小不少,24G理论上能塞下挺多并发。会不会是VLLM的--gpu-memory-utilization 0.9在配合--enforce-eager时反而触发了某些碎片化分配?我遇到过类似情况,关掉CUDA graph后显存分配变得特别碎,并发一高就炸,后来改成0.85加--swap-space 8反而稳了。
另外你对比transformers吞吐量这个点挺有意思,但注意VLLM的调度是continuous batching,并发低时单请求延迟反而可能更差,它强在整体吞吐。如果max-num-seqs降到2都觉得慢,那可能瓶颈不在显存而在线程数或CPU侧prefill,试试加--num-scheduler-steps或者调整--max-parallel-loading-workers。还有一个坑,你模型加载时是不是用了--dtype auto?有些情况下会自动落到float32,显存直接翻倍。可以强制--dtype bfloat16看看峰值能降多少。
最后问一句,你日志里除了OOM有没有提示具体的显存分配失败发生在哪个tensor上?如果是KV cache那还好调,如果是activation那可能和--max-num-batched-tokens有关系,默认值在某些版本里会飙得特别高。我之前用VLLM跑同系列模型也折腾了很久,最后是升级到0.6.x并手动设了--max-num-batched-tokens 8192才舒服,你可以试试。
试试把--max-model-len降到2048,7B模型默认的KV cache头数吃显存很凶,24G跑长上下文本来就紧。