从0开始的vLLM学习

从0开始的vLLM学习


文章结构(五大部分)

1. LLM Engine & Engine Core(核心引擎)

Engine 构造函数的四大组件:

  • vLLM Config:所有配置(模型、cache、并行度等)
  • Processor:原始输入 → EngineCoreRequest(tokenize + 校验)
  • Engine Core Client:单进程下 ≈ EngineCore,分布式下是 DPLBAsyncMPClient
  • Output Processor:原始输出 → 用户看到的 RequestOutput

Engine Core 内部:

  • Model ExecutorUniProcExecutorMultiProcExecutor
  • Scheduler(FCFS or Priority,含 waiting/running 两个队列)
  • KV Cache Manager(PagedAttention 的核心,管理 free_block_queue

Worker 初始化三步走init_deviceload_modelinit_kv_cache(包含 profile run 计算可用 blocks,以及 CUDA Graph 预热)

每个 Step 三阶段ScheduleForward PassPostprocess

Scheduler 的调度逻辑

  • 优先处理 running 队列(decode 请求)
  • 再处理 waiting 队列(prefill 请求)
  • allocate_slots 负责从 free_block_queue 里实际分配 KV cache block

2. Advanced Features(高级特性)

[table]


3. 从 UniProcExecutor 到 MultiProcExecutor(多 GPU 扩展)

  • MultiProcExecutor 用共享内存 rpc_broadcast_mq 广播任务给各 worker 进程
  • 每个 worker 独立执行 init → load → kv_cache 三步,通过 TP/PP 分片协作
  • 对上层 Engine 完全透明

4. 分布式 Serving 层

完整的请求生命周期:

curl /v1/completions
  → FastAPI (Uvicorn)
  → OpenAIServingCompletion
  → AsyncLLM.generate
  → DPLBAsyncMPClient(负载均衡:score = waiting*4 + running)
  → DPEngineCoreProc(input thread → main thread → output thread)
  → MultiProcExecutor → Worker(TP=4)
  → 结果原路返回

DP Coordinator 负责各引擎的负载均衡状态同步,支持弹性扩缩容(Ray 后端)。


5. Benchmarks & 性能指标

关键指标:TTFTITLTPOTE2E LatencyThroughputGoodput

Roofline 模型

  • Batch size 小 → memory-bandwidth bound → ITL 低但吞吐差
  • Batch size 大 → compute bound → 吞吐高但 ITL 上升
  • 两者天然对立,B_sat 是拐点

LLM Engine & Engine Core

Engine

  1. vLLM config:所有配置(模型/cache/并行度等)

模型:

chche:
并行度

  1. Processor(类似于点单员,把用户的需求转化成厨房能懂的格式):

原始输入->EngineCoreRequest(tokenize+校验)

tokenize: 把一段文本拆分成一个个“token” (最小的处理单元)详解

校验:审核格式,审核token长度(是否会超过 max_model_len ) 避免 KV cache 越界或 OOM ( Out Of Memory(内存不足) ),校验tokenizer和模型是否匹配,参数合法性校验(temperature,max_tokens),多模态对齐校验(prompt里的image 的token,( prompt: "Describe this <image>" )是否和实际传入的的图片数量匹配)

  1. Engine Core Client:单进程:EngineCore,分布式是 DPLBAsyncMPClient
  2. Output Processor(服务员,把东西处理好端给顾客): 原始输出 → 用户看到的 RequestOutput

Engine Core

  1. Model Executor(干活的)
  2. GPU Worker(干活的)
  3. Scheduler(调度员,决定做那些菜)
  4. KV Cache Manager(PagedAttention的核心,管理free_block_queue)

KV Cache:可以想象为提前的备菜

实际上的作用,在初始化的时候提前把一大块显存切分成小池子,传统方法预留一大块显存会很浪费空间,但使用KV Cache的时候可以按需分配显存,当需要的时候再把显存分给他,就可以在固定的显存范围内容纳更多的请求

Worker 初始化三步走init_deviceload_modelinit_kv_cache(包含 profile run 计算可用 blocks,以及 CUDA Graph 预热)

init_device:绑定 CUDA 设备,检查 VRAM 够不够,初始化 model_runner(存放前向传播所需的 buffer)

load_model:加载模型权重,调用 model.eval(),可选跑 torch.compile()

init_kv_cache:这步最有意思——先跑一次"假的"前向传播来量一下 GPU 内存占用,然后算出剩余 VRAM 能放多少个 KV cache block,分配这些 block,最后预热 CUDA Graph(把常用 batch size 的计算图提前录制好,推理时直接 replay,省掉 kernel launch 开销)

每条 prompt 会被包装成一个 Request 对象,状态设为 WAITING,扔进 Scheduler 的 waiting 队列

反复调用 step()

只要还有请求没处理完,引擎就一直循环,每次 step 分三阶段:

每个 Step 三阶段ScheduleForward PassPostprocess

Scheduler:最关键的部分

两种请求类型

Prefill(预填充):第一次处理一个请求,需要把完整的 prompt 全部过一遍模型,计算量大,是 compute-bound。结束时采样出第一个输出 token。

过一遍模型指的是算一遍qkv,

Decode(解码):后续每一步只输入最新的一个 token,因为之前的 token 的 KV 已经缓存了。每步计算量很小,但要把所有权重和 KV cache 从显存里读出来,是 memory-bandwidth-bound

Scheduler 的调度逻辑

  • 优先处理 running 队列(decode 请求)
  • 再处理 waiting 队列(prefill 请求)
  • allocate_slots 负责从 free_block_queue 里实际分配 KV cache block