Appearance
引擎核心 — 概念
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/ROUTERsocket 通信 - EngineCore 运行在独立进程
- 支持多前端同时连接
v0.26 修复了输出缓冲复用与 ZMQ 发送交错的 Bug(#50053):EngineCoreProc 把输出编码进一个复用的 bytearray 作为首帧,而旧代码的 send_multipart(track=True) 只跟踪最后一帧,导致 ZMQ 尚在发送时首帧就被下一轮覆盖。修复后新增 _send_msg_tracking_payload() 对首帧单独 track、pending 队列只跟踪 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 等模型声明
SupportsRealtime(buffer_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_wrapper(vllm/v1/fault_tolerance/engine_core_sentinel.py)包裹:EngineCore 出错时不立刻抛出,而是 on_fault() 中止所有请求(置 FINISHED_ABORTED)、清空 batch_queue、标记 UNHEALTHY/DEAD,然后在超时窗口内等待外部经 FT_UTILITY_METHOD(handle_fault_tolerance)utility RPC 下发的 retry 指令;超时才抛出原异常。控制端点为 entrypoints/serve/fault_tolerance/api_router.py 的 POST /fault_tolerance/apply。
Elastic EP 两阶段扩缩容
弹性专家并行扩缩容从单步 scale_elastic_ep(n) 改为两阶段(#47288):prepare_elastic_ep(new_dp_size) 先做扩缩容重配置但不把请求路由到新 engine,commit_elastic_ep() 再 finalize(更新 data_parallel_size_local、eplb_config.num_redundant_experts);中间状态由 DPLBAsyncMPClient._prepared_elastic_ep 缓存,并用新增的 VLLM_ENGINE_READY_TIMEOUT_S 轮询输入 socket 等待所有新 engine identity 就绪。
RL 权重同步
两条互补的 RL 训练集成:
- 权重版本号(#49040):EngineCore 持有不透明的
_weight_version(set/get_weight_version),经EngineCoreClient的 utility RPC 通道透传;AsyncLLM.finish_weight_update(weight_version=...)可在提交后打版本号,便于把 rollout 归属到产生它的权重版本。 - 有状态 Trainer-Send 抽象(#48042 [1/N]):
vllm/distributed/weight_transfer/base.py新增WeightSourceABC(可重迭代,metadata()廉价、__iter__用materialize_full_tensor聚合 FSDPDTensor)+TrainerWeightTransferEngine.trainer_init()一次性绑定 source,之后每轮用无参send_weights()重放(rank 0 即is_sender独占控制面);内置HTTPVLLMWeightSyncClient(clients.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)侧两阶段:BAKE 用FakeRDTTensor跑一遍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.py 的 get_metrics_snapshot() 直接读内存 REGISTRY。
主要指标族
| 族 | 代表指标 | 含义 |
|---|---|---|
| 调度状态 | vllm:num_requests_running/waiting、vllm: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_source、vllm:generation_tokens | 按 source(本地算/缓存/KV 传输)拆分 |
| 缓存命中 | vllm:prefix_cache_queries/hits、vllm:mm_cache_* | 前缀/多模态缓存命中率 |
| 推测解码 | vllm:spec_decode_num_accepted_tokens[_per_pos] | 草稿接受率(含 diffusion 别名) |
| MFU | vllm:estimated_flops_per_gpu_total | 解析估算的每 GPU FLOPs |
MFU 的解析估算:vllm/v1/metrics/perf.py 不测内核、也不除以 GPU 峰值,而是按模型结构解析地算 FLOPs。ModelMetrics 聚合若干 ComponentMetrics 子类——AttentionMetrics、MLAAttentionMetrics(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.py 的 attach_router() 把 make_asgi_app(registry) 挂到 /metrics,registry 来自 v1/metrics/prometheus.py 的 get_prometheus_registry()(多进程下走 PROMETHEUS_MULTIPROC_DIR)。Ray 部署改用 ray_wrappers.py 的 RayPrometheusStatLogger,指标名做 OTel 合规转义(:→_)。
配置:ObservabilityConfig(config/observability.py)主要开关——enable_mfu_metrics、kv_cache_metrics(+采样率)、cudagraph_metrics、collect_detailed_traces["model","worker","all"]、otlp_traces_endpoint、enable_logging_iteration_details、show_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 IDsfinish_reason:STOP / LENGTH / ABORT / ERRORlogprobs:token 概率events:生命周期事件
FinishReason 枚举
| 值 | 含义 |
|---|---|
| STOP | 遇到 EOS token 或 stop token |
| LENGTH | 达到 max_tokens 限制 |
| ABORT | 用户主动取消 |
| ERROR | 推理过程中出错 |
| REPETITION | 重复惩罚触发 |
相关概念
- Continuous Batching — 调度器的核心策略
- Paged Attention — KV 缓存的内存管理
- CUDA Graph — decode 阶段的图优化
- Flash Attention — 高效注意力计算内核
- 调度系统 — 调度器的详细分析
- API服务 — Rust Frontend 和 DP Supervisor 的前端模式