【vllm】vllm根据并发学习调度

并发性指的是running 队列中的吗, 不考虑waiting队列中的吗。running队列中是decode阶段的请求吧 那并发是不是就指的 decode 阶段请求呢? 结合代码分析

并发性的准确定义

并发性指的是 RUNNING 队列

是的,并发性特指 running 队列中的请求数量 ,即 len(self.running)。这个数量受 max_num_seqs 硬性限制: 1

python 复制代码
self.max_num_running_reqs = self.scheduler_config.max_num_seqs

调度器会严格检查这个限制: 2

python 复制代码
if len(self.running) == self.max_num_running_reqs:
    break

不考虑 WAITING 队列

并发性不考虑 waiting 队列中的请求 。waiting 队列中的请求处于等待状态,尚未被调度执行: 3

python 复制代码
self.waiting = create_request_queue(self.policy)
# requests skipped in waiting flow due async deps or constraints.
self.skipped_waiting = create_request_queue(self.policy)
self.running: list[Request] = []

RUNNING 队列不全是 Decode 阶段

running 队列中的请求不全是 decode 阶段 。vLLM V1 采用统一调度器设计,不严格区分 prefill/decode 阶段: 4

python 复制代码
def schedule(self) -> SchedulerOutput:
    # NOTE(woosuk) on the scheduling algorithm:
    # There's no "decoding phase" nor "prefill phase" in the scheduler.
    # Each request just has the num_computed_tokens and
    # num_tokens_with_spec.

running 队列中的请求可能处于多种状态:

  • 新请求的 prefill 阶段
  • 正在进行的 decode 阶段
  • 分块 prefill 的请求
  • 恢复执行的抢占请求

并发性 ≠ 仅 Decode 阶段

并发性不等于仅 decode 阶段的请求数。并发性指的是所有正在执行前向计算的请求,无论它们处于什么阶段。

从代码中可以看到,调度器会为不同状态的请求分配不同的 token 数量: 5

python 复制代码
num_new_tokens = (
    request.num_tokens_with_spec
    + request.num_output_placeholders
    - request.num_computed_tokens
)

请求状态转换流程

新请求到达
被调度执行
被抢占
重新排队
完成或中止
被中止
WAITING
RUNNING
PREEMPTED
FINISHED
RUNNING 队列包含:

  • prefill 阶段请求

  • decode 阶段请求 - chunked prefill 请求

  • 恢复执行的请求

总结

  • 并发性 = len(self.running),仅统计 RUNNING 队列
  • 不包含 waiting 队列中的请求
  • 不限于 decode 阶段,包含所有正在执行的请求
  • 统一调度:vLLM V1 混合处理不同阶段的请求

Notes

  • vLLM V1 的统一调度器设计使得 prefill 和 decode 可以在同一轮中混合执行
  • 实际并发能力受 max_num_seqs、token 预算、KV cache 等多重因素制约
  • 可以通过 get_request_counts() 方法获取运行中和等待中的请求数 6

Wiki pages you might want to explore:

Wiki pages you might want to explore:

Citations

File: vllm/v1/core/sched/scheduler.py (L105-106)

