Skip to content

引擎核心 — 概念

EngineCore 主循环

EngineCore 的核心是一个无限循环,每轮迭代执行三个阶段:

阶段 1:调度

调度器 分析当前所有待处理请求,决定:

  • 哪些新请求开始 prefill
  • 哪些请求继续 decode
  • 是否需要 preemption(抢占)
  • KV 缓存块的分配与释放

阶段 2:执行

Executor 接收 SchedulerOutput,分发到 Worker 执行模型前向传播。MultiProc 模式下通过 pipe 通信,Ray 模式下通过 Ray actor 调用。

阶段 3:输出处理

OutputProcessor 将原始模型输出转换为用户可见的输出:

  • logits → token IDs(通过采样)
  • token IDs → 文本(通过 Detokenizer)
  • 处理 finish reason(STOP、LENGTH、ABORT)
  • 计算 logprobs 和 prompt logprobs

AsyncLLM 异步前端

AsyncLLM 是面向 API 服务器的异步接口:

关键职责:

  • 输入处理:tokenize prompt,处理多模态输入,应用 chat template(raw prompt 的预处理可卸载到 renderer 线程池,#49608;v0 时代的 InputPreprocessor 已删除,见 topics/serving/concepts
  • 输出处理:detokenize,流式输出,统计信息收集
  • 请求管理:跟踪活跃请求,处理 abort 信号;准入控制(#49445)——check_admission() 在把请求发给 EngineCore 前按 server 级 max_num_queued_reqs / max_num_queued_tokens 拦截,超限抛 GracefulHTTPError 子类映射 HTTP 503
  • 指标收集:TTFT、TPOT、吞吐量等性能指标
  • 启动提速:多模态 renderer warmup 与 EngineCore 进程的模型加载重叠执行(#54557)——warmup 任务提交到与服务路径同一个 max_workers=1 的线程池(物理上不会与 _process_multimodal 并发进入 numba workqueue),且只在 engine-core 进程全部 fork 完成后才启动(第一版 #52764 因 fork 时持有锁死锁被 revert)

EngineCoreClient 通信机制

EngineCoreClient 提供两种通信模式:

同步模式(SYNC)

用于 LLMEngine(离线推理):

  • 直接函数调用,EngineCore 在同进程中运行
  • 适合批量推理场景

异步模式(ASYNC)

用于 AsyncLLM(在线服务):

  • 通过 ZMQ DEALER/ROUTER socket 通信
  • EngineCore 运行在独立进程
  • 支持多前端同时连接

v0.26 修复了输出缓冲复用与 ZMQ 发送交错的 Bug(#50053):EngineCoreProc 把输出编码进一个复用的 bytearray 作为首帧,而旧代码的 send_multipart(track=True) 只跟踪最后一帧,导致 ZMQ 尚在发送时首帧就被下一轮覆盖。修复后新增 _send_msg_tracking_payload() 对首帧单独 trackpending 队列只跟踪 bytearray;客户端侧则彻底删除了手动 pending_messages 引用计数,零拷贝张量帧改由 ZMQ 经 memoryview→owner 引用链保活。

请求处理流水线

一个请求经过的完整处理链:

Model Runner V2 (MRV2)

v0.22 起在 Worker 层引入 Model Runner V2 (MRV2),与 v1 的 GPUModelRunner 并存,由 VllmConfig.use_v2_model_runner 选择:

  • VLLM_USE_V2_MODEL_RUNNER 环境变量优先;
  • v0.26 起 MRV2 先成为所有稠密生成模型的默认(PR #44443,核心判定 not is_moe);2026-08 底(#53183)进一步删除 DEFAULT_V2_MODEL_RUNNER_ARCHITECTURES 白名单,MRV2 成为全部模型的默认 runner——VllmConfig.use_v2_model_runner 默认 True,仅 ROCm 上 DeepseekV32/V4 仍回退 V1(ROCM_DEFAULT_MRV1_ARCHITECTURES),无 Triton 时回退,含 _get_v2_model_runner_unsupported_features() 清单中的特性(stock torch.compile、SP+TP>1、ngram、P-Eagle、DBO、elastic EP、自定义 logits processors 等)时告警回退 v1;反向地,V1 会直接拒绝 PCP、dspark、adaptive verification、dflash2、扩散模型、batch-sharded sampling。两份 unsupported 清单(vllm/config/vllm.py)是理解当前 V1/V2 能力边界的最准确入口。

MRV2 把原 gpu_model_runner.py(约 7700 行)中"与具体模型架构耦合"的逻辑(注意力元数据构造、多模态预处理、采样器组合)拆解成可插拔组件,自身只剩通用执行调度(约 2200 行,且持续吸纳新能力):

  • RequestState:集中维护所有活跃请求的 token / 位置 / 采样状态,大张量(如 all_token_ids)用 UVA(统一虚拟寻址)而非 GPU 显存存放以省显存。
  • ModelState:按模型架构封装注意力元数据与多模态预处理的差异,由 init_model_state() 选择,模型也可通过 get_model_state_cls() 自定义。
  • 模块化采样 / 推测解码:采样算子(penalties、min_p、gumbel、logprob、bad_words…)拆到 vllm/v1/worker/gpu/sample/,推测解码 speculator 按算法(Eagle / DFlash / MTP / Gemma4 / Autoregressive)拆到 vllm/v1/worker/gpu/spec_decode/<algo>/

MRV2 扩展能力

MRV2 沿用上述可插拔结构持续吸收新能力:

  • 实时嵌入(realtime embeddings):流式 ASR 等模型声明 SupportsRealtimebuffer_realtime_audio 持续消费音频流产出 prompt 段)。MRV2 的 encoder runner 在 is_realtime=True 时不再跳过 decode 阶段的媒体嵌入收集,保证流式音频嵌入能随生成持续注入。
  • 高效视频采样(EVS):Qwen VL 等模型按时间元数据丢弃冗余视频帧(_postprocess_video_embeds_evs),在喂入 LLM 前就裁剪 vision token 数,随后按保留的嵌入重算 mrope 位置。
  • 推测解码性能:草稿模型的注意力后端可经 speculative_config.attention_backend 独立指定;DFlash 把全层 K/V 投影、K-norm、RoPE 各自堆叠成单个融合 GEMM / RMSNorm 一次性算完;贪心草稿采样开启 use_local_argmax_reduction 后,每 token 的 TP 通信从 O(词表大小) 的全词表 all-reduce 降为 O(2·tp)(每 rank 本地 argmax 后只对 2·tp 个元素做两次小 all-reduce)。

异步调度的输出处理

异步调度路径下,模型输出统一以 AsyncModelRunnerOutput 包装,由 executor 在 output_rank 上 get_output() 解包,不再需要 Runner 内部判断 async_scheduling 分别返回同步/异步对象,简化了 EngineCore 与 MultiprocExecutor 的分支。

弹性与训练集成

v0.26 这一波 EngineCore 的演进围绕横向扩展韧性RL 训练集成两条主线:

容错框架(Fault Tolerance)

针对 MoE DP+EP 外部负载均衡部署(详见 topics/distributed/),新增 FaultToleranceConfig.engine_recovery_timeout_sec(默认 120s)与 --enable-fault-tolerance 开关。run_busy_loop@fault_tolerant_wrappervllm/v1/fault_tolerance/engine_core_sentinel.py)包裹:EngineCore 出错时不立刻抛出,而是 on_fault() 中止所有请求(置 FINISHED_ABORTED)、清空 batch_queue、标记 UNHEALTHY/DEAD,然后在超时窗口内等待外部经 FT_UTILITY_METHODhandle_fault_tolerance)utility RPC 下发的 retry 指令;超时才抛出原异常。控制端点为 entrypoints/serve/fault_tolerance/api_router.pyPOST /fault_tolerance/apply

Elastic EP 两阶段扩缩容

弹性专家并行扩缩容从单步 scale_elastic_ep(n) 改为两阶段(#47288):prepare_elastic_ep(new_dp_size) 先做扩缩容重配置但不把请求路由到新 enginecommit_elastic_ep() 再 finalize(更新 data_parallel_size_localeplb_config.num_redundant_experts);中间状态由 DPLBAsyncMPClient._prepared_elastic_ep 缓存,并用新增的 VLLM_ENGINE_READY_TIMEOUT_S 轮询输入 socket 等待所有新 engine identity 就绪。

RL 权重同步

两条互补的 RL 训练集成:

  • 权重版本号(#49040):EngineCore 持有不透明的 _weight_versionset/get_weight_version),经 EngineCoreClient 的 utility RPC 通道透传;AsyncLLM.finish_weight_update(weight_version=...) 可在提交后打版本号,便于把 rollout 归属到产生它的权重版本。
  • 有状态 Trainer-Send 抽象(#48042 [1/N]):vllm/distributed/weight_transfer/base.py 新增 WeightSource ABC(可重迭代,metadata() 廉价、__iter__materialize_full_tensor 聚合 FSDP DTensor)+ TrainerWeightTransferEngine.trainer_init() 一次性绑定 source,之后每轮用无参 send_weights() 重放(rank 0 即 is_sender 独占控制面);内置 HTTPVLLMWeightSyncClientclients.py)走 RLHF HTTP 路由。

v0.26.1rc0 后这条主线继续深化:

  • DP 状态同步自适应(#52957):异步 RL 在 wave 中途下发 PauseGeneration 时,各 DP rank 需经 ParallelConfig.sync_dp_state() 的 all-reduce 对「是否还有未完成请求/是否可 pause」达成共识。原实现把 finish-sync 硬编码每 32 步一次,空闲 rank 的 pause 共识要等完整 interval 的 dummy batch。改动:interval 提为 --dp-sync-interval默认 32→16),且每个 wave 的第 1 步也强制同步——空闲 pause 只需一个 dummy batch 即可共识。配套:pause 完成时 synchronize_device(#52914)、DP coordinator 转发 wake 不再假设 engine 已启动(#51481)。
  • sharded RDT 权重传输vllm/distributed/weight_transfer/sharded_rdt_{trainer,engine}.py,各约 1500 行):Ray Direct Transport 的按 shard 拉取架构——trainer 侧 per-rank NIXL producer server(Ray actor)在 send_weights 时按 group 收集权重经 CUDA IPC share;consumer(vLLM worker)侧两阶段:BAKEFakeRDTTensor 跑一遍 load_weights 记录每个 leaf 模块的目的 slice,REPLAY 每轮不再走 load_weights、按 plan 打包拉取并只拉本 worker 在 TP/EP 下真正消费的 slice。另有坐标稀疏 NCCL 更新(sparse_nccl_engine.py,#53751)、rank-local IPC 引擎(#52497)。
  • sleep/wake_up 收尾重构(#50431):pause mode 校验从硬编码 ("keep","abort","wait") 改为 get_args(PauseMode)(单一事实来源),Scheduler.set_pause_state() 增加日志;v0 多进程时代遗留的两条 FIXME 删除。启动期还会监控前端进程、engine 起不来及时失败(#43417)。

指标与可观测性

vLLM 的指标体系采用 生产者 → 统计结构 → 多路 Logger → 出口 的分层设计,把「采什么」(scheduler/前端)和「怎么发」(stdout / Prometheus / Ray)彻底解耦。

数据流:每轮调度由 Scheduler.make_stats()v1/core/sched/scheduler.py)产出 SchedulerStats(运行/等待请求数、kv_cache_usage、prefix-cache、spec-decode、perf 等);前端 AsyncLLM.output_handler 再从 EngineCoreOutputs 构建 IterationStats,在其中推导出 TTFT、ITL 与请求生命周期(v1/metrics/stats.py)。两者统一交给 StatLoggerManager.record()v1/metrics/loggers.py)扇出。

Logger 抽象StatLoggerBase 是可插拔接口,默认两路输出——LoggingStatLogger(写日志)和 PrometheusStatLogger(定义全部 vllm:* 指标,按 engine label 区分 DP 实例)。StatLoggerManager 对 DP 场景透明:本地 Logger 复制 N 份,Prometheus 用单实例 + label。自定义 Logger 经 STAT_LOGGER_PLUGINS_GROUP 插件注入;离线推理则用 reader.pyget_metrics_snapshot() 直接读内存 REGISTRY。

主要指标族

代表指标含义
调度状态vllm:num_requests_running/waitingvllm:kv_cache_usage_perc实时队列与 KV 占用
生命周期vllm:time_to_first_token_seconds(TTFT)、vllm:inter_token_latency_seconds(ITL)、vllm:e2e_request_latency_seconds端到端时延分解
Token 计数vllm:prompt_tokens_by_sourcevllm:generation_tokens按 source(本地算/缓存/KV 传输)拆分
缓存命中vllm:prefix_cache_queries/hitsvllm:mm_cache_*前缀/多模态缓存命中率
推测解码vllm:spec_decode_num_accepted_tokens[_per_pos]草稿接受率(含 diffusion 别名)
MFUvllm:estimated_flops_per_gpu_total解析估算的每 GPU FLOPs

MFU 的解析估算vllm/v1/metrics/perf.py 不测内核、也不除以 GPU 峰值,而是按模型结构解析地算 FLOPs。ModelMetrics 聚合若干 ComponentMetrics 子类——AttentionMetricsMLAAttentionMetrics(DeepSeek MLA)、FfnMetrics(稠密 + MoE routed/shared)、UnembedMetrics(LM head)——每个组件从 VllmConfig 抽取维度,基于 ExecutionContext(prefill/decode 的 Σ tokens×context)给出 FLOPs 分解。量化只影响字节数,不影响 MAC=2·… 的 FLOPs。日志里的 MFU: %.1f TF/s/GPU 实为已达 TF/s;真 MFU% 由用户用 PromQL rate(vllm:estimated_flops_per_gpu_total[1m])/1e12 / <GPU峰值> 自行换算,由 enable_mfu_metrics 开关控制。

/metrics 出口entrypoints/serve/instrumentator/metrics.pyattach_router()make_asgi_app(registry) 挂到 /metrics,registry 来自 v1/metrics/prometheus.pyget_prometheus_registry()(多进程下走 PROMETHEUS_MULTIPROC_DIR)。Ray 部署改用 ray_wrappers.pyRayPrometheusStatLogger,指标名做 OTel 合规转义(:_)。

配置ObservabilityConfigconfig/observability.py)主要开关——enable_mfu_metricskv_cache_metrics(+采样率)、cudagraph_metricscollect_detailed_traces["model","worker","all"]、otlp_traces_endpointenable_logging_iteration_detailsshow_hidden_metrics_for_version(弃用指标的迁移逃生口)。

关键数据结构

EngineCoreRequest

使用 msgspec 定义的高效二进制序列化结构体:

  • prompt_token_ids:tokenized 输入
  • sampling_params:采样参数(temperature、top-k 等)
  • lora_request:LoRA 适配器(可选)
  • arrival_time:请求到达时间(用于计算 TTFT)

EngineCoreOutput

每轮迭代的输出:

  • new_token_ids:新生成的 token IDs
  • finish_reason:STOP / LENGTH / ABORT / ERROR
  • logprobs:token 概率
  • events:生命周期事件

FinishReason 枚举

含义
STOP遇到 EOS token 或 stop token
LENGTH达到 max_tokens 限制
ABORT用户主动取消
ERROR推理过程中出错
REPETITION重复惩罚触发

相关概念