11 KiB
GPU Memory Parameters by Engine
How vLLM, sglang, and TensorRT-LLM interpret memory-related parameters, and how to estimate total GPU VRAM usage for each.
Quick Reference
| Parameter | vLLM | sglang | TensorRT-LLM |
|---|---|---|---|
| Memory fraction | --gpu-memory-utilization |
--mem-fraction-static |
free_gpu_memory_fraction (YAML/override) |
| Fraction base | Total VRAM | Total VRAM | Free VRAM (after model load) |
| Default fraction | 0.90 | 0.90 | 0.90 |
| Max sequence length | --max-model-len |
--context-length |
max_seq_len (YAML/override) |
| KV cache size override | --kv-cache-memory-bytes |
N/A | max_gpu_total_bytes (broken in 1.3.0rc5) |
1. vLLM
How --gpu-memory-utilization works
This is a fraction of total GPU VRAM. The engine budgets everything within this limit:
budget = total_vram * gpu_memory_utilization
KV cache = budget - model_weights - peak_activations - framework_overhead
At startup, vLLM profiles actual model weight and activation memory, then pre-allocates the remaining budget as KV cache blocks. The KV pool size is fixed for the lifetime of the engine.
How --max-model-len works
Sets the maximum total sequence length (input + output tokens). Longer sequences
require more KV cache per request. If the requested max-model-len needs more
KV cache than the budget allows, vLLM errors at startup:
ValueError: ... X GiB KV cache is needed, which is larger than the available
KV cache memory (Y GiB). ...
Reducing --max-model-len is the most effective way to reduce VRAM when the
model fits but the KV cache doesn't.
How --kv-cache-memory-bytes works
When set, this overrides the automatic KV cache sizing from
gpu-memory-utilization. The engine allocates exactly this many bytes for KV
cache regardless of the fraction. This means gpu-memory-utilization still
controls the overall VRAM budget (and thus whether the model fits), but the
KV cache portion is pinned to the explicit byte value.
Consequence for profiling: if a script uses --kv-cache-memory-bytes,
changing DYN_GPU_MEMORY_FRACTION_OVERRIDE (which maps to
--gpu-memory-utilization) won't change the KV cache size, only the leftover
headroom for activations and overhead.
Estimating total GPU usage
total_vram ≈ model_weights + kv_cache + activations + overhead
model_weights ≈ num_params * bytes_per_param
(e.g. 7B * 2 bytes for BF16 ≈ 14 GiB)
kv_cache_per_token = 2 * num_layers * num_kv_heads * head_dim * bytes_per_element
(the factor of 2 is for K and V tensors)
kv_cache_total = kv_cache_per_token * max_model_len * max_concurrent_seqs
overhead ≈ engine-dependent (auto-computed by estimate_worker_vram):
vllm: 1.2 + 1.0 * sqrt(params_b) GiB (0.6B≈2.0, 8B≈4.0)
sglang: 2.5 + 1.5 * sqrt(params_b) GiB (0.6B≈3.7, 8B≈6.7)
trtllm: 2.0 + 1.2 * sqrt(params_b) GiB (0.6B≈2.9, 8B≈5.4)
Rule of thumb: set gpu-memory-utilization so that
total_vram * fraction >= model_weights + 2 GiB. The rest becomes KV cache.
2. sglang
How --mem-fraction-static works
Like vLLM, this is a fraction of total GPU VRAM:
budget = total_vram * mem_fraction_static
KV cache pool = budget - model_weights
The budget covers model weights and the KV cache pool. Activations and CUDA graph buffers are allocated outside this budget from the remaining VRAM. This is slightly different from vLLM (which includes activations in the budget).
sglang recommends keeping 5-8 GiB free for activations and overhead. If you
see OOM errors, decrease --mem-fraction-static by 0.01-0.05 increments.
How --context-length works
Equivalent to vLLM's --max-model-len. Defaults to the model's native context
window. Reducing it shrinks the per-request KV cache requirement and allows more
concurrent sequences.
Estimating total GPU usage
total_vram ≈ model_weights + kv_cache_pool + activations_and_overhead
kv_cache_pool = total_vram * mem_fraction_static - model_weights
activations_and_overhead ≈ 1-8 GiB (depends on model size, batch size, seq len;
~1-2 GiB for small models like 0.6B,
~5-8 GiB for larger models like 8B+ with CUDA graphs)
3. TensorRT-LLM
How free_gpu_memory_fraction works
This is a fraction of free VRAM (not total). The engine:
- Loads model weights and builds the TRT engine (fixed cost).
- Queries remaining free GPU memory.
- Allocates
free_memory * free_gpu_memory_fractionfor the KV cache pool.
kv_cache = free_vram_after_model_load * free_gpu_memory_fraction
This means the same fraction yields different absolute KV cache sizes depending on how much VRAM the model consumed. A 5 GiB model on a 48 GiB GPU leaves ~43 GiB free; fraction=0.24 gives ~10 GiB KV cache. A 30 GiB model leaves ~18 GiB free; fraction=0.24 gives only ~4 GiB.
Set via YAML config, CLI, or env var:
--override-engine-args '{"kv_cache_config":{"free_gpu_memory_fraction": 0.24}}'
DYN_TRTLLM_OVERRIDE_ENGINE_ARGS='{"kv_cache_config":{"free_gpu_memory_fraction": 0.24}}'
How max_seq_len works
Maximum total sequence length. Defaults to the model's native context. Sequences exceeding this limit are rejected at runtime.
VRAM impact: none (PyTorch backend). Reducing max_seq_len from 40960 to
2048 had zero effect on total VRAM or KV cache size in testing (Qwen3-0.6B,
trtllm 1.3.0rc5). The PyTorch backend does not pre-allocate internal buffers
proportional to max_seq_len; KV cache size is determined solely by
free_gpu_memory_fraction. This differs from vLLM/sglang where reducing
context length measurably reduces memory.
Override via:
--override-engine-args '{"max_seq_len": 4096}'
Override gotcha: sub-dict replacement
Overriding any field inside kv_cache_config replaces the entire sub-dict.
If your YAML has enable_block_reuse: true and you override only
free_gpu_memory_fraction, you lose enable_block_reuse. Always re-include
all fields you need:
{"kv_cache_config": {"free_gpu_memory_fraction": 0.15, "enable_block_reuse": true}}
How max_num_tokens works
Maximum batched input tokens per iteration. Primarily a throughput knob.
VRAM impact: none. Reducing from 8192 → 256 had no measurable effect on total VRAM (41,643 vs 41,465 MiB — within noise; the slight increase is because smaller activation footprint lets the fraction claim marginally more KV cache).
max_gpu_total_bytes (broken)
Intended as an absolute byte cap for KV cache. As of trtllm 1.3.0rc5, this
field is ignored. Setting 5 GiB cap with free_gpu_memory_fraction=0.95
still allocated ~42 GiB of KV cache. Setting free_gpu_memory_fraction=0.0
with only max_gpu_total_bytes causes "Impossible to fit any sequence in kvCache". Do not rely on this field.
Override precedence
--override-engine-args JSON > --extra-engine-args YAML > CLI flags
The DYN_TRTLLM_OVERRIDE_ENGINE_ARGS env var is equivalent to
--override-engine-args and avoids shell quoting issues with scripts whose
arg parsers consume unknown flags before passing "$@".
Estimating total GPU usage
total_vram ≈ model_weights + engine_overhead + kv_cache
model_weights ≈ num_params * bytes_per_param / tensor_parallel_size
engine_overhead ≈ 2.0 + 1.2 * sqrt(params_b) GiB (CUDA context + TRT buffers + activations)
kv_cache = free_vram_after_model_load * free_gpu_memory_fraction
Engine overhead is auto-computed by estimate_worker_vram when called with the
trtllm engine name. Examples: 0.6B → 2.9 GiB, 8B → 5.4 GiB, 30B → 8.6 GiB.
Empirical validation (Qwen3-0.6B, RTX 6000 Ada 48 GiB, trtllm 1.3.0rc5)
Controlled test: single worker via agg.sh, one override at a time.
| # | Override | Total VRAM | KV Cache | Tokens |
|---|---|---|---|---|
| 1 | Baseline (YAML frac=0.85) | 41,465 MiB | 38.04 GiB | 356,160 |
| 2 | free_gpu_memory_fraction=0.15 |
9,383 MiB | 6.71 GiB | 62,848 |
| 3 | max_num_tokens=256 |
41,643 MiB | 38.26 GiB | 358,208 |
| 4 | max_seq_len=4096 |
41,469 MiB | 38.05 GiB | 356,192 |
| 5 | max_seq_len=2048 |
41,469 MiB | 38.05 GiB | 356,192 |
| 6 | seq=4096 + frac=0.15 | 9,383 MiB | 6.71 GiB | 62,848 |
| 7 | tokens=256 + seq=4096 + frac=0.15 | 9,377 MiB | 6.75 GiB | 63,200 |
Conclusion: free_gpu_memory_fraction is the sole effective knob for
trtllm VRAM control. Neither max_seq_len nor max_num_tokens reduce memory.
Combined overrides (test 7) produce no additional benefit over fraction alone
(test 2).
Why vLLM/sglang fractions are NOT interchangeable with TensorRT-LLM
Consider wanting 10 GiB of KV cache on a 48 GiB GPU with a 5 GiB model:
| Engine | Fraction meaning | Calculation | Result |
|---|---|---|---|
| vLLM | 10/48 = 0.21 of total | 48 * 0.21 = 10 GiB budget (minus model = 5 GiB KV) |
Wrong — need higher fraction |
| sglang | Same as vLLM | Same math | Same problem |
| TensorRT-LLM | 10/43 = 0.23 of free | 43 * 0.23 = 10 GiB KV cache |
Correct |
For vLLM/sglang, you actually need (model + kv) / total = (5 + 10) / 48 = 0.31
to get 10 GiB of KV cache with a 5 GiB model.
The helper functions in gpu_utils.sh handle these differences:
gpu_gb_to_total_fraction: for vLLM/sglang (fraction of total VRAM)gpu_gb_to_free_fraction: for TensorRT-LLM (fraction of free VRAM)gpu_worker_fraction <engine>: unified wrapper — reads_EW_*vars fromestimate_worker_vramand calls the right function for the engine.
Launch scripts use gpu_worker_fraction so they all follow the same pattern:
estimate_worker_vram "$MODEL" "$SEQ_LEN" "$CONCURRENCY" trtllm
GPU_MEM_FRACTION=$(gpu_worker_fraction trtllm)
KV Cache Memory Per Token
The formula for KV cache memory per token is the same across all engines:
kv_bytes_per_token = 2 * num_layers * num_kv_heads * head_dim * bytes_per_element
| Model | Layers | KV Heads | Head Dim | Dtype | Per Token |
|---|---|---|---|---|---|
| Qwen3-0.6B | 28 | 8 | 128 | BF16 | 112 KiB |
| Llama-3.1-8B | 32 | 8 | 128 | BF16 | 128 KiB |
| Llama-3.1-70B | 80 | 8 | 128 | BF16 | 320 KiB |
| Qwen2.5-VL-7B | 28 | 4 | 128 | BF16 | 56 KiB |
To estimate KV cache for a given context length:
kv_cache_gib = kv_bytes_per_token * max_model_len * max_concurrent_seqs / (1024^3)
DYN_GPU_MEMORY_FRACTION_OVERRIDE
Environment variable used by Dynamo's VRAM profiler to binary-search the minimum memory fraction a script needs.
- Maps to
--gpu-memory-utilizationin vLLM and--mem-fraction-staticin sglang. - For TensorRT-LLM, maps to
kv_cache_config.free_gpu_memory_fractionvia--override-engine-args. - Launch scripts use
gpu_worker_fraction <engine>to compute the default fraction; the override bypasses this and splits the raw value between workers. - Scripts that use
--kv-cache-memory-bytes(vLLM) bypass the fraction-based KV cache sizing, making the profiler's fraction override ineffective for KV cache. Those scripts should warn whenDYN_GPU_MEMORY_FRACTION_OVERRIDEis set.