mirror of
https://github.com/handsomezhuzhu/handsomezhuzhu.github.io.git
synced 2026-02-20 20:00:14 +00:00
一些奇怪的更改
This commit is contained in:
171
docs/sop/notes/vllm-learning-notes-pre.md
Normal file
171
docs/sop/notes/vllm-learning-notes-pre.md
Normal file
@@ -0,0 +1,171 @@
|
||||
---
|
||||
title: vllm汇报稿
|
||||
date: 2025-09-26 23:00:00 # 发布日期和时间,格式:YYYY-MM-DD HH:MM:SS
|
||||
descriptionHTML: '<span style="color:var(--description-font-color);">预汇报稿件</span>'
|
||||
tags: # 文章标签列表,用于分类和搜索
|
||||
- AI
|
||||
- 笔记
|
||||
sidebar: true # 是否显示侧边栏:true显示,false隐藏
|
||||
readingTime: true # 是否显示阅读时间:true显示,false隐藏
|
||||
sticky: 0 # 精选文章设置:值越大在首页展示越靠前,0表示不精选
|
||||
recommend: true
|
||||
---
|
||||
|
||||
# vLLM 报告
|
||||
|
||||
> 主题聚焦:**PagedAttention 分页注意力、整体架构、调度器原理、Worker 原理、PP 与 TP 的区别、预填充(Prefill)与解码(Decode)**
|
||||
|
||||
## 1. 为什么需要 PagedAttention
|
||||
|
||||
### 1.1 传统 KV Cache 的两类浪费
|
||||
|
||||
* **内部碎片**:必须为每个请求按“可能最长序列(prompt+output)”预留 **连续** KV 空间,未用到的预留位被浪费。
|
||||
* **外部碎片**:全局连续大块分配导致“零头”拼不起来,无法切给其他请求。
|
||||
* **共享缺失**:**相同前缀的 KV** 无法复用,重复存储。
|
||||
|
||||
|
||||
### 1.2 PagedAttention 的核心思路
|
||||
|
||||
* 预分配一大块显存,**切成固定大小的 block**。
|
||||
* 逻辑上给每个序列分配 **Logical Blocks**,实际落在 **Physical Blocks**;两者通过 **Block Table** 映射。
|
||||
* 最多浪费 < block_size 个 slot,**显著减少内外部碎片**。
|
||||
|
||||
---
|
||||
|
||||
## 2. vLLM 的总体架构(抓主线)
|
||||
|
||||
|
||||
* **LLMEngine**:一次迭代做三件事:`schedule()` → `execute_model()` → `process_outputs()`。
|
||||
* **Scheduler**:决定本轮处理哪些请求、是否换入/换出 KV。
|
||||
* **Worker / ModelRunner**:执行模型前向,读写 KV cache,采样下一个 token。
|
||||
* **CacheEngine**:分配/维护 KV Blocks(GPU/CPU)、管理 **swap in/out、copy** 的 CUDA 流与事件。
|
||||
|
||||
---
|
||||
|
||||
## 3. 调度器原理(continuous batching)
|
||||
|
||||
### 3.1 核心策略
|
||||
|
||||
* **Iteration-level(continuous batching)**:每生成一个 token 后**重新调度**,因此 batch 大小可变。
|
||||
* **阶段区分**:vLLM 将请求分为 **Prefill(填充)** 与 **Decode(生成)** 两类批次,**同一轮只处理同一阶段**。
|
||||
|
||||
### 3.2 三个队列
|
||||
|
||||
* **waiting**:刚到达的请求,尚未分配 GPU blocks(只分配逻辑块)。
|
||||
* **running**:正在解码或已完成 prefill 的请求。
|
||||
* **swapped**:因显存不足被抢占、KV 暂存在 CPU 的请求。
|
||||
|
||||
### 3.3 块管理关键路径
|
||||
|
||||
* **BlockSpaceManager.can_allocate()**:预判 waiting 请求的 prompt 是否能拿到足够的 GPU blocks。
|
||||
* **allocate()**:为 prompt 的每个逻辑块绑定一个物理块。
|
||||
* **can_append_slot() / append_slot()**(解码迭代):若最后一个物理块**独占**则直接追加,否则触发复制。
|
||||
|
||||
### 3.4 抢占与换入/换出
|
||||
|
||||
* **Recompute**:单序列分支,释放 GPU 块,下次换回后**重算** KV。
|
||||
* **Swap**:多分支或不宜重算的场景,把物理块搬到 CPU;后续再 **swap-in** 回 GPU。
|
||||
* **优先级**:`swapped` 队列优先于 `waiting`。
|
||||
|
||||
---
|
||||
|
||||
## 4. Worker 原理(模型执行链路)
|
||||
|
||||
### 4.1 角色与初始化
|
||||
|
||||
* **Worker ↔ GPU**:一张卡一个 Worker;多卡时配合 **Tensor Parallel(TP)** 切分模型。
|
||||
* 初始化步骤:加载权重 → 建立分布式组 → **profile** 可用块 → **init CacheEngine** →(可选)**CUDA Graph** 预捕获。
|
||||
|
||||
### 4.2 一次执行(`execute_model`)
|
||||
|
||||
1. **状态同步**:驱动 Worker 广播批次规模与内存操作。
|
||||
2. **数据交换**:调用 CacheEngine 的 `swap_in/out`、`copy`。
|
||||
3. **准备输入**:生成 `input_tokens / positions / input_metadata`。
|
||||
4. **前向**:
|
||||
|
||||
* **Prefill**:走 FlashAttn;
|
||||
* **Decode**:走 PagedAttention 内核。
|
||||
5. **采样**:产生下一个 token。
|
||||
|
||||
### 4.3 CacheEngine 的数据结构
|
||||
|
||||
* 每层有 **key_blocks / value_blocks**。
|
||||
* **Block Table**:记录 `seq_id → [block_numbers...]`。
|
||||
* **slot_mapping**:映射“本轮 token”写入到物理块的哪个 slot。
|
||||
|
||||
---
|
||||
|
||||
## 5. Prefill vs Decode(执行特点与调优,展开版)
|
||||
|
||||
### Prefill(填充阶段)
|
||||
|
||||
* **目标**:对完整 prompt 做一次性前向传播,构建 KV 缓存,并生成第一个 token。
|
||||
* **数据特征**:长序列、少批次、padding 多、计算密集。
|
||||
* **实现**:FlashAttention;输入 `_prepare_prompt`,输出第一个 token。
|
||||
* **瓶颈**:HBM 带宽、padding 浪费、长 prompt 拖慢 batch。
|
||||
* **调优**:限制 `max_num_batched_tokens`,分批/压缩长 prompt,减少 padding。
|
||||
|
||||
### Decode(解码阶段)
|
||||
|
||||
* **目标**:逐 token 生成。
|
||||
* **数据特征**:迭代频繁、短序列、并发依赖多。
|
||||
* **实现**:PagedAttention;输入 `_prepare_decode`,只追加一个 slot。
|
||||
* **瓶颈**:Kernel 启动开销、随机访存、并发不足。
|
||||
* **调优**:提高 `max_num_seqs`、用 CUDA Graph、调整 block_size、控制采样分支。
|
||||
|
||||
### Prefill vs Decode 对比总结
|
||||
|
||||
| 维度 | Prefill | Decode |
|
||||
| ---------------- | --------------------- | -------------------------- |
|
||||
| **目标** | 编码完整 prompt,建立 KV 前缀 | 逐 token 生成 |
|
||||
| **批次特征** | 少批次、长序列、padding 多 | 多批次、短序列、迭代频繁 |
|
||||
| **Attention 路径** | FlashAttention | PagedAttention |
|
||||
| **主要瓶颈** | HBM 带宽 + Padding | Kernel Overhead + 随机访存 |
|
||||
| **优化抓手** | 控制 padding、限制长 prompt | 提升并发、CUDA Graph、block size |
|
||||
|
||||
---
|
||||
|
||||
## 6. TP 与 PP:概念、区别与 vLLM 现状
|
||||
|
||||
* **TP(Tensor Parallelism)**:层内张量切分;通信频繁;适合实时推理。
|
||||
* **PP(Pipeline Parallelism)**:层间切分;通信少但需流水线调度;更多见于训练。
|
||||
* **vLLM 支持**:TP 是主力;PP 支持有限。
|
||||
|
||||
---
|
||||
|
||||
## 7. 关键流程串讲
|
||||
|
||||
### Prefill 批
|
||||
|
||||
1. waiting 进入 running;BlockSpaceManager 分配块;
|
||||
2. Worker `_prepare_prompt` → FlashAttn → 写入 KV;
|
||||
3. Sampler 采样首 token。
|
||||
|
||||
### Decode 批
|
||||
|
||||
1. Scheduler 选择 running 序列;
|
||||
2. `_append_slot` 写 KV;
|
||||
3. Worker 调 PagedAttention → 输出;
|
||||
4. 采样 → EOS/长度 → 释放序列。
|
||||
|
||||
---
|
||||
|
||||
## 8. 参数与调优清单
|
||||
|
||||
* **`block_size`**:16;权衡碎片与索引开销。
|
||||
* **`gpu_memory_utilization`**:估算可分配 KV;预留峰值空间。
|
||||
* **`max_num_seqs`**:提升解码并发。
|
||||
* **`max_num_batched_tokens`**:限制长 prompt。
|
||||
* **`swap_space_bytes`**:CPU 交换空间,优先考虑 Recompute。
|
||||
* **采样参数**:top-k/top-p/温度/惩罚影响解码步数。
|
||||
* **CUDA Graph**:捕获稳定批,降低开销。
|
||||
|
||||
---
|
||||
|
||||
## 9. 可讲的亮点/考点总结
|
||||
|
||||
1. **PagedAttention**:block 化 + 块表管理,解决碎片问题。
|
||||
2. **continuous batching**:每 token 重调度,提升吞吐。
|
||||
3. **Recompute vs Swap**:计算 vs 带宽权衡。
|
||||
4. **TP 优先,PP 备选**。
|
||||
5. **Prefill vs Decode**:不同执行路径与瓶颈决定不同调优抓手。
|
||||
Reference in New Issue
Block a user