Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions docs/zh/docs/blogs/2025/inside-vllm.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
大语言模型引擎是 vLLM 的基础构建模块。仅凭其自身,它已经能够实现高吞吐量推理——但仅限于离线场景。
你还不能通过网络将其作为服务提供给客户。

我们将使用以下离线推理示例作为运行示例(以下代码改编自 [basic.py](https://github.com/vllm-project/vllm/blob/main/examples/offline_inference/basic/basic.py))。
本文将使用以下离线推理示例作为运行示例(以下代码改编自 [basic.py](https://github.com/vllm-project/vllm/blob/main/examples/offline_inference/basic/basic.py))。

```python
from vllm import LLM, SamplingParams
Expand Down Expand Up @@ -69,7 +69,7 @@ if __name__ == "__main__":
- 单 GPU(没有数据/模型/流水线/专家并行;DP/TP/PP/EP = 1)
- 使用[标准 Transformer](https://arxiv.org/abs/1706.03762)(支持 Jamba 等混合模型需要更复杂的混合 KV-cache 内存分配器)

从这里开始,我们将逐步构建一个在线、异步、多 GPU、多节点的推理系统——但仍然部署标准的 Transformer。
从这里开始,本文将逐步构建一个在线、异步、多 GPU、多节点的推理系统——但仍然部署标准的 Transformer。

在此示例中,我们做两件事:

Expand All @@ -93,7 +93,7 @@ if __name__ == "__main__":

引擎核心本身由几个子组件组成:

- 模型执行器(驱动模型的前向计算,目前我们使用 `UniProcExecutor`,它在单 GPU 上有一个 `Worker` 进程)。我们将逐步构建到支持多 GPU 的 `MultiProcExecutor`
- 模型执行器(驱动模型的前向计算,目前我们使用 `UniProcExecutor`,它在单 GPU 上有一个 `Worker` 进程)。本文将逐步构建到支持多 GPU 的 `MultiProcExecutor`
- 结构化输出管理器(用于引导解码——稍后讲解)
- 调度器(决定哪些请求进入下一步引擎计算),调度器进一步包含:

Expand Down Expand Up @@ -139,15 +139,15 @@ KV-cache 管理器维护一个 `free_block_queue`。这是所有可用 KV-cache
- 执行一次虚拟/分析前向计算并获取 GPU 内存快照,以计算可用显存中能容纳多少 KV-cache 块
- 分配、调整形状并绑定 KV-cache 张量到注意力层
- 准备注意力元数据(例如将后端设置为 FlashAttention),以供前向计算时内核使用
- 除非提供 `--enforce-eager`,否则对每个预热批次大小执行一次虚拟运行并捕获 CUDA 图。CUDA 图将整个 GPU 工作序列记录为 DAG。在后续前向计算中,我们直接启动/重放预先构建的图,从而减少内核启动开销并改善延迟
- 除非提供 `--enforce-eager`,否则对每个预热批次大小执行一次虚拟运行并捕获 CUDA 图。CUDA 图将整个 GPU 工作序列记录为 DAG。在后续前向计算中,我们直接启动/重放预先构建的图,从而减少内核启动开销并改善延迟

这里我抽象掉了许多底层细节——但这些是核心部分,因为在接下来的章节中我会反复引用它们。

现在我们已经初始化了引擎,让我们继续看 `generate` 函数。
现在我们已经初始化了引擎,让我们继续看看 `generate` 函数。

### `generate` 函数

第一步是验证并将请求送入引擎。对于每个提示词,我们
第一步是验证并将请求送入引擎。对于每个提示词:

1. 创建唯一请求 ID 并记录到达时间
2. 调用输入预处理器,将提示词分词并返回一个字典,包含 `prompt`、`prompt_token_ids` 和 `type`(text、tokens、embeds 等)
Expand Down Expand Up @@ -258,11 +258,11 @@ V1 调度器可以在同一步中混合处理两类请求,这得益于更智

接下来,我们将深入探讨:

1. 分块预填充
2. 前缀缓存
3. 引导解码(通过语法约束的有限状态机)
4. 投机解码
5. P/D 分离(预填充/解码)
1. [分块预填充](#_6)
2. [前缀缓存](#_7)
3. [引导解码](#_8)
4. [投机解码](#_9)
5. [P/D 分离](#pd)

### 分块预填充

Expand Down Expand Up @@ -914,7 +914,7 @@ vLLM 提供了一个 CLI 命令 `vllm bench {serve,latency,throughput}`,它封

- **latency(延迟)** — 使用较短的输入(默认 32 个 Token),生成 128 个输出 Token,使用小批量(默认 8)。脚本会执行多次迭代,并报告批量的端到端延迟。
- **throughput(吞吐量)** — 同时提交固定集合的 prompts(默认 1000 个 ShareGPT 样本,即 `QPS=Inf` 模式),报告整个运行期间的输入/输出/总 Token 数和每秒请求数。
- **serve(服务模拟)** — 启动一个 vLLM 服务,并模拟真实工作负载。请求的到达间隔时间遵循 Poisson 分布(或更通用的 Gamma 分布)。脚本在时间窗口内发送请求,测量前文提到的所有指标,并可选择通过信号量限制服务器最大并发数(例如限制为 64 个并发请求)。
- **serve(部署)** — 启动一个 vLLM 服务,并模拟真实工作负载。请求的到达间隔时间遵循 Poisson 分布(或更通用的 Gamma 分布)。脚本在时间窗口内发送请求,测量前文提到的所有指标,并可选择通过信号量限制服务器最大并发数(例如限制为 64 个并发请求)。

下面是运行延迟测试脚本的示例:

Expand All @@ -934,7 +934,7 @@ vllm bench latency

## 尾声

我们从基础引擎核心(`UniprocExecutor`)开始,加入了如投机解码和前缀缓存等高级特性,接着扩展到 `MultiProcExecutor`(TP/PP > 1),最终实现水平扩展,将所有组件封装到异步引擎和分布式服务栈中——最后展示了如何衡量系统性能。
本文从基础引擎核心(`UniprocExecutor`)开始,加入了如投机解码和前缀缓存等高级特性,接着扩展到 `MultiProcExecutor`(TP/PP > 1),最终实现水平扩展,将所有组件封装到异步引擎和分布式服务栈中——最后展示了如何衡量系统性能。

vLLM 还包含一些我未详细展开的专门处理,例如:

Expand Down
14 changes: 7 additions & 7 deletions docs/zh/docs/en/blogs/2025/inside-vllm.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ Later posts will dive into specific subsystems.

This post is structured into five parts:

1. [LLM engine & engine core](https://www.aleksagordic.com/blog/vllm#cpt1): fundamentals of vLLM (scheduling, paged attention, continuous batching, etc.)
2. [Advanced features](https://www.aleksagordic.com/blog/vllm#cpt2): chunked prefill, prefix caching, guided & speculative decoding, disaggregated P/D
3. [Scaling up](https://www.aleksagordic.com/blog/vllm#cpt3): from single-GPU to multi-GPU execution
4. [Serving layer](https://www.aleksagordic.com/blog/vllm#cpt4): distributed / concurrent web scaffolding
5. [Benchmarks and auto-tuning](https://www.aleksagordic.com/blog/vllm#cpt5): measuring latency and throughput
1. [LLM engine & engine core](#llm-engine--engine-core): fundamentals of vLLM (scheduling, paged attention, continuous batching, etc.)
2. [Advanced features](#advanced-features--extending-the-core-engine-logic): chunked prefill, prefix caching, guided & speculative decoding, disaggregated P/D
3. [Scaling up](#from-uniprocexecutor-to-multiprocexecutor): from single-GPU to multi-GPU execution
4. [Serving layer](#distributed-system-serving-vllm): distributed / concurrent web scaffolding
5. [Benchmarks and auto-tuning](#benchmarks-and-auto-tuning---latency-vs-throughput): measuring latency and throughput

!!! note

Expand Down Expand Up @@ -188,7 +188,7 @@ There are two main types of workloads an inference engine handles:

!!! tip

In the [benchmarking section](https://www.aleksagordic.com/blog/vllm#cpt5) we'll analyze the so-called roofline model of GPU perf. That will go into more detail behind prefill/decode perf profiles.
In the [benchmarking section](#benchmarks-and-auto-tuning---latency-vs-throughput) we'll analyze the so-called roofline model of GPU perf. That will go into more detail behind prefill/decode perf profiles.

The V1 scheduler can mix both types of requests in the same step, thanks to smarter design choices. In contrast, the V0 engine could only process either prefill or decode at once.

Expand Down Expand Up @@ -518,7 +518,7 @@ The best way to internalize this is to fire up your debugger and step through th

I've already previously hinted at the motivation behind disaggregated P/D (prefill/decode).

Prefill and decode have very different performance profiles (compute-bound vs. memory-bandwidth-bound), so separating their execution is a sensible design. It gives tighter control over latency — both `TFTT` (time-to-first-token) and `ITL` (inter-token latency) — more on this in the [benchmarking](https://www.aleksagordic.com/blog/vllm#cpt5) section.
Prefill and decode have very different performance profiles (compute-bound vs. memory-bandwidth-bound), so separating their execution is a sensible design. It gives tighter control over latency — both `TFTT` (time-to-first-token) and `ITL` (inter-token latency) — more on this in the [benchmarking](#benchmarks-and-auto-tuning---latency-vs-throughput) section.

In practice, we run `N` vLLM prefill instances and `M` vLLM decode instances, autoscaling them based on the live request mix. Prefill workers write KV to a dedicated KV-cache service; decode workers read from it. This isolates long, bursty prefill from steady, latency-sensitive decode.

Expand Down
Loading