最近在把我们微调过的Qwen2.5-7B部署到内网服务器上,用vLLM跑的,开了gptq量化。理论上7B模型int4量化后显存占用应该在5-6G左右,但实际部署后nvidia-smi一看,单卡A10直接吃了14G,吞吐也只有200 tokens/s不到。我已经设置了max_model_len=4096,也开了continuous batching,但感觉没起到什么作用。怀疑是不是上下文预填充阶段太吃显存,还是量化版本选错了?求有经验的大佬指点一下,或者有没有什么工具能定位显存到底被什么占住了?提前感谢。
部署7B大模型到生产环境,显存占用比预想高很多怎么办?
全部回复
共 90 条说实话你这个问题我前几天刚踩过一模一样的坑,最后发现是vLLM默认把KV cache给预留满了,跟量化本身关系不大。7B int4的权重确实只有5G左右,但如果你没手动设gpu_memory_utilization,vLLM会按显存上限去分配KV cache,A10的24G它恨不得全占上,14G这个数字看着就是权重加KV cache再加激活值的总量。你试试在启动参数里加个--gpu-memory-utilization 0.6,或者直接--max-num-seqs调小一点,应该能明显压下来。另外预填充阶段确实吃显存,尤其你max_model_len开到4096,如果并发请求里长文本多,临时激活值能飙得比权重还夸张,建议用--enable-prefix-caching把重复前缀的缓存打开。至于定位工具,别用nvidia-smi了,装个nvtop看实时利用率比它清楚,或者直接看vLLM的日志,里面会打印每个请求的KV cache分配情况。还有个小细节,gptq量化版本选4bit 128g group size的,别用32g的,后者显存占用高不少,吞吐也拉胯。你那个200 tokens/s如果是单请求那其实不算慢,要是并发下才200就得检查一下是不是CPU瓶颈,比如tokenizer和采样那部分没跑在GPU上。
14G这个数我太熟了,之前部署13B的时候也撞见过类似情况,后来发现是vLLM默认把KV cache预分配得太激进,尤其是你max_model_len设了4096但实际并发请求里长上下文占比不高的时候,显存全被KV cache占着。建议先看一眼vllm的日志里gpu_memory_utilization默认是0.9,这个值其实很坑,你拿它乘以总显存再减去模型权重,剩下的全塞给KV cache了,实际跑起来根本用不满。可以试着降到0.7或者0.8,同时开一下--enforce-eager,把CUDA graph的额外开销省掉,A10的显存带宽本来就一般,预填充阶段吃显存是必然的,但200 tokens/s确实偏低,你查下是不是gptq的group_size设得太小导致反量化开销过大。另外有个土办法,用nvidia-smi dmon实时盯一下显存分配曲线,或者直接在vLLM里加--print-model-statistics,能直接看到权重和KV cache分别占了多大,比瞎猜强多了。
vLLM的显存占用大头其实不在权重,而在KV cache和预填充阶段的中间激活值,你max_model_len设了4096,但A10是24G显存,如果没手动限制gpu_memory_utilization,vLLM默认会按比例预分配显存给KV cache,7B模型int4权重可能只有5G左右,但KV cache和激活值加起来轻松吃掉10G+。建议你先把gpu_memory_utilization调到0.7左右试试,同时看看vllm的日志里KV cache的分配情况。另外你提到吞吐200 tokens/s,这个数字对A10来说确实偏低,有可能你实际跑的是预填充占主导的任务,长prompt下prefill阶段本身就吃显存还慢,可以试试用--enable-prefix-caching或者把输入长度压缩一下。至于定位工具,vLLM自带--profile可以输出显存分配的分析图,或者你用nvidia-smi dmon看实时显存曲线,基本能看出是cache池满了还是激活值峰值。还有个坑,你确认一下gptq的版本是不是和vLLM的量化kernel匹配,不匹配的话有时候会回退到fp16推理,那显存直接翻倍。我之前遇到过类似问题,最后发现是vLLM版本太旧,升级到最新版后显存占用直接降了3G。如果还不行,建议换个思路,直接看下官方文档里关于paged attention的显存计算方式。
14G这个数确实不太对劲,我怀疑你大概率是被vLLM的显存预留机制坑了。它默认会按gpu_memory_utilization把能用的显存全吃满,哪怕你模型本身只占5G,剩下全给KV cache和activation预留了,所以nvidia-smi看到14G不代表实际全在跑推理。你可以先看看启动日志里gpu_memory_utilization是不是默认的0.9,手动调到0.6左右试试,吞吐可能反而会上来。另外你提到预填充阶段,这个确实很吃显存,尤其max_model_len=4096的时候,prefill的临时tensor会膨胀得很快,建议用vllm的--enable-prefix-caching配合--swap-space,或者干脆把max_model_len降到2048看看。至于量化版本,你确认过gptq的group_size和desc_act设置吗?Qwen2.5系列有的reproduce出来的量化文件跟官方不兼容,跑起来反而会显存翻倍。定位工具的话,nvidia-smi看进程内存不细,可以用torch.cuda.memory_snapshot()或者直接开vllm的--memory-profiler,能看出每个tensor的分配。最后200 tokens/s对7B int4来说其实不慢,如果延迟要求不高,不如先求稳定,别太纠结峰值显存。
vLLM默认会预分配显存池,试试--gpu-memory-utilization调低点,14G里有不少是预留的。
我上次也踩过类似的坑,建议先别急着怀疑量化版本,用vLLM的--gpu-memory-utilization参数把显存利用率压到0.85以下看看,有时默认预留太多。另外你提到预填充阶段,可以试试把--max-num-seqs调小,比如改成64,这玩意儿对显存峰值影响挺大的。工具的话,nvidia-smi dmon能看到实时显存分配,配合vLLM的--log-requests日志能大致判断是KV cache还是权重占了大头。我之前是发现context length设太长导致KV cache爆炸,砍到2048直接省了3G。
14G这个数字确实不对劲,我之前也踩过类似的坑,大概率不是量化本身的问题,而是KV cache和预留在作祟。vLLM默认会预分配显存,加上A10的显存带宽也就那样,200 tokens/s基本是正常水平。你可以先用vllm的--gpu-memory-utilization参数把利用率压到0.7左右,再配合--max-num-seqs调小并发数看看,显存占用会直观降下来。另外确认下你下载的gptq模型是不是真的4bit,有些仓库标的int4实际是8bit权重。想排查的话,用nvidia-smi看进程的used memory,再对比下vLLM日志里显存分配明细,基本能定位是模型权重还是KV cache占大头。
A10是16G显存,你看到14G占用其实挺正常的,vLLM默认会预分配KV cache,加上pytorch的缓存机制,nvidia-smi显示的数值会虚高不少,不代表实际全用上了。你试试启动时加个--gpu-memory-utilization参数,比如设成0.85,再配合--max-num-seqs限制并发,应该能压下来一些。另外你说的gptq量化,如果是AutoGPTQ那种老版本,对Qwen2.5的支持可能不太好,建议换用GPTQModel或者直接用vLLM内置的AWQ,内存占用和速度都会有改善。200 tokens/s对于7B int4在A10上其实算正常水平了,别被网上那些benchmark忽悠,他们是拿H100跑出来的数据。预填充阶段确实吃显存,尤其4096长度,你可以观察下--prefix-caching有没有开,开了能复用公共前缀的KV,减少重复计算。还有个笨办法,直接看vLLM的日志,里面会打印KV cache pool的大小和剩余量,基本就能定位是不是缓存分配的问题。我上次部署也踩过这坑,最后发现是tokenizer的padding策略没对齐,导致实际序列长度比预期长很多。
说实话14G这个数字确实不太像纯int4推理该有的水平,A10的带宽跑7B到200 tokens/s已经算正常偏上了,重点还是显存。建议先确认下vLLM有没有真的加载gptq版本,有时候模型路径没指对会偷偷回退到fp16,直接乘4倍。另外你可以开--gpu-memory-utilization限制显存池,再配合vLLM的--profiling看看prefill阶段峰值,我上次就是被那几K的max_model_len坑了,4096对7B来说预填充矩阵膨胀得很夸张,降到2048试试。
你这情况我熟,vLLM里gptq的显存占用经常比理论值高,因为要额外存量化参数和中间激活值,14G其实算正常范围。建议先跑一下vllm的--gpu-memory-utilization调低到0.8试试,或者把--max-num-seqs限制到8,能明显压峰值。预填充阶段确实吃显存,可以开--enable-prefix-caching减少重复计算。排查工具的话,nvidia-smi dmon看实时分配,或者直接翻vLLM日志里的显存统计,比瞎猜靠谱。
这情况我也踩过坑,光看量化后的权重文件大小去估显存不太靠谱,实际跑起来KV cache和激活值才是大头,尤其prefill阶段峰值能比稳态高一倍。建议先用vLLM自带的--print-model-statistics看下显存分配,或者跑个小batch size测一下不同并发下的占用曲线,另外检查下是不是因为max_model_len设得不够小导致KV cache预分配太多了。
还有你确认下GPTQ的group size和desc_act设置,有些量化版本在长上下文下反而会放大中间张量,试试换成AWQ或者把vLLM的gpu_memory_utilization调低点留出余量。吞吐200其实对7B来说不算太离谱,如果业务场景允许可以加个--enable-prefix-caching,多轮对话能省不少计算。
说实话你这个问题我上个月刚踩过一模一样的坑,7B int4理论值跟实际部署差距大太正常了。vLLM里gptq的显存占用不只是权重,还有KV cache、激活值和临时缓冲区,max_model_len=4096看着不长,但A10的16G带宽本来就不算富余,预填充阶段attention矩阵是平方级膨胀的,并发一多直接吃满。建议你先用vllm的--gpu-memory-utilization参数把显存利用率压到0.85以下,再跑个单请求测试看看峰值,排除一下是不是continuous batching把多个sequence的KV cache全堆进去了。另外你确认一下加载的是不是vLLM专用的GPTQ模型格式,我之前用AutoGPTQ转的权重直接塞进去,结果它内部没走量化kernel,等于int4白开。想定位的话可以开vLLM的--verbose日志,里面有每个阶段的显存分配明细,或者用nvidia-smi dmon实时盯几个关键指标。吞吐200不到确实有点低,检查下是否没开--enable-prefix-caching,长prompt重复请求时能省不少预填充算力。最后问一句,你部署环境里有没有别的进程占显存?Docker不限制的话有时候别的服务会偷吃显存的。
vLLM的显存占用大头其实在KV cache和CUDA context,int4权重只是其中一部分,A10 14G挺正常的。你可以用vllm的--gpu-memory-utilization参数限一下,再把--max-num-seqs调小试试,吞吐应该还能再压一压。另外检查下是不是gptq的group_size没设对,128和32的显存差距挺明显的。想定位的话可以开vLLM的--vllm-logging-level或者用nsys抓一下kernel,基本能看出是预填充还是decode在吃资源。
vLLM的KV cache默认预留挺激进的,你这max_model_len=4096但batch开大了照样能吃满显存,试试把gpu_memory_utilization调低到0.7左右,或者干脆用--kv-cache-dtype fp8看看。另外GPTQ的7B模型实际加载时权重就占4-5G,但激活和中间buffer在预填充阶段峰值能飙到10G+,A10的14G挺正常。建议先跑个单请求的profiling,看下峰值显存是发生在prefill还是decode,再决定要不要换AWQ或者加--enforce-eager。
vLLM的显存占用大头其实在KV cache和预填充的临时激活值,int4只压缩了权重,这两块可没缩水。你试试把gpu_memory_utilization调低到0.7左右,给运行时留点余量,另外确认下是不是用的GPTQ官方那个带marlin内核的版本,非marlin内核的int4速度会差不少。想定位的话可以开vLLM的--enable-memory-profiling参数,它会打印详细的显存分解,或者直接看/metrics里的gpu_cache_usage指标,能区分是权重还是KV cache占的。
vLLM的显存占用大头其实在KV cache和激活值,int4只压缩了权重,7B模型权重也就4G多,剩下全被运行时吃掉了。建议先看下vllm的日志,里面有详细的显存分配明细,特别是KV cache预留了多少,另外把gpu_memory_utilization调低到0.7左右试试,A10才24G,别让它默认全占。200 tokens/s对7B来说确实偏慢,检查下是不是max_num_seqs设太小了,这个参数对吞吐影响很大。
量化版本的话,gptq一般没问题,但确认下你是不是用的vLLM官方支持的gptq格式,有些微调后的权重需要重新跑一遍量化脚本。还有个笨办法,把max_model_len降到2048看看显存降多少,如果降得明显那就是预填充阶段的临时显存问题,可以开下chunked prefill选项缓解。
我之前也踩过类似的坑,vLLM对gptq的显存占用本来就有额外开销,加上预填充阶段KV cache会按max_model_len预留,14G真不算离谱。你可以先用vllm run带--gpu-memory-utilization参数压一下,顺便开--enable-prefix-caching试试,能省不少。另外建议用nvidia-smi dmon或者vllm的--log-stats看下具体是权重还是KV cache占了大头,A10跑7B确实紧巴,不行就换awq量化或者直接上双卡。
14G这个数字确实有点离谱,我怀疑你GPTQ的版本或者加载方式有问题。7B int4理论权重就4G上下,加上KV cache和激活值,4096长度撑死也就6-7G,你跑到14G大概率是vLLM把整个模型按未量化精度加载了,或者你下载的量化权重实际是动态量化而非预量化,可以先用transformers直接加载看看峰值占用,排除vLLM的缓存策略干扰。
另外200 tokens/s对A10来说其实不算慢,但如果你觉得是预填充阶段卡顿,可以试试把--gpu-memory-utilization调到0.9强制预留,或者监控一下是不是显存碎片化严重。我遇到过类似情况,最后发现是continuous batching在某些batch大小下会预分配整块连续显存,反而浪费空间。
你可以用nvidia-smi dmon看实时显存分配,或者跑个profiling工具看算子级占用,比如torch.profiler配合vLLM的--profile,能直接看到是attention还是MLP层吃掉的。如果确认是量化没生效,重新用AutoGPTQ跑一遍量化,注意校准集别用太短的文本,不然激活值分布不准。
还有个小坑,你max_model_len设4096但实际请求若是长文档,vLLM会按最大可能长度预分配缓存,试试把--block-size调小,或者直接用--enable-chunked-prefill,能显著降低预填充阶段的峰值。先查这些吧,大概率不是模型本身的问题。
之前我也踩过类似的坑,vLLM的显存大头其实在KV cache和预填充的临时激活值上,7B int4理论值只是权重部分,实际跑起来翻倍很正常。你试试把gpu_memory_utilization调低到0.7左右,再配合--enable-prefix-caching,能省不少。另外确认下你用的GPTQ是不是针对Qwen2.5重新校准过的版本,有些通用量化包对特定模型效果很差,直接换AWQ说不定立竿见影。
vLLM的显存占用大头其实在KV cache和CUDA context,7B int4理论值只是权重部分,A10的14G挺正常的,你max_model_len设4096但batch开大了照样吃满。建议先跑一下vllm的--gpu-memory-utilization参数,手动限制到0.9看看reserved和used的差值,多半是预分配机制在作怪。另外GPTQ的group size如果是128,实际显存会比理论高不少,可以换AWQ试试,或者干脆用fp8,A10对fp8支持还行。吞吐200不到有点低了,确认下是不是没开--enable-prefix-caching,长prompt场景这个影响巨大。