一些参数
max_num_batched_tokens、max_num_seqs 以及 gpu_memory_utilization 的协作机制
这三个参数在 vLLM 中共同控制着请求调度和内存分配。让我详细解释它们之间的关系。
参数定义
1. max_num_batched_tokens
这是单次迭代中可以处理的最大 token 数量。它控制着每个批次的计算预算。 1
2. max_num_seqs
这是单次迭代中可以并发处理的最大请求(序列)数量。 2
3. gpu_memory_utilization
这是GPU 内存的使用比例(默认 0.9,即 90%),用于确定分配给 KV cache 的内存大小。 3
它们如何协作
调度器的约束机制
调度器在调度请求时会同时检查这两个约束: 4
关键验证逻辑: - max_num_batched_tokens
必须 >= max_num_seqs -
max_num_batched_tokens 不能超过
max_num_seqs * max_model_len 5
KV Cache 内存分配流程
gpu_memory_utilization 通过以下流程影响 KV cache:
计算请求内存: 6
内存分析:执行一次前向传播来测量激活内存使用 7
计算可用 KV cache 内存:
可用 KV cache 内存 = 请求内存 - 模型权重 - 峰值激活内存 - 非 torch 内存 - CUDA graph 内存计算 block 数量: 8
实际例子说明
例子 1:基本配置场景
假设配置: - max_num_batched_tokens = 2048 -
max_num_seqs = 128 -
gpu_memory_utilization = 0.9 -
max_model_len = 4096
调度行为: - 调度器最多可以同时处理 128 个请求 - 但单次迭代的总 token 数不能超过 2048 - 例如:可以调度 128 个请求,每个生成 16 个 token(128 × 16 = 2048) - 或者:调度 64 个请求,每个处理 32 个 token
KV Cache 分配: - 假设 GPU 有 80GB
内存,gpu_memory_utilization=0.9 意味着可用 72GB -
扣除模型权重(假设 40GB)和激活内存(假设 8GB)后,剩余 24GB 用于 KV
cache - 这 24GB 需要支持最多 128 个并发请求的 KV cache 存储
例子 2:Chunked Prefill 场景 9
配置: - max_num_batched_tokens = 8192 -
max_num_seqs = 256 -
gpu_memory_utilization = 0.9
场景:一个长提示词请求(1000 tokens)+ 多个解码请求
调度器会: 1. 优先调度所有解码请求(假设 200 个请求,各 1 token = 200 tokens) 2. 剩余预算:8192 - 200 = 7992 tokens 3. 可以调度长提示词的部分(如 7992 tokens),或完整调度多个较短的新请求
例子 3:内存不足导致的抢占 10
当 KV cache 空间不足时: - 调度器会抢占最低优先级的请求 - 释放其 KV
cache blocks - 被抢占的请求重新加入等待队列 -
num_computed_tokens 重置为 0,需要重新计算
优化建议
提高吞吐量
- 增加
max_num_batched_tokens(如 16384):允许更大的批次 - 增加
gpu_memory_utilization(如 0.95):提供更多 KV cache 空间 - 增加
max_num_seqs(如 512):支持更多并发请求
改善延迟
- 减小
max_num_batched_tokens(如 2048):减少 prefill 对 decode 的影响 - 减小
max_num_seqs:减少竞争,降低抢占频率
避免 OOM
- 降低
gpu_memory_utilization:为系统预留更多内存 - 减小
max_num_seqs:减少 KV cache 需求 - 使用 tensor parallelism:在多个 GPU 间分片模型
Notes
这三个参数形成了一个相互制约的系统: -
max_num_batched_tokens 和 max_num_seqs
是调度约束,限制每次迭代能处理多少工作 -
gpu_memory_utilization 是内存约束,通过 KV
cache 分配间接限制能同时支持多少请求 - 如果 KV cache 内存不足以支持
max_num_seqs
个满长度序列,会发生频繁的抢占,严重影响性能
合理配置需要根据: 1. 模型大小和 GPU 内存容量 2. 预期的请求模式(长提示词 vs 短对话) 3. 性能目标(吞吐量 vs 延迟)
进行平衡调整。
Citations
File: vllm/config/scheduler.py (L50-55)
max_num_batched_tokens: int = Field(default=DEFAULT_MAX_NUM_BATCHED_TOKENS, ge=1)
"""Maximum number of tokens to be processed in a single iteration.
The default value here is mainly for convenience when testing.
In real usage, this should be set in `EngineArgs.create_engine_config`.
"""File: vllm/config/scheduler.py (L57-62)
max_num_seqs: int = Field(default=DEFAULT_MAX_NUM_SEQS, ge=1)
"""Maximum number of sequences to be processed in a single iteration.
The default value here is mainly for convenience when testing.
In real usage, this should be set in `EngineArgs.create_engine_config`.
"""File: vllm/config/scheduler.py (L265-270)
if self.max_num_batched_tokens < self.max_num_seqs:
raise ValueError(
f"max_num_batched_tokens ({self.max_num_batched_tokens}) must "
"be greater than or equal to max_num_seqs "
f"({self.max_num_seqs})."
)File: vllm/config/scheduler.py (L272-278)
if self.max_num_batched_tokens > self.max_num_seqs * max_model_len:
logger.warning(
"max_num_batched_tokens (%d) exceeds max_num_seqs "
"* max_model_len (%d). This may lead to unexpected behavior.",
self.max_num_batched_tokens,
self.max_num_seqs * max_model_len,
)File: vllm/config/cache.py (L49-56)
gpu_memory_utilization: float = Field(default=0.9, gt=0, le=1)
"""The fraction of GPU memory to be used for the model executor, which can
range from 0 to 1. For example, a value of 0.5 would imply 50% GPU memory
utilization. If unspecified, will use the default value of 0.9. This is a
per-instance limit, and only applies to the current vLLM instance. It does
not matter if you have another vLLM instance running on the same GPU. For
example, if you have two vLLM instances running on the same GPU, you can
set the GPU memory utilization to 0.5 for each instance."""File: vllm/v1/worker/utils.py (L253-272)
def request_memory(init_snapshot: MemorySnapshot, cache_config: CacheConfig) -> float:
"""
Calculate the amount of memory required by vLLM, then validate
that the current amount of free memory is sufficient for that.
"""
requested_memory = init_snapshot.total_memory * cache_config.gpu_memory_utilization
if init_snapshot.free_memory < requested_memory:
GiB = lambda b: round(b / GiB_bytes, 2)
raise ValueError(
f"Free memory on device {init_snapshot.device_} "
f"({GiB(init_snapshot.free_memory)}/"
f"{GiB(init_snapshot.total_memory)} GiB) on startup "
f"is less than desired GPU memory utilization "
f"({cache_config.gpu_memory_utilization}, "
f"{GiB(requested_memory)} GiB). Decrease GPU memory "
f"utilization or reduce GPU memory used by other processes."
)
return requested_memoryFile: vllm/v1/worker/gpu_worker.py (L343-345)
self.available_kv_cache_memory_bytes = (
self.requested_memory - profile_result.non_kv_cache_memory
)File: vllm/v1/core/kv_cache_utils.py (L829-844)
def get_num_blocks(
vllm_config: VllmConfig, num_layers: int, available_memory: int, page_size: int
) -> int:
"""
Get the number of kv cache blocks.
Args:
vllm_config: The global VllmConfig
num_layers: The number of layers
available_memory: Memory available for KV cache in bytes.
page_size: The page size of the KV cache.
"""
num_blocks = int(available_memory // page_size // num_layers)
num_blocks = max(num_blocks, 0)
num_blocks = may_override_num_blocks(vllm_config, num_blocks)
return num_blocksFile: docs/configuration/optimization.md (L15-24)
WARNING 05-09 00:49:33 scheduler.py:1057 Sequence group 0 is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space. This can affect the end-to-end performance. Increase gpu_memory_utilization or tensor_parallel_size to provide more KV cache memory. total_cumulative_preemption_cnt=1While this mechanism ensures system robustness, preemption and recomputation can adversely affect end-to-end latency. If you frequently encounter preemptions, consider the following actions:
- Increase
gpu_memory_utilization. vLLM pre-allocates GPU cache using this percentage of memory. By increasing utilization, you can provide more KV cache space. - Decrease
max_num_seqsormax_num_batched_tokens. This reduces the number of concurrent requests in a batch, thereby requiring less KV cache space. - Increase
tensor_parallel_size. This shards model weights across GPUs, allowing each GPU to have more memory available for KV cache. However, increasing this value may cause excessive synchronization overhead. - Increase
pipeline_parallel_size. This distributes model layers across GPUs, reducing the memory needed for model weights on each GPU, indirectly leaving more memory available for KV cache. However, increasing this value may cause latency penalties.
**File:** docs/configuration/optimization.md (L43-48)
```markdown
You can tune the performance by adjusting `max_num_batched_tokens`:
- Smaller values (e.g., 2048) achieve better inter-token latency (ITL) because there are fewer prefills slowing down decodes.
- Higher values achieve better time to first token (TTFT) as you can process more prefill tokens in a batch.
- For optimal throughput, we recommend setting `max_num_batched_tokens > 8192` especially for smaller models on large GPUs.
- If `max_num_batched_tokens` is the same as `max_model_len`, that's almost the equivalent to the V0 default scheduling policy (except that it still prioritizes decodes).