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 引擎

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

2.1 LLMEngine

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

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

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

2.2 AsyncLLMEngine

AsyncLLMEngineLLMEngine 的异步包装,基于 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