模型在 EngineCore 中的加载流程

📊 完整流程图

EngineCore.__init__() (84行)
    ↓
self.model_executor = executor_class(vllm_config)
    ↓
MultiprocExecutor._init_executor() (53行)
    ↓
WorkerProc.make_worker_process() (440行)
    ├─ 创建 worker 进程
    └─ WorkerProc.__init__() (381行)
        ↓
        WorkerWrapperBase.init_worker()
            ↓
            Worker.__init__() (gpu_worker.py:47行)
                ├─ 初始化分布式环境
                ├─ init_device() (156行)
                │   └─ init_worker_distributed_environment()
                └─ 创建 GPUModelRunner (201行)
                    ↓
                    self.model_runner = GPUModelRunner(vllm_config, device)
        ↓
        self.worker.init_device() (430行)
        ↓
        self.worker.load_model() ⭐ (437行)
            ↓
            Worker.load_model() (gpu_worker.py:210-213行)
                ↓
                self.model_runner.load_model()
                    ↓
                    GPUModelRunner.load_model() 
                        └─ 调用 model_loader 加载模型权重

1️⃣ EngineCore 初始化 (core.py:83-84行)

# core.py:83-84
# Setup Model.
self.model_executor = executor_class(vllm_config)

说明

  • executor_class 通常是 MultiprocExecutorRayDistributedExecutor
  • Executor 负责管理 Worker 进程

2️⃣ Executor 创建 Worker 进程 (multiproc_executor.py:93-100行)

# multiproc_executor.py:93-100
for rank in range(self.world_size):
    unready_workers.append(
        WorkerProc.make_worker_process(
            vllm_config=self.vllm_config,
            local_rank=rank,
            rank=rank,
            distributed_init_method=distributed_init_method,
            input_shm_handle=scheduler_output_handle,
            shared_worker_lock=shared_worker_lock,
        ))

说明

  • 为每个 tensor parallel rank 创建一个 worker 进程
  • 例如 TP=4,会创建 4 个 worker 进程

3️⃣ Worker 进程初始化 (multiproc_executor.py:381-437行)

# multiproc_executor.py:381-437
class WorkerProc:
    def __init__(self, ...):
        # ① 创建 WorkerWrapperBase
        wrapper = WorkerWrapperBase(vllm_config=vllm_config, rpc_rank=rank)
        
        # ② 初始化 Worker(GPU Worker)
        wrapper.init_worker(all_kwargs)
        self.worker = wrapper
        
        # ③ 初始化设备(CUDA、分布式环境)
        self.worker.init_device()
        
        # ④ 🔥 加载模型!
        self.worker.load_model()

4️⃣ Worker.load_model() (gpu_worker.py:210-213行)

# gpu_worker.py:210-213
def load_model(self) -> None:
    eep_scale_up = os.environ.get("VLLM_ELASTIC_EP_SCALE_UP_LAUNCH") == "1"
    with self._maybe_get_memory_pool_context(tag="weights"):
        # 调用 GPUModelRunner 的 load_model
        self.model_runner.load_model(eep_scale_up=eep_scale_up)

说明

  • _maybe_get_memory_pool_context 用于内存池管理(sleep mode)
  • 实际加载由 GPUModelRunner.load_model() 完成

5️⃣ GPUModelRunner.load_model() - 真正的模型加载

虽然文件太大我没完整看到,但根据代码注释和结构,GPUModelRunner.load_model() 会调用:

# 伪代码展示加载流程
def load_model(self, eep_scale_up=False):
    # ① 使用 model_loader 加载模型
    from vllm.model_executor.model_loader import get_model_loader
    
    model_loader = get_model_loader(
        load_config=self.load_config,
        model_config=self.model_config,
        ...
    )
    
    # ② 加载模型权重
    self.model = model_loader.load_model(
        model_config=self.model_config,
        device_config=self.device_config,
        ...
    )
    
    # ③ 移动模型到设备(GPU)
    self.model = self.model.to(self.device, dtype=self.dtype)
    
    # ④ 初始化 LoRA、量化等
    if self.lora_config:
        self._init_lora()
    
    # ⑤ 计算模型内存占用
    self.model_memory_usage = calculate_memory_usage(self.model)

关键组件说明

组件 职责 文件位置
EngineCore 推理引擎核心,协调调度和执行 v1/engine/core.py
Executor 管理多个 Worker 进程 v1/executor/multiproc_executor.py
WorkerProc Worker 进程包装器 v1/executor/multiproc_executor.py
Worker 实际执行推理的工作进程 v1/worker/gpu_worker.py
GPUModelRunner 管理模型、输入准备、推理执行 v1/worker/gpu_model_runner.py
ModelLoader 从磁盘加载模型权重 model_executor/model_loader/

模型加载的关键步骤

初始化分布式环境 (gpu_worker.py:169行)

init_worker_distributed_environment(
    self.vllm_config, self.rank,
    self.distributed_init_method,
    self.local_rank,
    current_platform.dist_backend
)
  • 初始化 NCCL/Gloo 通信
  • 设置 Tensor Parallel / Pipeline Parallel 组

加载模型架构和权重

# 从 HuggingFace / 本地路径加载
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    torch_dtype=torch.bfloat16,
    ...
)

应用优化

  • 量化:FP8、INT8、AWQ、GPTQ 等
  • LoRA:加载 LoRA 适配器
  • 权重分片:Tensor Parallel 切分权重

内存分析

# gpu_worker.py:222-302
def determine_available_memory(self) -> int:
    # 执行 dummy forward 来 profile 内存使用
    self.model_runner.profile_run()
    # 计算可用于 KV Cache 的内存
    return available_kv_cache_memory_bytes

总结

模型加载发生在哪里?

后台 Worker 进程中,不是在 EngineCore 主进程
每个 Worker 进程加载一份模型(TP 情况下是模型的一部分)
加载时机:Worker 进程启动时,在 WorkerProc.__init__() 的 437 行调用

为什么这样设计?

  1. 多进程隔离:模型加载在独立进程,避免 GIL 锁
  2. 并行加载:多个 worker 可以并行加载模型分片
  3. 内存隔离:每个进程有独立的 CUDA 上下文
  4. 容错性:worker 崩溃不影响 EngineCore
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