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 类

llm.py:194-402

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 服务器

api_server.py:108-136

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

arg_utils.py:412-716

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_tokensmax_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

llm_engine.py:50-177

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 工厂

core_client.py:80-103

根据 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)的核心流程:

  1. 创建 ZMQ context 和 input/output socket
  2. 调用 launch_core_engines() 启动 EngineCoreProc 后台进程
  3. 等待所有 engine 发回 READY 信号
  4. 启动 engine core monitor 线程

6. EngineCore 初始化 —— 核心入口

core.py:97-232

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 选择

abstract.py:48-92

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(单进程模式)

uniproc_executor.py:46-66

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(多进程模式)

multiproc_executor.py:109-246

为每个 GPU 创建一个 WorkerProc 后台进程(multiproc_executor.py:579-637),每个进程内执行相同的 init_worker → init_device → load_model 序列。与 UniProc 的区别仅在于额外增加了:

  • Worker 之间通过消息队列广播 SchedulerOutput / ModelRunnerOutput
  • Worker monitor 线程检测健康状态

7.4 WorkerWrapperBase:解析 Worker 类

worker_base.py:230-313

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()

gpu_worker.py:236-331

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()

gpu_worker.py:335-349

def load_model(self):
    with memory_pool_context():           # 为权重加载设置内存池
        self.model_runner.load_model()

7.7 GPUModelRunner.load_model()

gpu_model_runner.py:5032-5181

核心步骤:

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()

interface.py:470-506

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}

入口:gpu_model_runner.py:7291

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)

kv_cache_utils.py:1945-2077

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

core.py:144-146

调用 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 初始化

core.py:148-156

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.py:64-277

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()

kv_cache_manager.py:110-180

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()

block_pool.py:130-182

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

每个 KVCacheBlockkv_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

本站访问量

This site uses Just the Docs, a documentation theme for Jekyll.