max_num_seqs 参数详解
一、参数定义
max_num_seqs 是 vLLM 和 vLLM-Ascend 中一个关键的调度配置参数,用于限制单个调度步骤中同时处理的最大序列(请求)数量。
默认值
- 默认值: 128
- 位置 :
vllm/vllm/config/scheduler.py中的SchedulerConfig.DEFAULT_MAX_NUM_SEQS
python
DEFAULT_MAX_NUM_SEQS: ClassVar[int] = 128
二、核心作用
1. 调度器层面的限制
在调度器(Scheduler)中,max_num_seqs 被转换为 max_num_running_reqs,用于限制同时处于运行状态(RUNNING)的请求数量。
代码位置 : vllm/vllm/v1/core/sched/scheduler.py
python
# 第97行
self.max_num_running_reqs = self.scheduler_config.max_num_seqs
调度逻辑:
python
# 第428-429行:当运行中的请求数达到上限时,停止从等待队列中调度新请求
if len(self.running) == self.max_num_running_reqs:
break
这意味着:
- 如果当前有 128 个请求正在运行(
max_num_seqs=128),新的请求将停留在等待队列(WAITING)中 - 只有当某个运行中的请求完成或被抢占后,等待队列中的请求才能被调度
2. 内存分配和缓冲区大小
max_num_seqs 直接影响 Worker 中各种缓冲区的分配大小。
2.1 RequestState 缓冲区
代码位置 : vllm/vllm/v1/worker/gpu/states.py
RequestState 类使用 max_num_reqs(即 max_num_seqs)来分配所有请求相关的状态缓冲区:
python
class RequestState:
def __init__(self, max_num_reqs: int, ...):
# 为每个请求分配固定大小的缓冲区
self.prompt_len = np.zeros(self.max_num_reqs, dtype=np.int32)
self.prefill_token_ids = UvaBuffer(
self.max_num_reqs, self.max_model_len, dtype=torch.int32
)
self.prefill_len = self._make_buffer(self.max_num_reqs, dtype=torch.int32)
self.num_computed_tokens = torch.zeros(
self.max_num_reqs, dtype=torch.int32, device=device
)
# ... 更多缓冲区
内存影响:
prefill_token_ids: 大小为max_num_reqs × max_model_len × 4字节(int32)- 例如:128 × 32768 × 4 = 16MB(仅这一个缓冲区)
- 所有请求状态缓冲区的总大小与
max_num_seqs成正比
2.2 InputBuffers 缓冲区
代码位置 : vllm/vllm/v1/worker/gpu/model_runner.py
python
self.input_buffers = InputBuffers(
max_num_reqs=self.max_num_reqs, # 使用 max_num_seqs
max_num_tokens=self.max_num_tokens,
...
)
InputBuffers 用于存储每个推理步骤的输入数据,其大小也受 max_num_seqs 影响。
2.3 BlockTables 缓冲区
代码位置 : vllm/vllm/v1/worker/gpu/model_runner.py
python
self.block_tables = BlockTables(
block_sizes=block_sizes,
max_num_reqs=self.max_num_reqs, # 使用 max_num_seqs
max_num_batched_tokens=self.max_num_tokens,
...
)
BlockTables 用于管理 KV Cache 的块表,其大小也与 max_num_seqs 相关。
3. 数据并行(DP)场景下的特殊含义
在 vLLM-Ascend 的文档中明确指出:
--max-num-seqs表示每个 DP group 允许处理的最大请求数。如果发送到服务的请求数超过此限制,多余的请求将保持在等待状态,不会被调度。
重要公式:
总并发能力 = max_num_seqs × data_parallel_size
示例:
- 如果
max_num_seqs=16,data_parallel_size=2 - 那么总共有 16 × 2 = 32 个请求可以同时运行
- 如果实际并发请求数为 40,那么有 8 个请求会处于等待状态
性能测试建议:
在测试性能时,一般建议
max_num_seqs × data_parallel_size >= 实际总并发数,否则等待时间也会被计入 TTFT(Time To First Token)和 TPOT(Time Per Output Token)等指标中。
4. 与 max_num_batched_tokens 的关系
max_num_seqs 和 max_num_batched_tokens 共同决定了批处理的能力:
验证逻辑 (vllm/vllm/config/scheduler.py):
python
# 第264-269行:max_num_batched_tokens 必须 >= max_num_seqs
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})."
)
关系说明:
max_num_batched_tokens: 限制单次推理步骤中处理的总 token 数max_num_seqs: 限制单次推理步骤中处理的总请求数- 理论上,如果每个请求只有 1 个 token,最多可以处理
max_num_batched_tokens个请求 - 但实际上,由于
max_num_seqs的限制,最多只能处理max_num_seqs个请求
警告逻辑(第271-277行):
python
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.",
...
)
三、影响范围
1. 内存占用
直接影响:
- 所有请求状态缓冲区的大小
- InputBuffers 的大小
- BlockTables 的大小
- 采样参数缓冲区的大小
内存计算公式(简化):
额外内存 ≈ max_num_seqs × (
max_model_len × 4 + # prefill_token_ids
各种状态缓冲区大小
)
2. 调度性能
正面影响:
- 吞吐量 : 较大的
max_num_seqs允许更多请求并发处理,可能提高吞吐量 - GPU 利用率: 更多请求可以更好地利用 GPU 的并行计算能力
负面影响:
- 延迟 : 如果
max_num_seqs太小,请求会在等待队列中停留更长时间 - 内存压力 : 更大的
max_num_seqs需要更多 GPU 内存
3. 并发处理能力
限制因素:
- 如果实际并发请求数 >
max_num_seqs × data_parallel_size,多余的请求会排队等待 - 等待时间会被计入性能指标(TTFT、TPOT),影响用户体验
4. 批处理效率
批处理大小:
- 每次推理步骤最多处理
max_num_seqs个请求 - 如果请求数 <
max_num_seqs,批处理可能不够满,GPU 利用率可能下降 - 如果请求数 >
max_num_seqs,需要多步处理,可能增加延迟
四、使用建议
1. 根据硬件资源调整
GPU 内存充足时:
- 可以设置较大的
max_num_seqs(如 256、512) - 提高并发处理能力和吞吐量
GPU 内存受限时:
- 需要减小
max_num_seqs(如 64、32) - 避免 OOM(Out of Memory)错误
2. 根据工作负载调整
高并发场景:
- 确保
max_num_seqs × data_parallel_size >= 预期并发数 - 避免请求长时间等待
低延迟优先场景:
- 可以适当减小
max_num_seqs - 减少批处理大小,降低单步处理时间
高吞吐量优先场景:
- 增大
max_num_seqs - 提高批处理效率
3. 与 max_num_batched_tokens 的平衡
经验法则:
max_num_batched_tokens应该 >=max_num_seqs- 对于短序列请求,可以设置
max_num_batched_tokens远大于max_num_seqs - 对于长序列请求,需要确保
max_num_batched_tokens足够大以处理长序列
4. 数据并行场景
重要公式:
总并发能力 = max_num_seqs × data_parallel_size
建议:
- 在设置
max_num_seqs时,考虑data_parallel_size - 确保总并发能力满足实际需求
五、代码示例
1. 在 vLLM 中设置
python
from vllm import LLM
llm = LLM(
model="your-model",
max_num_seqs=128, # 设置最大序列数
max_num_batched_tokens=2048, # 必须 >= max_num_seqs
)
2. 在 vLLM-Ascend 中设置
bash
vllm serve Qwen/Qwen3-VL-235B-A22B-Instruct \
--max-num-seqs 16 \
--data-parallel-size 2 \
--max-num-batched-tokens 4096 \
...
3. 验证配置
python
# 在 SchedulerConfig 中会自动验证
# 如果 max_num_batched_tokens < max_num_seqs,会抛出 ValueError
六、常见问题
Q1: max_num_seqs 设置太小会怎样?
A:
- 请求会在等待队列中停留更长时间
- 吞吐量下降
- 延迟增加(等待时间计入 TTFT/TPOT)
Q2: max_num_seqs 设置太大会怎样?
A:
- GPU 内存占用增加
- 可能导致 OOM
- 批处理效率可能下降(如果实际请求数远小于 max_num_seqs)
Q3: 如何确定合适的 max_num_seqs?
A:
- 根据 GPU 内存容量估算
- 根据实际并发需求计算:
max_num_seqs × data_parallel_size >= 并发数 - 通过性能测试找到最佳平衡点
Q4: max_num_seqs 和 max_num_batched_tokens 的区别?
A:
max_num_seqs: 限制请求数量max_num_batched_tokens: 限制token 数量- 两者共同决定批处理能力
七、总结
max_num_seqs 是 vLLM 中一个关键的调度和资源管理参数,它:
- 限制并发: 控制同时处理的请求数量
- 分配内存: 决定各种缓冲区的分配大小
- 影响性能: 直接影响吞吐量、延迟和 GPU 利用率
- 与 DP 相关 : 在数据并行场景下,总并发能力 =
max_num_seqs × data_parallel_size
合理设置 max_num_seqs 需要在内存占用 、并发能力 和性能指标之间找到平衡点。