编程 走进 vLLM:一个请求从 API 到 GPU 执行的完整路径

2026-09-07 21:11:16

走进 vLLM:一个请求从 API 到 GPU 执行的完整路径

本文跟随一个离线推理请求穿过 vLLM V1(基于 vLLM 0.22.0,2026-09-03 核对):从 LLM.generate() 与进程间通信,到调度、输入展平、GPU 模型执行、paged KV-cache 访问、采样与资源回收。回答一个具体问题:调用 llm.generate() 之后、完整结果回到调用者之前,发生了什么?

需要 Transformer 推理基础(prefill、decode、KV cache、自回归)。文章是源码走查而非 API 教程;vLLM 演化快,文件名与调用边界会移动,但持续批处理(continuous batching)、token 预算、paged KV 分配、调度与执行分离这些长期思想才是真正的主体。

系统地图:进程边界与引擎循环

vLLM V1 中 EngineCore 在子进程里持续运行,请求在步骤之间进出活跃集合——调用者不逐 token 驱动 GPU 执行。

调用者: LLM.generate() → LLMEngine.add_request() → EngineCoreClient
        → IPC 输入队列 → scheduler.add_request()

EngineCore 子进程 busy loop:
  step() 重复:
    1. scheduler.schedule()       —— 花 token / KV-cache 预算 → SchedulerOutput
    2. model_executor.execute_model() —— GPU 前向与采样 → token
    3. scheduler.update_from_output()  —— 更新状态、释放完成的 KV 块
  → IPC 输出队列 → LLMEngine.step()/get_output() → 去token化组装 → 返回

连续循环实现持续批处理:第 3 步移除完成请求并释放资源,下一次 schedule() 准入等待中的工作——GPU 不必等一个固定批次全部结束再进新请求。

三个边界贯穿全文:主进程消费结果,EngineCore 生产结果——调用 LLMEngine.step() 并不让 GPU 走一步,它通过 get_output() 取已产出的输出再组装;EngineCore.step() 三阶段:调度、执行、状态更新;持续批处理让请求集每步都在变化。

LLM 是薄门面,generate() 是循环

LLM 在 entrypoints/llm.py 里把配置装进 EngineArgs,用工厂方法 LLMEngine.from_engine_args() 建引擎——LLM 是门面,LLMEngine 拥有下面的请求处理路径。generate() 剥离细节后是个循环:

for prompt in prompts:
    self.llm_engine.add_request(prompt, ...)     # 注册请求
outputs = []
while self.llm_engine.has_unfinished_requests():  # 有未完成请求就推进
    step_outputs = self.llm_engine.step()
    for out in step_outputs:
        if out.finished:
            outputs.append(out)
return outputs

与手写 for _ in range(max_tokens) 逐序列循环不同,它跟踪的是一批请求的集合,靠 EngineCore 的忙碌循环推进。

调度:怎么花 token 与 KV-cache 预算

scheduler.schedule() 按 token 预算与 KV-cache 预算准入请求:每个请求要算 prefill 需要的 token 与 KV 块,预算内放行、超预算等待下一轮。这解释了为什么 vLLM 的批不是"按数量"而是"按预算"——变长请求让"一批 N 条"没有意义,预算才是物理约束。

输入展平与执行

变长请求被展平成单个扁平 token 张量,配合 metadata 描述每条请求的边界;执行阶段 metadata 一路进 attention 内核。PagedAttention 的读写路径都按块寻址 KV-cache——类似虚拟内存的页表,在 kernel 边界可见。

实践要点

  • 理解 vLLM 性能优先看三件事:预算型调度(为什么并发上限是 token/KV 预算而非请求数)、调度与执行分离(EngineCore 异步推进,调用侧只是消费)、paged KV 分配(谁在何时分配/释放块,决定长上下文能否跑);
  • 排查线上 LLM 服务延迟时,区分"引擎忙"与"调用侧消费慢"——两者隔着进程边界;
  • vLLM 版本迭代快,读源码按"0.22.0 标签"对齐,思想层(持续批处理/预算/分页)比具体函数名更值得记。

来源:Inside vLLM: Following One Request from the API to GPU Execution - DEV Community

复制全文 生成海报 AI vLLM 推理 性能

推荐文章

程序员茄子在线接单