vLLM 初始化流程分析
1. 概述
vLLM 的初始化是一个多阶段的系统工程,从用户调用的入口点开始,经过配置解析、Engine 创建、Worker 初始化、模型加载、KV cache 内存分配、Scheduler 创建等阶段,最终进入就绪状态等待请求。
本文以 V1 引擎为主线,追踪从入口到就绪的完整调用链。
2. 初始化流程全景
Entry Point
│
├─ EngineArgs.create_engine_config() → VllmConfig
│
├─ LLMEngine / AsyncLLM.__init__()
│ └─ EngineCoreClient 创建(in-proc 或多进程)
│
├─ EngineCore.__init__()
│ │
│ ├─ Step 1: Executor._init_executor()
│ │ └─ Worker 创建 → init_device() → load_model()
│ │
│ ├─ Step 2: _initialize_kv_caches()
│ │ ├─ get_kv_cache_specs() ← 收集各层的 attention spec
│ │ ├─ determine_available_memory() ← 显存 profiling
│ │ ├─ get_kv_cache_configs() ← 计算 block 数量分配
│ │ ├─ initialize_from_config() ← 分配 KV cache tensor
│ │ └─ compile_or_warm_up_model() ← CUDA graph 捕获
│ │
│ ├─ Step 3: resolve_kv_cache_block_sizes()
│ │
│ └─ Step 4: Scheduler.__init__()
│ └─ KVCacheManager → KVCacheCoordinator → BlockPool
│
└─ 就绪,进入 run_busy_loop()
3. 入口点
vLLM 提供两种入口,最终都汇聚到同一个 Engine 创建路径。
3.1 离线推理:LLM 类
class LLM:
def __init__(self, model, **kwargs):
# 1. 创建 EngineArgs
engine_args = EngineArgs(model=model, **kwargs)
# 2. 通过工厂方法创建 LLMEngine
self.llm_engine = LLMEngine.from_engine_args(engine_args)
3.2 API 服务器
async def build_async_engine_client_from_engine_args(args):
engine_args = AsyncEngineArgs.from_cli_args(args)
vllm_config = engine_args.create_engine_config()
async_llm = AsyncLLM.from_vllm_config(vllm_config)
两种入口的差异仅在最外层:
LLM类 →LLMEngine.from_engine_args()→ 同步 API- API 服务器 →
AsyncLLM.from_vllm_config()→ 异步 API
底层都依赖 EngineArgs.create_engine_config() 和 EngineCore。
4. 配置解析:EngineArgs → VllmConfig
EngineArgs 是一个包含所有可配置字段的大 dataclass(约 300 个字段),其核心方法是 create_engine_config()(arg_utils.py:1697-2252),按顺序生成以下子配置:
| 子配置 | 行号 | 关键职责 |
|---|---|---|
| DeviceConfig | L1709 | 设备类型(cuda/rocm/cpu) |
| ModelConfig | L1730 | 模型名、dtype、tokenizer、max_model_len(可为 -1 自动适配) |
| CacheConfig | L1754 | block_size(默认 16,此时为占位值)、gpu_memory_utilization(默认 0.9)、enable_prefix_caching |
| ParallelConfig | L1957 | TP/PP/DP/CP 并行度 |
| SchedulerConfig | L2028 | max_num_batched_tokens、max_num_seqs、调度策略 |
| LoadConfig | — | 模型加载方式(safetensors、dummy、bitsandbytes 等) |
| VllmConfig | L2224 | 聚合所有子配置 |
block_size在此时只是占位值 —— 真正的值在后续update_block_size_for_backend()和get_kv_cache_configs()阶段才确定。
AsyncEngineArgs 继承自 EngineArgs,额外增加了 enable_log_requests 等异步特有字段。
5. Engine 创建
5.1 LLMEngine
class LLMEngine:
@classmethod
def from_engine_args(cls, engine_args):
vllm_config = engine_args.create_engine_config() # Step 1
executor_class = Executor.get_class(vllm_config) # Step 2
return cls(vllm_config, executor_class)
def __init__(self, vllm_config, executor_class):
self.vllm_config = vllm_config # 保存配置
self.renderer = Renderer(...) # [L90] 输出渲染
self.input_processor = InputProcessor(...) # [L93] 输入处理
self.output_processor = OutputProcessor(...) # [L96] 输出处理
self.engine_core = EngineCoreClient.make_client(...) # [L104] 核心引擎
self.log_stats_manager = StatLoggerManager(...) # [L114] 日志
5.2 EngineCoreClient 工厂
根据 distributed_executor_backend 选择通信方式:
┌── 非多进程模式 (--distributed-executor-backend unset)
EngineCoreClient.make_client() ───┤ → InprocClient [L274]
│ 在同一进程内直接调用 EngineCore
│
└── 多进程模式
├─ LLM 类 → SyncMPClient [L716]
│ 后台进程 + ZMQ 同步通信
└─ AsyncLLM → AsyncMPClient [L887]
后台进程 + ZMQ 异步通信
多进程模式下,MPClient.__init__()(core_client.py:473-611)的核心流程:
- 创建 ZMQ context 和 input/output socket
- 调用
launch_core_engines()启动EngineCoreProc后台进程 - 等待所有 engine 发回 READY 信号
- 启动 engine core monitor 线程
6. EngineCore 初始化 —— 核心入口
EngineCore 是所有执行路径汇聚的地方。其 __init__ 是初始化的核心编排者:
Step 1: 加载插件 [L108]
Step 2: self.model_executor = executor_class(vllm_config) [L121]
└─ _init_executor() → Worker 创建序列(见第 7 节)
Step 3: kv_cache_config = self._initialize_kv_caches() [L131]
└─ KV cache 分配序列(见第 8 节)
Step 4: self.structured_output_manager = ... [L132]
Step 5: 获取 scheduler 类 [L135]
Step 6: scheduler_block_size, hash_block_size = [L144]
resolve_kv_cache_block_sizes(...)
Step 7: self.scheduler = Scheduler(...) [L148]
Step 8: 设置 PP batch queue [L191]
Step 9: 设置 prefix caching hasher [L205]
Step 10: 冻结 GC heap [L227]
7. Executor 与 Worker 初始化
7.1 Executor 选择
Executor.get_class() 根据 distributed_executor_backend 选择:
| Backend 字符串 | Executor 类 | 场景 |
|---|---|---|
"uni" | UniProcExecutor | 单进程、单 GPU |
"mp" | MultiprocExecutor | 多 GPU,ZMQ 进程通信 |
"ray" | RayDistributedExecutor | Ray 集群 |
"external_launcher" | ExecutorWithExternalLauncher | torchrun 启动 |
7.2 UniProcExecutor(单进程模式)
def _init_executor(self):
wrapper = WorkerWrapperBase(rpc_rank=0) # 创建 wrapper
wrapper.init_worker(all_kwargs=[kwargs]) # 解析 worker 类并实例化
wrapper.init_device() # 设置 CUDA 设备、分布式环境
wrapper.load_model() # 加载模型权重
current_platform.update_block_size_for_backend() # 更新 block_size
7.3 MultiprocExecutor(多进程模式)
为每个 GPU 创建一个 WorkerProc 后台进程(multiproc_executor.py:579-637),每个进程内执行相同的 init_worker → init_device → load_model 序列。与 UniProc 的区别仅在于额外增加了:
- Worker 之间通过消息队列广播
SchedulerOutput/ModelRunnerOutput - Worker monitor 线程检测健康状态
7.4 WorkerWrapperBase:解析 Worker 类
def init_worker(self, **kwargs):
# 1. 从配置中解析 worker 类名
worker_class = resolve_obj_by_qualname(parallel_config.worker_cls)
# 2. 可选:动态继承注入 worker_extension_cls(用于 collective RPC)
if worker_extension_cls:
worker_class = type("Worker", (worker_extension_cls, worker_class), {})
# 3. 实例化
self.worker = worker_class(**kwargs) # 默认为 GPUWorker
7.5 GPUWorker.init_device()
def init_device(self):
# Step 1: 设置 CUDA 设备 [L271]
self.device = torch.device(f"cuda:{self.local_rank}")
# Step 2: 初始化分布式环境(NCCL、TP/PP 通信组) [L280]
init_worker_distributed_environment(self.vllm_config)
# Step 3: 设置随机种子 [L292]
set_random_seed(self.model_config.seed)
# Step 4: 记录内存快照 [L295]
self.init_snapshot = MemorySnapshot(device=self.device)
# Step 5: 计算请求的显存总量 [L300]
self.requested_memory = total_memory * gpu_memory_utilization
# Step 6: 初始化 workspace 管理器 [L310]
init_workspace_manager(self.device, num_ubatches)
# Step 7: 创建 GPUModelRunner [L313]
self.model_runner = GPUModelRunner(self.vllm_config, self.device)
7.6 GPUWorker.load_model()
def load_model(self):
with memory_pool_context(): # 为权重加载设置内存池
self.model_runner.load_model()
7.7 GPUModelRunner.load_model()
核心步骤:
1. 创建 EPLB state(若启用 expert parallelism) [L5044]
2. 加载模型权重 [L5052]
model_loader = get_model_loader(self.load_config)
self.model = model_loader.load_model(vllm_config=...)
3. 应用 LoRA adapter(若配置) [L5056]
4. 加载 drafter 模型(推测解码场景) [L5060]
5. 解析 MoE 模型结构(EPLB 场景) [L5095]
6. 可选:torch.compile() 编译模型 [L5162]
7. 可选:CUDAGraph wrapper [L5177]
7.8 update_block_size_for_backend()
Worker 初始化后,调用 attention backend 的 get_preferred_block_size() 来调整 cache_config.block_size。如果 backend 返回的值不同于默认(16),则使用 backend 的偏好值。对于 hybrid 模型(attention + mamba 混合),还会对齐不同层的 block size。
8. KV Cache 分配 —— 最复杂的阶段
EngineCore._initialize_kv_caches()(core.py:235-313)编排了整个 KV cache 的内存规划、profiling 和分配。
8.1 Step 1:收集 KV Cache Spec
kv_cache_specs = self.model_executor.get_kv_cache_specs()
对所有 worker 发起 collective_rpc("get_kv_cache_spec"),每个 worker 遍历其持有的 Attention 层,调用每个层的 get_kv_cache_spec() 返回 KVCacheSpec。结果是一个字典:{layer_name: KVCacheSpec}。
8.2 Step 2:显存 Profiling
available_gpu_memory = self.model_executor.determine_available_memory()
对每个 worker 发起 determine_available_memory()(gpu_worker.py:358-510):
请求的显存 = total_memory × gpu_memory_utilization
profiling:
├─ model_runner.profile_run()
│ ├─ 多模态 encoder 的 dummy 推理(若有)
│ ├─ _dummy_run() 使用 max_num_tokens 运行一次模型
│ └─ _dummy_sampler_run() 或 _dummy_pooler_run()
│
├─ model_runner.profile_cudagraph_memory()(若启用 CUDAGraph)
│
└─ 计算可用 KV cache 显存:
available = requested_memory - non_kv_cache_memory - cudagraph_estimate
8.3 Step 3:计算 KV Cache 配置
kv_cache_configs = get_kv_cache_configs(vllm_config, kv_cache_specs, available_gpu_memory)
get_kv_cache_configs():
├─ 合并所有 worker / PP stage 的 spec
├─ get_kv_cache_groups(): 将 attention 层分组
│ 处理 uniform / uniform-type / DeepseekV4 / hybrid / attention-free
│ 每组对应一个 attention type
├─ _auto_fit_max_model_len(): 若 max_model_len = -1,二分搜索最大 context 长度
├─ 检查显存是否足够
├─ 为每个 worker 生成 KVCacheConfig:
│ ├─ num_blocks(每个 KV cache group 的物理 block 数量)
│ └─ kv_cache_tensors(tensor 形状描述)
└─ 规一化为所有 worker 中最小的 num_blocks
8.4 Step 4:更新 block_size
vllm_config.cache_config.block_size = min(g.kv_cache_spec.block_size for g in groups)
将 cache_config.block_size 从占位值更新为实际确定的值。
8.5 Step 5:分配 KV Cache Tensor 并预热
self.model_executor.initialize_from_config(kv_cache_configs)
每个 worker 的 GPUModelRunner.initialize_kv_cache()(gpu_model_runner.py:7135-7192):
1. 添加 encoder-only 层到 kv cache config
2. 添加 KV sharing 层
3. initialize_attn_backend():
- 为每个 KV cache group 查询 attention layer 的 backend
- 按 (backend, kv_cache_spec) 分组 → AttentionGroup
- 解析 CUDA graph 兼容模式
4. prepare_kernel_block_sizes():
- 查询 backend 支持的 kernel block sizes
- 可能需要将 virtual block 拆分为更小的 kernel block
5. 初始化 metadata builders
6. 重新初始化 InputBatch(若 block size 变化)
7. initialize_kv_cache_tensors():
- 为每层分配 GPU 显存中的 KV cache tensor
- bind_kv_cache() 绑定到 attention layer
然后对所有 worker 调用 compile_or_warm_up_model():
- CUDA graph 捕获
- torch.compile() 编译优化
- 模型预热
8.6 Step 6:确定 Scheduler Block Size
调用 resolve_kv_cache_block_sizes()(kv_cache_utils.py:571-634):
单一 KV cache group:
scheduler_block_size = cache_config.block_size × dcp × pcp
hash_block_size = scheduler_block_size
多个 KV cache group(混合 attention type):
scheduler_block_size = LCM(所有 group 的 block size)
hash_block_size = 用户指定 或 GCD(所有 group 的 block size)
(hash_block_size 必须能整除所有 group 的 block size)
9. Scheduler 初始化
self.scheduler = Scheduler(
vllm_config=vllm_config,
kv_cache_config=kv_cache_config,
structured_output_manager=self.structured_output_manager,
block_size=scheduler_block_size,
hash_block_size=hash_block_size,
)
9.1 Scheduler.init()
Scheduler.__init__():
├─ 存储配置参数: max_model_len, block_size 等
├─ 创建 KVConnector(prefix/offloading 传输) [L121]
├─ 创建 KV event publisher [L137]
├─ 创建 EC connector(early completion 传输) [L141]
├─ 创建请求队列: waiting, skipped_waiting, running [L164]
├─ 计算 encoder cache 大小 [L186]
├─ 设置推测解码参数: num_spec_tokens, lookahead [L211]
└─ 创建 KVCacheManager [L222]:
self.kv_cache_manager = KVCacheManager(
kv_cache_config=...,
max_model_len=..., hash_block_size=...,
enable_caching=...)
9.2 KVCacheManager.init()
class KVCacheManager:
def __init__(self, kv_cache_config, ...):
# 创建 KVCacheCoordinator
self.coordinator = get_kv_cache_coordinator(kv_cache_config, ...)
self.block_pool = self.coordinator.block_pool
KVCacheCoordinator 选择逻辑(kv_cache_coordinator.py:610-658):
| 条件 | Coordinator | 行号 |
|---|---|---|
| Prefix caching 禁用 | KVCacheCoordinatorNoPrefixCache | L276 |
| 1 个 KV cache group | UnitaryKVCacheCoordinator | L324 |
| 多个 KV cache group | HybridKVCacheCoordinator | L392 |
9.3 BlockPool.init()
class BlockPool:
def __init__(self, num_gpu_blocks):
# 1. 创建所有 KVCacheBlock 对象 (num_gpu_blocks 个)
self.blocks = [KVCacheBlock(block_id=i) for i in range(num_gpu_blocks)]
# 2. 创建双向空闲链表(O(1) 分配/释放)
self.free_block_queue = FreeKVCacheBlockQueue(self.blocks)
# 初始状态: 所有 block 都在空闲链表中
# 3. 创建 prefix cache 哈希表
self.cached_block_hash_to_block = BlockHashToBlockMap()
# 4. 创建 null block 占位符(从空闲队列取一个 block 标记为 null)
self.null_block = self.free_block_queue.popleft()
self.null_block.is_null = True
每个 KVCacheBlock(kv_cache_utils.py:116)初始化时 ref_cnt=0,通过双向链表指针串联成空闲队列。
9.4 SingleTypeKVCacheManager 创建
在 KVCacheCoordinator 的 __init__ 中,为每个 KV cache group 创建对应的 SingleTypeKVCacheManager:
UnitaryKVCacheCoordinator:
└─ 1 个 FullAttentionManager (或对应 type 的 manager)
HybridKVCacheCoordinator:
├─ FullAttentionManager (group 0)
├─ SlidingWindowManager (group 1)
└─ ...
基类 single_type_kv_cache_manager.py:31,各子类的实现见下表:
| Manager | 行号 | 对应的 Spec |
|---|---|---|
FullAttentionManager | L481 | FullAttentionSpec, MLAAttentionSpec |
SlidingWindowManager | L542 | SlidingWindowSpec |
ChunkedLocalAttentionManager | L692 | ChunkedLocalAttentionSpec |
MambaManager | L842 | MambaSpec |
CrossAttentionManager | L1122 | CrossAttentionSpec |
SinkFullAttentionManager | L1176 | SinkFullAttentionSpec |
10. 完整调用链汇总
LLM.__init__ [llm.py:194]
│
├─ EngineArgs → VllmConfig [arg_utils.py:1697]
│ ├─ DeviceConfig [L1709]
│ ├─ ModelConfig (含 max_model_len, dtype, tokenizer) [L1730]
│ ├─ CacheConfig (block_size 占位值) [L1754]
│ ├─ ParallelConfig (TP/PP/DP) [L1957]
│ └─ SchedulerConfig [L2028]
│
├─ LLMEngine.from_engine_args() [llm_engine.py:152]
│ └─ EngineCoreClient.make_client() [core_client.py:80]
│ └─ InprocClient / SyncMPClient / AsyncMPClient
│
├─ EngineCore.__init__ [core.py:97]
│ │
│ ├─ [L121] Executor._init_executor()
│ │ ├─ WorkerWrapperBase.init_worker() [worker_base.py:230]
│ │ │ └─ 实例化 GPUWorker [worker_base.py:313]
│ │ ├─ GPUWorker.init_device() [gpu_worker.py:236]
│ │ │ ├─ CUDA 设备设置 [L271]
│ │ │ ├─ NCCL 分布式环境初始化 [L280]
│ │ │ ├─ MemorySnapshot [L295]
│ │ │ └─ 创建 GPUModelRunner [L313]
│ │ ├─ GPUWorker.load_model() [gpu_worker.py:335]
│ │ │ └─ GPUModelRunner.load_model() [gpu_model_runner.py:5032]
│ │ │ └─ model_loader.load_model() → 加载权重
│ │ └─ update_block_size_for_backend() [interface.py:470]
│ │
│ ├─ [L131] _initialize_kv_caches() [core.py:235]
│ │ ├─ get_kv_cache_specs() [gpu_model_runner.py:7291]
│ │ ├─ determine_available_memory() [gpu_worker.py:358]
│ │ │ └─ profile_run() → dummy forward pass
│ │ ├─ get_kv_cache_configs() [kv_cache_utils.py:1945]
│ │ │ ├─ get_kv_cache_groups() → 层分组
│ │ │ └─ 计算 num_blocks(根据可用显存)
│ │ ├─ initialize_from_config() [gpu_model_runner.py:7135]
│ │ │ ├─ initialize_attn_backend()
│ │ │ ├─ prepare_kernel_block_sizes()
│ │ │ └─ initialize_kv_cache_tensors() → 分配 GPU 显存
│ │ └─ compile_or_warm_up_model()
│ │ └─ CUDA graph 捕获 / torch.compile
│ │
│ ├─ [L144] resolve_kv_cache_block_sizes() [kv_cache_utils.py:571]
│ │ → (scheduler_block_size, hash_block_size)
│ │
│ └─ [L148] Scheduler.__init__() [scheduler.py:64]
│ ├─ 请求队列 (waiting, running) [L164]
│ ├─ KVCacheManager [L222] [kv_cache_manager.py:110]
│ │ └─ KVCacheCoordinator [kv_cache_coordinator.py]
│ │ └─ BlockPool [block_pool.py:130]
│ │ ├─ KVCacheBlock × N (空闲链表)
│ │ ├─ BlockHashToBlockMap (prefix cache)
│ │ └─ per-group SingleTypeKVCacheManager
│ └─ KVConnector (prefix 传输) [L121]
│
└─ 就绪 → run_busy_loop()
11. Block Size 的确定过程
block_size 不是一次性决定的,而是在初始化过程中逐步确定:
阶段 1: EngineArgs.create_engine_config()
└─ cache_config.block_size = 16 (默认) 或用户指定值
(此时为占位值)
阶段 2: Worker init_device()
└─ update_block_size_for_backend() [interface.py:470]
└─ 查询 attention backend 的 get_preferred_block_size()
若不同于默认值,则更新 cache_config.block_size
阶段 3: _initialize_kv_caches()
└─ cache_config.block_size = min(group.block_size for groups)
(从 KV cache group 的实际 spec 中获取)
阶段 4: resolve_kv_cache_block_sizes() [kv_cache_utils.py:571]
└─ scheduler_block_size = block_size × dcp × pcp (单 group)
或 LCM(所有 group 的 block size) (多 group)
12. 关键文件索引
| 组件 | 文件 | 核心行号 |
|---|---|---|
| LLM 入口 | llm.py | 194-402 |
| API 服务器入口 | api_server.py | 108-136 |
| 配置解析 | arg_utils.py | 412-716, 1697-2252 |
| LLMEngine | llm_engine.py | 50-177 |
| AsyncLLM | async_llm.py | 73-229 |
| EngineCoreClient | core_client.py | 80-103, 274-365, 473-611 |
| EngineCore | core.py | 97-232, 235-313 |
| Executor 基类 | abstract.py | 48-137 |
| UniProcExecutor | uniproc_executor.py | 46-66 |
| MultiprocExecutor | multiproc_executor.py | 109-246, 579-637 |
| WorkerWrapperBase | worker_base.py | 230-313 |
| GPUWorker | gpu_worker.py | 236-349, 358-569 |
| GPUModelRunner | gpu_model_runner.py | 418-897, 5032-5181, 7135-7192 |
| KVCacheConfig | kv_cache_utils.py | 1945-2077, 571-634 |
| BlockPool | block_pool.py | 130-182 |
| KVCacheCoordinator | kv_cache_coordinator.py | 28-658 |
| KVCacheManager | kv_cache_manager.py | 110-180 |
| SingleTypeKVCacheManager | single_type_kv_cache_manager.py | 31-149 |
| Scheduler | scheduler.py | 64-277 |
| Platform block_size hook | interface.py | 470-506 |