Skip to content

vLLM 架构设计 ​

vLLM 提供了多种与系统交互的入口点,并在内部通过统一的配置对象 VllmConfig 连接 LLM 引擎、Worker、模型运行器与模型本身。本文将概述这些核心组件及其设计考量。

1. 入口点(Entrypoints) ​

1.1 LLM 类 ​

LLM 类是离线推理的主要 Python 接口,无需启动推理服务器即可与模型交互。

python
from vllm import LLM, SamplingParams

prompts = [
    "Hello, my name is",
    "The capital of France is",
    "The largest ocean is",
]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

llm = LLM(model="facebook/opt-125m")
outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    prompt = output.prompt
    generated_text = output.outputs[0].text
    print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

代码位置:vllm/entrypoints/llm.py

更多 API 细节可参考 Offline Inference API 文档。

1.2 OpenAI 兼容 API 服务端 ​

vLLM 的第二个主要入口是 OpenAI 兼容的 API 服务器:

bash
vllm serve <model>

CLI 代码位置:vllm/entrypoints/cli/main.py

也可直接使用 API 服务端入口:

bash

代码位置:vllm/entrypoints/openai/api_server.py

2. LLM 引擎 ​

LLMEngine 与 AsyncLLMEngine 是 vLLM 系统的核心组件,分别负责同步与异步请求处理。

2.1 LLMEngine ​

LLMEngine 负责接收客户端请求并生成模型输出,涵盖:

  • 输入处理:使用分词器对输入文本进行分词。
  • 调度机制:决定每一步应处理哪些请求。
  • 模型执行:运行语言模型,可跨多个 GPU 分布式执行。
  • 输出处理:将 Token ID 解码为可读文本。

代码位置:vllm/engine/llm_engine.py

2.2 AsyncLLMEngine ​

AsyncLLMEngine 是 LLMEngine 的异步包装,基于 asyncio 构建后台循环,持续处理接入请求,支持并发请求与流式输出。OpenAI 兼容 API 服务端即基于此实现。

代码位置:vllm/engine/async_llm_engine.py

3. Worker 与模型运行器 ​

3.1 Worker ​

Worker 是实际运行模型推理的进程。vLLM 通常遵循“每个进程对应一个加速设备”的模式。例如,张量并行度为 2、流水线并行度为 2 时,总共有 4 个 Worker。Worker 通过 rank(全局)与 local_rank(本地)标识,分别用于全局调度与设备分配。

3.2 Model Runner ​

每个 Worker 拥有一个模型运行器对象,负责加载并运行模型。大部分模型执行逻辑位于此处,包括输入张量准备与 CUDA Graph 捕获。

3.3 Model ​

每个模型运行器包含一个模型对象,即实际的 torch.nn.Module 实例。不同配置会影响最终模型类的构建。

4. 类层次结构设计考量 ​

vLLM 的类层次结构围绕 VllmConfig 这一全局配置对象展开。

4.1 可扩展性 ​

所有类都接受一个 VllmConfig 配置对象。当需要新增仅涉及模型运行器的功能时,只需在 VllmConfig 中添加配置项,模型运行器即可直接访问,而无需修改引擎、Worker 或模型类的构造函数。

4.2 一致性 ​

模型运行器需要一个统一的接口来创建并初始化模型。vLLM 支持超过 50 种开源模型,统一的构造函数签名避免了复杂的条件判断。例如,视觉-语言模型可由视觉模型与语言模型分别创建后组合而成。

构造函数签名已统一为关键字参数形式:

python
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):

若使用旧模型类,可通过适配代码兼容:

python
from vllm.config import VllmConfig

class MyOldModel(nn.Module):
    def __init__(self, config, cache_config=None, quant_config=None, lora_config=None, prefix=""):
        ...

class MyNewModel(MyOldModel):
    def __init__(self, *, vllm_config: VllmConfig, prefix=""):
        config = vllm_config.model_config.hf_config
        cache_config = vllm_config.cache_config
        quant_config = vllm_config.quant_config
        lora_config = vllm_config.lora_config
        super().__init__(config, cache_config, quant_config, lora_config, prefix)

import vllm
if vllm.__version__ >= "0.6.4":
    MyModel = MyNewModel
else:
    MyModel = MyOldModel

4.3 初始化时的切片与量化 ​

张量并行与量化等特性需要在模型初始化阶段直接处理权重,以降低内存开销。例如,405B 参数模型理想状态下每块 H100 仅加载约 50 GB 权重;若初始化后再切片,则每块 GPU 都需加载完整模型,造成极大浪费。

构造函数中的 prefix 参数支持非统一量化配置。顶层模型 prefix="",子模型如视觉模块、语言模块可分别为 "vision"、"language"。

5. 测试挑战 ​

由于各组件依赖完整 VllmConfig,单元测试难以完全独立进行。vLLM 通过默认初始化函数生成字段全为 None 的配置对象,便于只设置关注字段进行测试。此外,vLLM 的许多测试为端到端测试,覆盖整个系统流程,因此该限制影响较小。

总结 ​

VllmConfig 是 vLLM 引擎层级的全局状态对象,在系统各个组件间共享。通过统一的配置机制、构造函数接口以及初始化策略,vLLM 实现了良好的可扩展性、一致性与可组合性,为大规模语言模型推理提供了坚实基础。

参考 ​

Maintained by Robin