Files
handsomezhuzhu.github.io/docs/sop/notes/vllm-learning-notes-pre.md
2025-11-23 11:09:23 +08:00

137 lines
5.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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
publish: false
---
# 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 BlocksGPU/CPU)、管理 **swap in/out、copy** CUDA 流与事件
---
## 3. 调度器原理
### 3.1 核心策略
* **Iteration-level**每生成一个 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 ParallelTP** 切分模型
* 初始化步骤加载权重 建立分布式组 **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 & 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 Graphblock size |
---
## 6. TP & PP
* **TPTensor Parallelism**层内张量切分通信频繁适合实时推理
* **PPPipeline Parallelism**层间切分通信少但需流水线调度更多见于训练