【vLLM】源码解读:vllm中engine core 如何加载模型的
vLLM模型加载流程:EngineCore初始化时创建Executor,后者为每个GPU rank创建Worker进程。Worker通过GPUModelRunner加载模型,包括初始化分布式环境、加载权重、应用量化/LoRA优化,并分析内存使用。整个过程在独立Worker进程中完成,主进程仅负责协调。
·
模型在 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通常是MultiprocExecutor或RayDistributedExecutor- 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 行调用
为什么这样设计?
- 多进程隔离:模型加载在独立进程,避免 GIL 锁
- 并行加载:多个 worker 可以并行加载模型分片
- 内存隔离:每个进程有独立的 CUDA 上下文
- 容错性:worker 崩溃不影响 EngineCore
更多推荐
所有评论(0)