python 复制代码
        self.max_num_running_reqs = self.scheduler_config.max_num_seqs
        self.max_num_scheduled_tokens = (

File: vllm/v1/core/sched/scheduler.py (L166-169)

python 复制代码
        self.waiting = create_request_queue(self.policy)
        # requests skipped in waiting flow due async deps or constraints.
        self.skipped_waiting = create_request_queue(self.policy)
        self.running: list[Request] = []

File: vllm/v1/core/sched/scheduler.py (L341-365)

python 复制代码
    def schedule(self) -> SchedulerOutput:
        # NOTE(woosuk) on the scheduling algorithm:
        # There's no "decoding phase" nor "prefill phase" in the scheduler.
        # Each request just has the num_computed_tokens and
        # num_tokens_with_spec. num_tokens_with_spec =
        # len(prompt_token_ids) + len(output_token_ids) + len(spec_token_ids).
        # At each step, the scheduler tries to assign tokens to the requests
        # so that each request's num_computed_tokens can catch up its
        # num_tokens_with_spec. This is general enough to cover
        # chunked prefills, prefix caching, speculative decoding,
        # and the "jump decoding" optimization in the future.

        scheduled_new_reqs: list[Request] = []
        scheduled_resumed_reqs: list[Request] = []
        scheduled_running_reqs: list[Request] = []
        preempted_reqs: list[Request] = []

        req_to_new_blocks: dict[str, KVCacheBlocks] = {}
        num_scheduled_tokens: dict[str, int] = {}
        token_budget = self.max_num_scheduled_tokens
        if self._pause_state == PauseState.PAUSED_ALL:
            # Do not schedule any requests when paused.
            token_budget = 0

        # Encoder-related.

File: vllm/v1/core/sched/scheduler.py (L397-408)

python 复制代码
            num_new_tokens = (
                request.num_tokens_with_spec
                + request.num_output_placeholders
                - request.num_computed_tokens
            )
            if 0 < self.scheduler_config.long_prefill_token_threshold < num_new_tokens:
                num_new_tokens = self.scheduler_config.long_prefill_token_threshold
            num_new_tokens = min(num_new_tokens, token_budget)

            # Make sure the input position does not exceed the max model len.
            # This is necessary when using spec decoding.
            num_new_tokens = min(

File: vllm/v1/core/sched/scheduler.py (L561-562)

python 复制代码
                if len(self.running) == self.max_num_running_reqs:
                    break

File: vllm/v1/core/sched/scheduler.py (L1722-1724)

python 复制代码
    def get_request_counts(self) -> tuple[int, int]:
        """Returns (num_running_reqs, num_waiting_reqs)."""
        return len(self.running), len(self.waiting) + len(self.skipped_waiting)


运行中的请求,decoder一段后请求被抢占后,重新调调时, 要重新做prefill吗?作prefill 要带上之前的 token 吗?
The user is asking about VLLM's preemption mechanism - specifically when a running request (in decode phase) gets preempted and then rescheduled, does it need to redo prefill? And if so, does it include the previously generated tokens? Let me analyze this from the VLLM source code perspective.

VLLM 抢占后重新调度深度分析

两种抢占模式入口

python 复制代码
# vllm/core/scheduler.py

class PreemptionMode(Enum):
    SWAP = "swap"           # KV Cache换出到CPU
    RECOMPUTE = "recompute" # 丢弃KV Cache,重新计算

def _preempt(
    self,
    seq_group: SequenceGroup,
    blocks_to_swap_out: Dict[int, int],
    preemption_mode: Optional[PreemptionMode] = None,
) -> PreemptionMode:
    
    # 默认选择逻辑
    if preemption_mode is None:
        # beam search 强制用 RECOMPUTE
        if seq_group.get_max_num_running_seqs() == 1:
            preemption_mode = PreemptionMode.SWAP
        else:
            preemption_mode = PreemptionMode.RECOMPUTE
    
    if preemption_mode == PreemptionMode.RECOMPUTE:
        self._preempt_by_recompute(seq_group)
    else:
        self._preempt_by_swap(seq_group)
    
    return preemption_mode

RECOMPUTE 模式深度分析

抢占时做了什么

python 复制代码
# vllm/core/scheduler.py

def _preempt_by_recompute(
    self,
    seq_group: SequenceGroup,
) -> None:
    
    seqs = seq_group.get_seqs(status=SequenceStatus.RUNNING)
    assert len(seqs) == 1  # 只支持单序列
    
    for seq in seqs:
        seq.status = SequenceStatus.WAITING  # ← 状态改回WAITING
        
        # ⚠️ 关键:释放所有KV Cache blocks
        self.block_manager.free(seq)
        
        # ⚠️ 关键:token数据不丢失!
        # seq.data 里保存着:
        #   - 原始输入 prompt tokens
        #   - 已生成的 output tokens
        # 全部保留!
    
    # 重新放回 waiting 队列头部(优先被调度)
    self.waiting.appendleft(seq_group)

seq.data 里保存了什么

python 复制代码
# vllm/sequence.py

class SequenceData:
    def __init__(self, prompt_token_ids: List[int]):
        
        self._prompt_token_ids = prompt_token_ids  # 原始输入
        self._output_token_ids: List[int] = []     # 已生成的token
        
    def get_token_ids(self) -> List[int]:
        # 返回完整序列 = prompt + 已生成output
        return self._prompt_token_ids + self._output_token_ids
    
    def get_len(self) -> int:
        return len(self._prompt_token_ids) + len(self._output_token_ids)
复制代码
抢占时数据状态:

假设请求:
  prompt:  [tok1, tok2, tok3]        (原始输入3个token)
  output:  [tok4, tok5, tok6]        (已生成3个token)

_preempt_by_recompute 后:
  ✅ seq.data._prompt_token_ids = [tok1, tok2, tok3]  保留
  ✅ seq.data._output_token_ids = [tok4, tok5, tok6]  保留
  ❌ KV Cache blocks = 全部释放!

重新调度时的 Prefill

python 复制代码
# vllm/core/scheduler.py

def _schedule_prefills(self, budget):
    
    while self.waiting:
        seq_group = self.waiting[0]  # 取出(可能是被抢占的请求)
        
        # 计算需要prefill的token数量
        num_new_tokens = self._get_num_new_tokens(
            seq_group,
            SequenceStatus.WAITING
        )

def _get_num_new_tokens(self, seq_group, status):
    
    num_new_tokens = 0
    seqs = seq_group.get_seqs(status=status)
    
    for seq in seqs:
        # ⚠️ 关键:返回的是完整序列长度!
        # = prompt tokens + 已生成的output tokens
        num_new_tokens += seq.data.get_num_uncomputed_tokens()
    
    return num_new_tokens

# SequenceData
def get_num_uncomputed_tokens(self):
    # KV Cache被清空了
    # 所有token都需要重新计算
    return self.get_len()
    # = len(prompt_tokens) + len(output_tokens)
    # = 3 + 3 = 6 个token 全部重新prefill!

重新 Prefill 带哪些 Token

python 复制代码
# vllm/worker/model_runner.py

def prepare_model_input(self, seq_group_metadata_list):
    
    for seq_group_metadata in seq_group_metadata_list:
        
        if seq_group_metadata.is_prompt:  # prefill阶段
            
            seq_data = seq_group_metadata.seq_data
            
            # ⚠️ 核心:取完整token序列
            prompt_tokens = seq_data.get_token_ids()
            # = prompt_token_ids + output_token_ids
            # = [tok1,tok2,tok3] + [tok4,tok5,tok6]
            # = [tok1,tok2,tok3,tok4,tok5,tok6]  全部!
            
            input_tokens.extend(prompt_tokens)
            
            # position ids 也是完整的
            input_positions.extend(range(len(prompt_tokens)))

RECOMPUTE 完整时间线

复制代码
初始请求:prompt = [A, B, C]

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
阶段1: 正常 Prefill
  输入: [A, B, C]
  KV Cache: 生成 A,B,C 的 KV
  输出: token D (第1个生成token)
  
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
阶段2: 正常 Decode
  seq.data = prompt[A,B,C] + output[D]
  输入: [D] (只输入最新token)
  KV Cache: 复用A,B,C的KV + 生成D的KV
  输出: token E

阶段3: 正常 Decode  
  seq.data = prompt[A,B,C] + output[D,E]
  输入: [E]
  KV Cache: 复用A,B,C,D的KV + 生成E的KV
  输出: token F

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️  被抢占!RECOMPUTE模式

  seq.data = prompt[A,B,C] + output[D,E,F]  ← 数据保留
  KV Cache = 全部释放!                      ← KV丢弃
  状态: RUNNING → WAITING
  放回 waiting 队列头部

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
阶段4: 重新 Prefill ← 要带上之前所有token!

  输入: [A, B, C, D, E, F]  ← prompt + 已生成的output 全部重算
  KV Cache: 重新生成 A,B,C,D,E,F 的 KV
  输出: token G (继续生成,不会重复输出D,E,F)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
阶段5: 继续 Decode
  输入: [G]
  KV Cache: 复用A~F的KV + 生成G的KV
  输出: token H

SWAP 模式对比

python 复制代码
# vllm/core/scheduler.py

def _preempt_by_swap(self, seq_group):
    
    # KV Cache 换出到 CPU,不释放
    self.block_manager.swap_out(seq_group)
    # GPU KV Cache → CPU 内存
    
    for seq in seq_group.get_seqs(status=SequenceStatus.RUNNING):
        seq.status = SequenceStatus.SWAPPED
    
    # 放入 swapped 队列(不是waiting!)
    self.swapped.append(seq_group)

# 恢复时
def _schedule_swapped(self, budget):
    
    while self.swapped:
        seq_group = self.swapped[0]
        
        # 换回GPU
        self.block_manager.swap_in(seq_group)
        # CPU 内存 → GPU KV Cache
        
        # ✅ KV Cache 完整恢复
        # ✅ 不需要重新 Prefill!
        # ✅ 直接继续 Decode
        
        for seq in seq_group.get_seqs():
            seq.status = SequenceStatus.RUNNING

SWAP vs RECOMPUTE 完整对比

复制代码
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                SWAP              RECOMPUTE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
KV Cache     换出到CPU内存        直接释放
Token数据    保留                 保留
恢复后       直接decode           重新prefill(全部token)
重新prefill  ❌ 不需要            ✅ 需要
Prefill输入  -                   prompt + 所有已生成token
等待队列     swapped队列          waiting队列头部
ITL影响      增加swap时间         增加整个prefill时间
显存需求     需要CPU内存          不需要额外内存
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

总结

复制代码
┌────────────────────────────────────────────────────┐
│              抢占后重新调度的核心问题                 │
├────────────────┬───────────────────────────────────┤
│ RECOMPUTE模式  │                                   │
│ 要重新prefill? │ ✅ 要!                            │
│ 带哪些token?   │ prompt + 所有已生成的output token  │
│                │ 完整序列全部重新计算                │
│ 为什么?        │ KV Cache已释放,必须重建            │
├────────────────┼───────────────────────────────────┤
│ SWAP模式       │                                   │
│ 要重新prefill? │ ❌ 不需要!                        │
│ 带哪些token?   │ 不需要,KV Cache从CPU换回GPU       │
│                │ 直接继续decode                     │
│ 为什么?        │ KV Cache保存在CPU内存,可以恢复     │
└────────────────┴───────────────────────────────────┘

是的,被抢占的请求在恢复时必须重新执行 Prefill,并且重新计算时必须要带上之前已经生成的所有 token(即原始 Prompt + 已生成的 Token)。

这是 vLLM V1 版本中 RECOMPUTE 抢占模式的核心理念:用计算换显存,避免昂贵的 KV Cache 数据传输。


1. 官方文档明确指出:Preempted requests are recomputed

vLLM 官方文档在说明抢占机制时写道:

Preempted requests are recomputed when sufficient KV cache space becomes available again.
当有足够的 KV 缓存空间再次可用时,被抢占的请求将被重新计算。

同时,在 vLLM V1 中,默认的抢占模式是 RECOMPUTE 而不是 SWAP,因为在 V1 架构中,重新计算的开销更低。具体来说:

  • 重计算的开销从未超过 SWAP(交换到 CPU 内存)延迟的 20%
  • 对于小的 block 大小,重计算比 SWAP 快得多,因为数据移动成本很高

2. 为什么要带上之前所有的 Token?

这个问题触及了 Transformer 自回归生成的核心原理。

2.1 KV Cache 的本质

在生成过程中,每个 token 的 Key 和 Value 向量依赖于当前 token 本身以及之前所有 token 的上下文。KV Cache 存储的就是这些计算好的 K/V 向量,避免重复计算。

当请求被抢占时,它的 KV Cache 会被立即释放,以腾出显存给其他请求。此时,系统只保留了请求的原始信息:

  • 原始 Prompt 的 token IDs
  • 已生成的 token IDs

2.2 为什么不能"从中断处继续"?

如果没有 KV Cache,模型无法直接从第 N 个 token 继续生成。因为:

  • 生成第 N+1 个 token 时,注意力机制需要访问第 1 到第 N 个 token 的 K/V 信息
  • 这些信息正是被释放掉的 KV Cache

因此,恢复时必须从头开始重新计算所有 token 的 KV Cache,即:

  1. 输入 = [Prompt tokens] + [已生成的 tokens]
  2. 执行完整的 Prefill,一次性计算出所有位置的 KV Cache
  3. 然后从最后一个 token 的位置继续 Decode 生成

2.3 代码层面的证据

vLLM 调度器代码中的警告信息明确说明了这一点:

复制代码
WARNING ... Sequence group 0 is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space. ... total_cumulative_preemption_cnt=1

RECOMPUTE 这个模式名称本身就说明了一切------需要重新计算,而不是恢复。


3. 重计算 vs 交换:为什么 vLLM 选择重计算

有用户在 vLLM 社区讨论中问道:"为什么不使用 KV connectors 将被抢占的请求 offload 到存储层,而不是重新计算?"

回答确认:

  • vLLM V1 中,通过重计算进行抢占是默认且首选策略
  • 交换模式(SWAP,即把 KV Cache offload 到 CPU)仅用于多序列请求(如 beam search),因为重计算在那里不被支持
  • 基准测试表明,重计算的开销远低于交换

4. 重计算对性能的影响

虽然重计算比交换更高效,但它仍然会显著增加端到端延迟

  • 被抢占的请求需要花费额外的时间重新 Prefill
  • 在重计算期间,该请求不会生成新 token
  • 这会导致该请求的 ITL(Inter-Token Latency)出现一个明显的大峰值

因此,官方建议如果频繁遇到抢占,应该:

  1. 增加 gpu_memory_utilization(提供更多 KV Cache 空间)
  2. 减少 max_num_seqsmax_num_batched_tokens(降低并发)
  3. 增加 tensor_parallel_size(分片模型,每 GPU 更多显存)

5. 一个具体的例子

假设一个请求:

  • Prompt: "What is the capital of France?" (8 tokens)
  • 已生成: "The capital of France is" (6 tokens)
  • 被抢占,KV Cache 被释放

恢复时:

  1. 系统取出完整输入 = 8 + 6 = 14 tokens
  2. 执行 Prefill,一次性计算 14 个 token 的 KV Cache
  3. 从第 14 个 token 的位置继续 Decode,生成下一个 token:"Paris"
  4. 后续正常 Decode

关键点:步骤 2 的 Prefill 必须一次性处理所有 14 个 token,不能只处理新增的 6 个。


6. 总结

问题 答案
被抢占后是否需要重新 Prefill? ,必须重新执行 Prefill 来重建 KV Cache
重新 Prefill 需要带上之前的所有 token 吗? ,必须从原始 Prompt 开始,包含所有已生成的 token
为什么不能从中断处继续? 因为 KV Cache 已被释放,注意力机制需要完整的历史信息
vLLM 用什么模式实现? RECOMPUTE(V1 默认),比交换到 CPU 更高效
重计算会影响延迟吗? 会,被抢占的请求会经历一次额外的 Prefill,导致 ITL 尖峰

如果你关心的是如何通过 PD 分离(Prefill-Decode Disaggregation)从根本上避免抢占,我可以进一步展开说明这个方案。

When chunked prefill is disabled, max_num_batched_tokens must be greater than max_model_len.

In that case, if max_num_batched_tokens < max_model_len, vLLM may crash at server start‑up.

调用流程是什么这里失败,怎么返回的。这个流程是在进程创建中决定的吗

Chunked Prefill Disabled: max_num_batched_tokens Constraint

Your statement is correct. When chunked prefill is disabled, vLLM enforces that max_num_batched_tokens must be greater than max_model_len to prevent startup crashes.

Validation Logic

The constraint is enforced in SchedulerConfig.verify_max_model_len(): 1

python 复制代码
if (
    self.max_num_batched_tokens < max_model_len
    and not self.enable_chunked_prefill
):
    raise ValueError(
        f"max_num_batched_tokens ({self.max_num_batched_tokens}) is "
        f"smaller than max_model_len ({max_model_len}). "
        "This effectively limits the maximum sequence length to "
        "max_num_batched_tokens and makes vLLM reject longer "
        "sequences. Please increase max_num_batched_tokens or "
        "decrease max_model_len."
    )

Default Configuration Logic

The engine argument utils automatically handle this constraint when setting defaults: 2

python 复制代码
if not self.enable_chunked_prefill:
    # If max_model_len is too short, use the default for higher throughput.
    self.max_num_batched_tokens = max(
        model_config.max_model_len,
        self.max_num_batched_tokens,
    )

Documentation Warning

The optimization guide explicitly warns about this scenario: 3

text 复制代码
!!! warning
    When chunked prefill is disabled, `max_num_batched_tokens` must be greater than `max_model_len`.  
    In that case, if `max_num_batched_tokens < max_model_len`, vLLM may crash at server start‑up.

Why This Constraint Exists

When chunked prefill is disabled, vLLM cannot split long prompts into smaller chunks. If max_num_batched_tokens is smaller than max_model_len, the system would be unable to process requests that require the full model context length, leading to runtime failures or crashes during request processing.

Notes

  • This constraint only applies when enable_chunked_prefill = False
  • With chunked prefill enabled (default), max_num_batched_tokens can be smaller than max_model_len
  • The validation occurs during scheduler configuration initialization
  • Test cases in tests/v1/core/test_scheduler.py verify this constraint is properly enforced

Wiki pages you might want to explore:

Citations

File: vllm/config/scheduler.py (L258-270)

python 复制代码
    def verify_max_model_len(self, max_model_len: int) -> Self:
        if (
            self.max_num_batched_tokens < max_model_len
            and not self.enable_chunked_prefill
        ):
            raise ValueError(
                f"max_num_batched_tokens ({self.max_num_batched_tokens}) is "
                f"smaller than max_model_len ({max_model_len}). "
                "This effectively limits the maximum sequence length to "
                "max_num_batched_tokens and makes vLLM reject longer "
                "sequences. Please increase max_num_batched_tokens or "
                "decrease max_model_len."
            )

File: vllm/engine/arg_utils.py (L2210-2215)

python 复制代码
            if not self.enable_chunked_prefill:
                # If max_model_len is too short, use the default for higher throughput.
                self.max_num_batched_tokens = max(
                    model_config.max_model_len,
                    self.max_num_batched_tokens,
                )

File: docs/configuration/optimization.md (L61-63)

markdown 复制代码
!!! warning
    When chunked prefill is disabled, `max_num_batched_tokens` must be greater than `max_model_len`.  
    In that case, if `max_num_batched_tokens < max_model_len`, vLLM may crash at server start‑up.

Chunked Prefill 禁用时的参数验证流程

调用流程分析

当 chunked prefill 禁用且 max_num_batched_tokens < max_model_len 时,失败发生在引擎初始化阶段,而非进程创建中。

1. 验证触发点

验证在 SchedulerConfig.verify_max_model_len() 中执行: 1

python 复制代码
def verify_max_model_len(self, max_model_len: int) -> Self:
    if (
        self.max_num_batched_tokens < max_model_len
        and not self.enable_chunked_prefill
    ):
        raise ValueError(
            f"max_num_batched_tokens ({self.max_num_batched_tokens}) is "
            f"smaller than max_model_len ({max_model_len}). "
            "This effectively limits the maximum sequence length to "
            "max_num_batched_tokens and makes vLLM reject longer "
            "sequences. Please increase max_num_batched_tokens or "
            "decrease max_model_len."
        )
2. 自动修正机制

EngineArgs._set_default_max_num_seqs_and_batched_tokens_args() 中有自动修正逻辑: 2

python 复制代码
if not self.enable_chunked_prefill:
    # If max_model_len is too short, use the default for higher throughput.
    self.max_num_batched_tokens = max(
        model_config.max_model_len,
        self.max_num_batched_tokens,
    )

完整调用流程

VllmConfig SchedulerConfig EngineConfig EngineArgs 用户代码 VllmConfig SchedulerConfig EngineConfig EngineArgs 用户代码 alt [验证失败且chunked_prefill禁用] [验证通过或自动修正] LLM(engine_args) create_engine_config() SchedulerConfig(max_model_len, ...) post_init() verify_max_model_len() ValueError 抛出异常 创建成功 VllmConfig.post_init() 引擎初始化完成

返回机制

  1. 异常抛出 :直接抛出 ValueError 异常,包含详细的错误信息
  2. 阻止启动:异常会阻止 vLLM 服务器启动
  3. 用户可见:错误信息会直接显示给用户,指导如何修正参数

时机分析

这个验证发生在:

  • 引擎配置创建阶段EngineArgs.create_engine_config() 调用期间
  • 进程创建之前:在实际的 worker 进程创建之前
  • 配置验证阶段:属于配置合法性检查的一部分

Notes

  • 验证在单进程环境中完成,不涉及分布式进程创建
  • 自动修正机制会在大多数情况下避免异常
  • 只有当用户显式设置了不兼容的参数时才会抛出异常
  • 建议让 vLLM 使用默认值以避免此类配置错误

Wiki pages you might want to explore:

Wiki pages you might want to explore:

Citations

File: vllm/config/scheduler.py (L258-270)

python 复制代码
    def verify_max_model_len(self, max_model_len: int) -> Self:
        if (
            self.max_num_batched_tokens < max_model_len
            and not self.enable_chunked_prefill
        ):
            raise ValueError(
                f"max_num_batched_tokens ({self.max_num_batched_tokens}) is "
                f"smaller than max_model_len ({max_model_len}). "
                "This effectively limits the maximum sequence length to "
                "max_num_batched_tokens and makes vLLM reject longer "
                "sequences. Please increase max_num_batched_tokens or "
                "decrease max_model_len."
            )

File: vllm/engine/arg_utils.py (L2210-2215)

python 复制代码
            if not self.enable_chunked_prefill:
                # If max_model_len is too short, use the default for higher throughput.
                self.max_num_batched_tokens = max(
                    model_config.max_model_len,
                    self.max_num_batched_tokens,
                )
相关推荐
muls12 小时前
java面试宝典
java·linux·服务器·网络·算法·操作系统
瑶总迷弟2 小时前
Python入门第6章:字典(键值对数据结构)
java·数据结构·python
婷婷_1722 小时前
【PCIe验证每日学习·Day23】Completion 完成包全机制深度解析
学习·程序人生·芯片·completion·pcie 验证·outsatanding·id路由
o丁二黄o2 小时前
【MyBatisPlus】MyBatisPlus介绍与使用
java
_MyFavorite_2 小时前
JAVA重点基础、进阶知识及易错点总结(14)字节流 & 字符流
java·开发语言·python
羊小猪~~3 小时前
Redis学习笔记(数据类型、持久化、事件、管道、发布订阅等)
开发语言·数据库·c++·redis·后端·学习·缓存
结衣结衣.3 小时前
【Linux】命名管道的妙用:实现进程控制与实时字符交互
linux·运维·开发语言·学习·操作系统·交互
red_redemption3 小时前
自由学习记录(151)
学习
好家伙VCC3 小时前
**CQRS模式实战:用Go语言构建高并发读写分离架构**在现代分布式系统中,随着业务复杂度的提升和用户量的增长,传统的单数据库模型逐
java·数据库·python·架构·golang