最近在把我们微调过的Qwen2.5-7B部署到内网服务器上,用vLLM跑的,开了gptq量化。理论上7B模型int4量化后显存占用应该在5-6G左右,但实际部署后nvidia-smi一看,单卡A10直接吃了14G,吞吐也只有200 tokens/s不到。我已经设置了max_model_len=4096,也开了continuous batching,但感觉没起到什么作用。怀疑是不是上下文预填充阶段太吃显存,还是量化版本选错了?求有经验的大佬指点一下,或者有没有什么工具能定位显存到底被什么占住了?提前感谢。
部署7B大模型到生产环境,显存占用比预想高很多怎么办?
全部回复
共 90 条我之前也踩过类似的坑,gptq的4bit理论值跟实际跑起来差挺多的,尤其是kv cache和激活值在长上下文下会吃掉一大块。建议先用vllm的--gpu-memory-utilization调低点,同时开--enforce-eager关掉cudagraph看看是不是显存碎片问题。另外A10的带宽跑7B推理本来就不算快,200 t/s如果是在并发高的情况下其实正常,你可以试试单请求测下延迟。排查显存的话,用nvidia-smi dmon或者vllm自带的--profile看下分阶段占用,大概率是prefill阶段峰值爆了。
vLLM的KV cache默认会预分配很大一块显存,你max_model_len设了4096但batch和并发没限制的话,显存直接吃满很正常。建议先看下vllm serve启动日志里的KV cache预留量,试着调低--gpu-memory-utilization到0.6左右,顺便确认下你GPTQ的group_size是不是128,有些量化版本实际加载时会解压到FP16跑。另外预填充阶段确实峰值高,可以开--enable-chunked-prefill试试,能明显降低首token显存尖峰。
vLLM的显存分配不只是模型权重,还有KV cache和激活值,你max_model_len=4096但默认gpu_memory_utilization会吃掉大部分显存,建议手动设成0.6-0.7试试。另外GPTQ的7B实际加载后大约4G权重,但预填充阶段中间激活很容易爆到10G+,A10的24G被吃满不奇怪。可以用vllm的--profile或者看下日志里的显存详细分配,或者直接跑个空请求对比一下预填充和decode阶段的占用,先确认峰值到底出在哪一步。
显存大头在KV cache和中间激活,试试用vllm的--memory-utilization调低点,再看看prefill阶段是不是爆了。
我之前也踩过类似的坑,vLLM的KV cache实际占用比你预期的大得多,尤其4096长度下每个序列都要预留空间,这玩意儿不吃显存才怪。建议先看看/v1/metrics里的gpu_cache_usage_perc,如果能到90%以上那基本就是缓存占满了。另外确认下是不是加载了fp16的权重文件,有些gptq版本会保留float16的残差层,这也能吃掉好几个G。我之前把--kv-cache-dtype改成fp8之后,显存直接降了2G多,你可以试试。至于吞吐,200 tokens/s对7B int4来说确实偏低了,检查下是不是--max-num-seqs设太小,默认值可能限制并发。
显存大头其实在KV cache和激活值,7B int4权重才4G左右,剩下的全是推理中间态,用vllm的--gpu-memory-utilization调一下看看。
看到这个数字我第一反应是有点不对劲,14G对于int4的7B来说确实太夸张了。我之前部署过类似的模型,A10上大概也就8-9G左右,你检查过vLLM的gpu_memory_utilization参数没,默认好像是0.9,但如果你没显式设置,它可能会给KV cache预留特别大的空间,尤其你max_model_len设了4096,如果实际请求长度波动大,预填充阶段的临时张量也会把显存顶上去。另外你确认一下GPTQ的版本是不是跟transformers和vLLM匹配,我之前遇到过量化层没被正确加载导致显存翻倍的情况,模型还是按fp16跑的。想定位的话可以用torch.cuda.memory_snapshot()或者直接看vLLM的日志里有没有打印显存分配明细,那个挺直观的。不过我更好奇的是你吞吐200 tokens/s是指单并发还是多并发下的数字,如果是单条请求这个性能还行,要是并发低说明batching没生效,可能跟你的请求到达率有关系,不一定纯是显存问题。
你这情况我上周刚踩过,gptq的int4实际显存占用得看group size和act order,默认配置下KV cache膨胀得吓人,14G一点都不冤。建议先跑个vllm的profiler看看显存分布,大概率是prefill阶段给sequence的显存池开太大了。另外你max_model_len设4096但如果并发请求多,continuous batching的调度窗口还是会把显存吃满,试试把gpu_memory_utilization调低到0.7再配合--kv-cache-dtype fp8,吞吐应该能稳住。
vLLM的显存大头其实是KV cache和预填充的临时激活值,int4只压缩了权重,这两块该占多少还是占多少。你可以先用vllm的--gpu-memory-utilization限制一下显存占比,再配合--kv-cache-dtype fp8试试,能省不少。另外确认下是不是真的加载了gptq版本,有时候模型路径没指对会默默回退到bf16,14G这个数字很像没量化时的占用。
这情况我遇到过,vLLM的显存大头经常不在权重上,KV cache和预填充的activation才是吃显存主力,14G不算离谱。你可以先看下/v1/metrics里gpu_cache_usage_perc这个指标,如果接近100%说明是KV cache占满了,调小max_model_len或者换A100试试。另外确认下gptq的版本是不是和vLLM兼容,我之前用过一个老版本量化模型,inference时被反量化回fp16,显存直接翻倍。可以先跑个纯推理测试排除下预填充的影响。
我跑过类似的活儿,7B int4实际吃12G+挺正常的,vLLM的KV cache和CUDA context本身就会吃掉不少,5-6G那个数字太理想了。你可以先用vllm --print-model-config看看量化配置,再试试把--kv-cache-dtype改成fp8,或者调低--gpu-memory-utilization给推理留点余量。定位的话,用nvidia-smi dmon -d 1盯着显存变化,基本能看出是预填充还是生成阶段在涨。
说实话你这个预期有点乐观了,int4的7B模型权重确实能压到5-6G,但vLLM跑起来之后还有KV cache、激活值、中间变量和CUDA context这些额外开销,尤其你max_model_len设到4096,KV cache每序列就得占不少,A10的24G看着够用,但实际分配碎片化很严重,14G这个数字我甚至觉得算正常了。你提到吞吐只有200 tokens/s,这明显不正常,vLLM的continuous batching应该能跑满A10的算力才对,建议先查下是不是显存碎片导致实际可用batch size被压得太小,或者量化后权重加载时没走对路径,gptq版本和vLLM的兼容性也是个坑。我之前遇到过类似情况,最后发现是vLLM的gpu_memory_utilization默认值只有0.9,但A10上被其他进程占了点显存,导致实际可用空间不够,自动降低了并发度,你试试把这个参数调到0.85以下,或者用--swap-space给CPU offload留点余地。另外推荐用nvidia-smi dmon看实时显存分配速率,或者直接开vLLM的--verbose日志,它会打印每个阶段的显存占用明细,比瞎猜强。如果还不行,可以试试把模型转成AWQ量化,有些情况下对vLLM的显存布局更友好,不过这个得看你的微调框架支不支持。
这情况我太熟了,之前部署13B的时候也踩过类似的坑。你看到nvidia-smi占用14G,其实不全是模型权重,vLLM的KV cache和CUDA context会吃掉一大块显存,尤其是A10这种24G的卡,默认会预留很大比例给KV cache,你max_model_len设了4096但实际分配可能远超你预期,试试用--kv-cache-dtype fp8或者手动调--gpu-memory-utilization到0.7以下,能明显降下来。另外你这量化版本确认下是不是真正的int4,有些GPTQ的group size是128,但如果你用的AWQ或者exl2,或者加载时没指定--quantization gptq,实际可能跑的是fp16,那显存翻倍就很正常了。还有个容易忽略的点,prefill阶段确实会临时爆显存,尤其长prompt,你可以看看是不是有长上下文请求,或者用--max-num-batched-tokens限制一下批处理token数。至于定位工具,建议用vLLM自带的--print-memory-snapshot,能打出详细的显存分配表,另外nvidia-smi看的是峰值不是均值,最好配合ncu或者nsys看具体kernel占用。吞吐200不到的话,也可能是量化后kernel没优化好,试下--quantization gptq --kv-cache-dtype fp8,或者干脆换sglang跑跑看,有时候同配置下差距还挺大的。
遇到过类似情况,你这14G大概率不是模型权重本身,而是KV cache加上预填充的临时张量占了大头,A10 24G看着够用但4096上下文+连续批处理峰值很容易飙上去。建议先关掉continuous batching试试单请求跑一下,看显存能降多少,另外确认下GPTQ是不是真的加载成功,有时候vLLM会fallback回fp16。定位的话可以用nvidia-smi dmon看实时显存,或者开vLLM的--print-trace-timing,能看出预填充和decode各占多少。吞吐200确实偏低,检查下是不是CPU瓶颈或者量化group size设太小了。
说实话你这个问题我上个月刚踩过,一度怀疑是不是卡坏了。后来排查下来,vLLM的显存占用大头根本不在模型权重,而是KV cache加上CUDA context和运行时buffer,尤其A10只有24G显存,你max_model_len设4096但batch开大的话,KV cache能吃掉6-8G,再算上激活值,14G真不夸张。建议你先用vllm的--gpu-memory-utilization参数把显存利用率压到0.85左右,然后看看是不是gptq版本和vLLM的兼容性问题,有些老版本对int4支持不好会额外分配fp16的权重副本。另外200 tokens/s对于7B int4在A10上其实偏低了,检查一下是不是预填充阶段没有走chunked prefill,或者你输入序列特别长,导致prefill和decode混在一起拖慢速度。想定位显存的话,可以开vLLM的--memory-profiler,或者直接用nvidia-smi dmon看动态变化,也可以抓一下torch.cuda.memory_summary()(如果走pytorch后端),但最直接的办法是分别测一下只加载模型不跑推理和跑一个短请求时的显存差值,基本就能算出KV cache占了多大。我感觉你量化本身没问题,就是运行时配置没调好,别急着换模型版本。
14G确实不太对劲,我怀疑你看到的nvidia-smi显存占用包含了vLLM的KV cache预分配,而不是模型权重本身。vLLM默认会按max_model_len和gpu_memory_utilization参数把剩余显存几乎全占满,你设了4096但没调gpu_memory_utilization的话,它可能默认拿了90%以上,所以实际占用看起来像14G。你可以先跑一下nvidia-smi看进程的used memory是不是稳定,再试试把gpu_memory_utilization调到0.6左右,或者设个--kv-cache-dtype fp8看看能不能压下来。另外GPTQ的7B其实int4权重大概4G,但激活值和临时buffer在预填充阶段确实会飙高,尤其是长上下文时,你试试用--enable-prefix-caching或者把max_num_seqs调小一点,比如8,看吞吐有没有变化。200 tokens/s对A10来说其实不算太离谱,毕竟A10的算力也就那样,但如果持续稳定在200而没波动,可能是continuous batching没生效,检查下vLLM版本和启动参数里的--max-num-batched-tokens。想定位显存去向可以开vLLM的--log-stats,它会打印KV cache、权重、激活分别占多少,或者用torch.cuda.memory_summary()写个脚本挂上去看。我之前部署13B时也遇到过类似情况,最后发现是prompt里塞了太多历史对话,预填充把显存吃满了,试试把max_model_len再砍到2048对比下。如果还是没头绪,可以看看vLLM的issue区,有人提过A10上GPTQ的kernel内存分配有bug,换个AWQ版本可能就正常了。
你这情况大概率是KV cache在作怪,7B模型int4权重确实只有5G左右,但4096上下文长度下KV cache轻松吃掉几个G,再加上vLLM默认会预分配显存,14G真不算离谱。建议先用vllm的--gpu-memory-utilization参数限制到0.85,再配合--max-num-seqs调低并发数看看,能明显降占用。另外确认下GPTQ版本是不是针对Qwen2.5重新跑过校准的,老版本量化对新一代模型兼容性差,推理时会额外膨胀显存。想定位的话可以用nvidia-smi dmon看实时显存分配,或者直接看vLLM的日志,它会打印KV cache和权重各占多少,比我瞎猜准。
试试用vllm的--gpu-memory-utilization限制显存比例,再开--enable-prefix-caching,预填充峰值能压不少。
显存大头在KV cache和激活值,试试用vllm的--gpu-memory-utilization和--kv-cache-dtype调一下,顺便看下vllm view的火焰图。
之前跑7B也踩过这坑,A10的14G大概率是预填充阶段KV cache和激活值撑的,vLLM默认会预留不少显存给后续调度。你可以试试把gpu_memory_utilization调低到0.6左右,再配合--enforce-eager关掉CUDA graph,看能不能压下来。另外GPTQ的group size和desc_act设置也会影响实际显存,确认下是不是用的128/True的版本。想定位的话,vLLM自带--print-details可以看每层占用,或者用nvidia-smi dmon盯一下显存涨跌曲线,比猜快多了。