How to reduce VRAM usage for LLM inference without hurting quality
Need to reduce GPU memory usage for LLM inference without a specific error, or: torch.cuda.OutOfMemoryError under production load that a smaller test load did not trigger
Also appears as
- How to fit a larger model on my GPU
- vLLM/transformers using more VRAM than expected in production
Short answer
VRAM usage during inference comes from three independent budgets: model weights (params x bytes/param), KV cache (scales with batch x context length), and activation/workspace memory. Reducing usage means attacking whichever budget dominates: quantize weights to cut the largest fixed cost, cap max context length and concurrency to cut the largest variable cost, and use an efficient attention/serving backend to minimize workspace overhead.
Affects: Any on-prem LLM serving deployment where VRAM is the binding constraint on model size, context length, or concurrency
Cut VRAM usage in order of impact
- 1Quantize the weights first, since they are usually the largest fixed cost: AWQ or GPTQ 4-bit cuts weight memory roughly 4x versus bf16 with a small accuracy cost.
- 2Cap --max-model-len (vLLM) or max_length (transformers) to your application's real context need rather than the model's architectural maximum, since KV cache scales directly with it.
- 3Reduce max concurrent sequences or batch size if KV cache for many simultaneous requests is the dominant cost, since KV cache also scales linearly with concurrent batch size.
- 4Use FlashAttention or an equivalent memory-efficient attention kernel, which avoids materializing the full attention matrix and meaningfully cuts activation memory for long sequences.
- 5If still short, add tensor parallelism across a second GPU so both weights and KV cache are sharded rather than trying to fit everything on one device.
How to confirm this is your problem
- Model serves fine at low concurrency or short context but OOMs as production traffic or context length increases
- VRAM usage is close to the GPU's total capacity even during light load, leaving no headroom for traffic spikes
- Reducing batch size or max_model_len resolves the OOM but at a concurrency or context ceiling that is too low for the product requirement
- Same model at a different quantization level (or on a bigger GPU) does not exhibit the problem
Root causes and fixes
Weight memory, KV cache memory, and activation memory are all drawing from the same fixed VRAM pool without an explicit budget for each
Total inference VRAM use is the sum of three largely independent costs: weights (fixed, set at model choice and precision), KV cache (variable, grows with concurrent requests and context length), and activation/workspace memory (variable, grows with batch size and sequence length within a single forward pass). Treating VRAM as one undifferentiated number instead of these three budgets makes it hard to know which lever actually reduces usage.
Fix: Break down current usage into weights, KV cache, and activations using framework instrumentation or the underlying math, then attack whichever is largest rather than guessing at a single global setting.
python -c "params=7e9; bytes_per_param=2; print('weights GB', params*bytes_per_param/1e9)"Weights are kept in full bf16/fp16 precision when a quantized variant would serve the same requests
Weight memory is fixed and often the largest single line item; every bit of precision above what your accuracy requirements actually need is memory spent for no benefit. 4-bit quantization (AWQ, GPTQ) typically cuts weight memory to roughly a quarter of bf16 with modest accuracy impact, freeing that memory for KV cache or higher concurrency instead.
Fix: Benchmark a 4-bit quantized version of your model against your actual evaluation set; if quality holds, the freed memory can be redirected to context length or concurrency, which usually matters more to users than the last percent of raw accuracy.
vllm serve MODEL --quantization awq
max_model_len is set to the model's architectural maximum context rather than what the application uses
KV cache memory scales linearly with the configured maximum context length, since the serving engine must reserve enough cache capacity for the worst-case sequence. A model with a 128k native context configured at that maximum reserves dramatically more KV cache per sequence than one capped at, say, 8k, even if actual usage rarely approaches the maximum.
Fix: Set max_model_len to a value that comfortably covers your real traffic's 95th or 99th percentile context length, not the model's theoretical ceiling, and monitor actual usage to validate the choice.
vllm serve MODEL --max-model-len 8192
Batch size or maximum concurrent sequences is unbounded, letting KV cache grow unpredictably under load
Without an explicit cap on concurrent sequences, a serving engine will keep admitting new requests until KV cache memory is exhausted, meaning VRAM usage under peak traffic is effectively unbounded by the request pattern rather than by a deliberate operational limit.
Fix: Set an explicit maximum concurrent sequence or batch size limit appropriate to your GPU's KV cache budget, and use request queuing or rejection for traffic beyond that limit rather than letting the process OOM.
vllm serve MODEL --max-num-seqs 64
The attention implementation is a naive or non-optimized kernel that materializes large intermediate tensors
Standard (non-fused) attention implementations compute and store the full attention score matrix, which scales quadratically with sequence length and can dominate activation memory for long-context requests; fused kernels like FlashAttention avoid materializing this matrix entirely.
Fix: Confirm FlashAttention or an equivalent fused kernel is active (most modern serving frameworks enable it by default when the GPU and library versions support it) rather than falling back to a naive attention path.
python -c "import torch; print(torch.backends.cuda.flash_sdp_enabled())"
Diagnostic commands
Break down current VRAM usage by category
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
Comparing total used against your independently calculated weight memory reveals how much is KV cache plus activations, telling you which budget to target next.
Check actual context length distribution in production traffic
grep -o 'prompt_tokens\":[0-9]*' access.log | sort -n | uniq -c | tail -20
If your 99th percentile actual context length is far below max_model_len, you have unused KV cache reservation that can be reclaimed by lowering the configured maximum.
Confirm FlashAttention or equivalent is active
python -c "import torch; print(torch.backends.cuda.flash_sdp_enabled(), torch.backends.cuda.mem_efficient_sdp_enabled())"
Both should report True on supported GPUs (Ampere and later); False indicates a fallback path that costs more activation memory than necessary for long sequences.
Stopping it from happening again
- Track weight, KV cache, and activation memory as separate metrics in your monitoring rather than one aggregate VRAM number.
- Set max_model_len and max concurrency from measured production traffic percentiles, revisited quarterly, not from defaults.
- Evaluate quantized model variants as a standing part of your deployment checklist, not only when hardware runs out.
- Load test at realistic concurrency and context length before production cutover to reveal the true VRAM ceiling.
When this becomes an architecture problem
If you have already quantized the weights, capped context length and concurrency to real traffic needs, and confirmed efficient attention kernels are active, and VRAM is still the binding constraint on serving the traffic your business requires, that is a signal to add GPUs (tensor parallelism) or move to a larger-memory GPU tier rather than continuing to tune software settings.
Frequently asked questions
What uses the most VRAM: weights, KV cache, or activations?
For most production serving configurations, weights are the largest fixed cost (fully determined by model size and precision), while KV cache is the largest variable cost that grows with concurrency and context length. Activation memory during inference is typically the smallest of the three unless sequences are extremely long, since inference does not need to retain activations for a backward pass the way training does.
Does quantizing the KV cache help as much as quantizing the weights?
KV cache quantization (for example FP8 KV cache, supported on some hardware in vLLM) roughly halves KV cache memory, which matters most in high-concurrency, long-context deployments where KV cache dominates VRAM usage. It is a smaller absolute saving than weight quantization in typical deployments but becomes increasingly valuable as concurrency and context length grow.
Is there a formula I can use to estimate my VRAM budget?
Yes: total VRAM needed is approximately (params x bytes_per_param) for weights, plus (2 x num_layers x num_kv_heads x head_dim x total_tokens_across_all_concurrent_sequences x bytes_per_element) for KV cache, plus a smaller activation term that depends on your attention kernel and batch size. Plugging in your specific model config and target concurrency gives a reasonably accurate planning number.
Should I reduce max_model_len even if some users occasionally need longer context?
Consider routing long-context requests to a separate, appropriately sized serving pool rather than reserving worst-case KV cache for every request on your main pool. This keeps the common case efficient while still supporting the occasional long-context need through a deliberately provisioned path.
Size it properly next time
Free calculators that prevent this class of failure before you provision hardware.
KV Cache Memory Calculator
Calculate KV cache memory per sequence and per batch from model architecture and context length, then see how many concurrent sequences your GPU can hold.
Free ToolLLM Quantization Memory Savings Calculator
Compare FP16, FP8, and INT4 memory footprints for any model size and see how many fewer GPUs quantization requires to serve it.
Free ToolConcurrent Users Per GPU Calculator
Estimate how many connected users one GPU can support, accounting for both VRAM limits and throughput limits, plus the fact that most users are not actively streaming at any given moment.
Free ToolvLLM Throughput Estimator
Estimate aggregate tokens-per-second throughput for a vLLM deployment from model size, GPU class, and batch depth, accounting for continuous batching gains.
Related problems
vLLM fails to start because there is not enough memory for the KV cache
vLLM reserves a fixed pool of GPU memory (gpu_memory_utilization, default 0.9) for weights plus KV cache, and if the weights already consume most of that budget there is nothing left for even one sequence's KV cache blocks. The fix is to raise gpu_memory_utilization toward the physical limit, lower max_model_len so each sequence's KV cache is smaller, or serve a quantized checkpoint so more of the budget is available for cache.
CUDA out of memory when loading an LLM
This happens because model weights alone require roughly 2 bytes per parameter in fp16/bf16 (a 70B model needs about 140 GB before you even run inference), and that number does not fit your GPU. The fix is to either quantize the weights (AWQ, GPTQ, FP8, or GGUF), split the model across multiple GPUs with tensor parallelism, or pick a GPU with enough VRAM for the parameter count you are loading.
LLM inference is extremely slow after enabling CPU offload
CPU offload trades memory capacity for speed because every offloaded layer's weights must cross the PCIe bus (typically 16-64 GB/s) on every forward pass, versus terabytes-per-second on-GPU HBM bandwidth; this is not a bug, it is the fundamental cost of running more model than your GPU can hold. The real fix is usually to reduce how much needs to be offloaded (quantize first) rather than trying to make offloading itself faster.
GuideKV Cache Optimization: Prefix Caching and Chunked Prefill
KV cache optimization techniques for production LLM serving: prefix caching, chunked prefill, PagedAttention, and sizing memory for concurrent users.
GuideLLM Quantization: AWQ vs GPTQ vs FP8 vs GGUF
AWQ, GPTQ, FP8, and GGUF compared for production LLM serving: memory savings, throughput impact, quality loss, and which format fits which deployment.
GuideThe LLM Inference Cost Optimization Playbook
Cut LLM inference costs with a practical playbook: quantization, batching, GPU right-sizing, caching, and the on-prem vs API breakeven math for 2026.
Still stuck, or tired of fighting your own infrastructure?
Netray deploys and operates on-prem AI for regulated manufacturers and defense suppliers. We have debugged this stack in production, on air-gapped networks, at scale.