
Mini-SGLang GitHub 项目SGLang 官方文档vLLM 官方文档1. 如果没有 KV Cache会发生什么LLM 生成文本是**自回归Autoregressive**的模型一次生成一个 token再把这个 token 接到输入后面继续生成下一个 token。假设用户输入 prompt 后模型依次生成token1、token2、token3。如果没有 KV Cache每次前向都可能重新计算历史 token 的 Key 和 Value用户 Prompt第 1 次前向计算 Prompt生成 token1第 2 次前向重新计算 Prompt token1生成 token2第 3 次前向重新计算 Prompt token1 token2生成 token3这会造成大量重复计算。序列越长、生成 token 越多浪费越明显。我的理解是KV Cache 就像模型给历史 token 做的一份笔记。已经算过的 Key/Value 不应该每次都重新算一遍而应该保存下来下次直接读取。2. KV Cache 是什么在 Transformer Decoder 的每一层 Attention 中每个 token 都会经过投影得到Query当前 token 想查询什么信息。Key当前 token 可以被怎样匹配。Value当前 token 真正携带的信息。KV Cache 保存的就是历史 token 在每一层已经计算好的 Key 和 Value。生成一个新 token 时只计算新 token 的 Query、Key、Value。把新 token 的 Key/Value 追加到 KV Cache。用新 token 的 Query 去读取历史 KV Cache。经过 Attention 和后续网络得到 logits。Sampler 选出下一个 token。新 tokenQ/K/V 投影Q_newK_new / V_newKV Cache历史 K/VAttention输出 hidden stateSamplernext tokenKV Cache 的作用是避免重复计算历史 token 的 K/V。加快 Decode 阶段。支持公共前缀复用。但它也有代价KV Cache 会占用 GPU 显存。序列越长缓存越大。并发请求越多总 KV Cache 越大。一个粗略的单 token KV Cache 显存公式是单 token KV 字节数 ≈ 2 × L × H_kv × D_head × B其中2Key 和 Value 各一份。LTransformer 层数。H_kvKV head 数。D_head每个 attention head 的维度。B每个元素占多少字节例如 FP16/BF16 通常是 2 字节FP8 是 1 字节。那么N个 token 的 KV Cache 大约是总 KV 字节数 ≈ 2 × L × H_kv × D_head × B × NGQA/MQA 会减少 KV head 数因此可以明显减少 KV Cache 的显存和读取量。3. Prefill 和 Decode一次 LLM 请求通常可以分成两个阶段Prefill 和 Decode。3.1 PrefillPrefill 是“处理用户输入 prompt”的阶段。它的特点是prompt 在请求开始时已经完整给出。可以并行处理 prompt 中的多个 token。计算量较大矩阵乘法形状较大。会为 prompt 中的所有 token 计算并写入 K/V。主要影响TTFTTime To First Token首 token 延迟。通常更偏Compute-bound计算受限。3.2 DecodeDecode 是“逐个生成输出 token”的阶段。它的特点是不使用推测解码时每一步通常只为每个请求生成一个新 token。token 之间有依赖关系不能像 Prefill 那样一次性并行生成整段答案。每一步都要把新 token 的 K/V 追加到缓存。每一步都要读取该请求已经积累的 KV Cache。主要影响TPOT/TBT每个输出 token 的时间。通常更偏Memory-bound显存带宽受限。未结束EOS 或达到 max_tokens用户 PromptTokenizerPrefill并行处理 prompt计算并写入 K/VSampler得到第一个 next tokenDecode Loop计算新 token 的 Q/K/V追加 K/V读取全量 KV Cache生成 next token返回完整输出3.3 Prefill 与 Decode 对比对比项PrefillDecode处理对象用户输入的完整 prompt每一步新生成的 tokentoken 是否已知输入 token 已知输出 token 依赖上一步采样结果并行性可以并行处理多个 prompt token不使用推测解码时每步每个请求通常只处理一个新 tokenKV Cache 行为批量写入 prompt 的 K/V追加一个新 K/V并读取历史缓存主要瓶颈通常是计算瓶颈通常是显存带宽瓶颈关键指标TTFTTPOT、TBT、流式输出速度3.4 Chunked Prefill如果 prompt 很长一次性 Prefill 可能占用大量 GPU 资源并让正在 Decode 的请求等待很久。Chunked Prefill 会把长 prompt 切成多个 chunk分多次调度。这样可以避免长 Prefill 长时间阻塞 Decode。更平滑地调度多个请求。控制在线服务的 token 间延迟。但它也可能让单个请求的 TTFT 变长需要调度器在延迟和吞吐之间做平衡。4. 内存墙是什么GPU 中既有计算单元也有显存SM / Tensor Core 负责计算。HBMHigh Bandwidth Memory保存权重、KV Cache、激活等数据。计算单元要工作必须先把数据从 HBM 搬到芯片上的寄存器或 SRAM。问题是GPU 计算能力的增长速度长期快于 HBM 带宽的增长速度。当计算单元很快但数据供应不上时GPU 就会等待。这个瓶颈就是内存墙Memory Wall。有限带宽 Bytes/s低FLOPs 少但读取数据多高计算多且数据复用充分HBM / 显存Weights KV Cache ActivationsSM / Tensor Cores计算单元算术强度高吗Memory-bound遇到内存墙Compute-bound主要受计算能力限制判断一个操作更偏计算受限还是内存受限可以看算术强度算术强度 FLOPs / 访问字节数算术强度高单位数据可以支撑很多计算更容易受计算能力限制。算术强度低没算多少东西就要读写大量数据更容易受显存带宽限制。一个简化的性能判断方式是数据搬运时间 ≈ 访问字节数 / 显存带宽 计算时间 ≈ FLOPs / 计算峰值 实际耗时主要取决于两者中更长的那个Decode 阶段每一步新 token 的计算量较小但要读取模型权重和大量 KV Cache因此很容易变成 Memory-bound。上下文越长KV Cache 越大Decode 每一步要读取的数据越多内存墙越明显。我对内存墙的理解是不是 GPU 不会算而是它需要的数据来不及从 HBM 送过来。5. KV Cache、Prefill/Decode 和内存墙的关系自回归生成需要历史 token 信息使用 KV Cache避免重复计算历史 K/V占用更多 GPU 显存显存容量压力能同时服务多少请求Decode 每步读取 KV Cache显存带宽压力每个 token 生成多快内存墙这里其实有两个问题显存容量问题KV Cache 能不能放下能支持多少并发和多长上下文。显存带宽问题Decode 时读取 KV Cache 够不够快token 生成速度是多少。PagedAttention 更偏向解决多请求下 KV Cache 如何分页、分配和减少碎片RadixAttention 更偏向解决公共前缀如何自动复用FlashAttention/FlashInfer 更偏向通过分块、融合和更好的内存访问减少数据搬运。6. 常见优化手段分别解决什么问题优化手段主要解决的问题我的理解KV Cache避免重复计算历史 K/V把历史 token 的 K/V 保存下来GQA / MQA减少 KV head 数用更少的 K/V 服务更多 Query headPagedAttentionKV Cache 分页管理和碎片问题像操作系统管理内存页一样管理 KVRadixAttention公共前缀复用把可复用前缀组织成树避免重复 PrefillFlashAttention / FlashInferAttention 的 HBM 数据搬运和 kernel 融合分块读取、片上计算避免落地巨大 attention 矩阵Continuous Batching动态拼批提高 GPU 利用率请求可以动态加入或离开批次Chunked Prefill长 prompt 阻塞调度把长 Prefill 切成小块与 Decode 一起调度KV Cache 量化减少 KV 显存容量和读取字节用 FP8/INT4 等方式保存 K/V推测解码提高 Decode 每步计算量和并行度一次验证多个候选 tokenCUDA Graph减少 CPU 启动 kernel 的开销把多个 kernel 启动流程录下来重放Prefill/Decode 分离两类阶段资源需求不同让 Prefill 和 Decode 使用不同 GPU 池Tensor Parallel单卡放不下权重或 KV把权重和 KV 分片到多 GPU但会引入通信7. 用自己的话总结KV Cache 是一份历史 token 的 Key/Value 笔记。它让模型不用在每次生成新 token 时都重新计算整个历史。Prefill 是批量处理 prompt、批量写笔记的阶段。它通常计算密集决定首 token 多久出现。Decode 是一边追加新笔记、一边翻看全部旧笔记的阶段。它通常受显存带宽限制决定 token 流式输出的速度。内存墙是计算单元和显存带宽之间的速度差。GPU 算得很快但数据来不及从 HBM 搬过来计算单元就只能等待。现代推理系统不是只靠某一个优化而是同时管理显存、批次、前缀、Attention kernel、通信和调度。8. Mini-SGLang 中对应源码阅读 Mini-SGLang 时可以把笔记和下面文件对应起来笔记概念Mini-SGLang 对应文件KV Cache 抽象python/minisgl/kvcache/base.pyMHA KV 池python/minisgl/kvcache/mha_pool.pyRadix Cachepython/minisgl/kvcache/radix_cache.pyPrefill 调度python/minisgl/scheduler/prefill.pyDecode 调度python/minisgl/scheduler/decode.pyKV 页和 page tablepython/minisgl/scheduler/cache.py调度主循环python/minisgl/scheduler/scheduler.py模型执行和采样python/minisgl/engine/engine.pyAttention 层python/minisgl/layers/attention.pyFlashAttention Backendpython/minisgl/attention/fa.pyFlashInfer Backendpython/minisgl/attention/fi.py9. 参考资料Mini-SGLang GitHubhttps://github.com/sgl-project/mini-sglangSGLang 官方文档https://docs.sglang.io/SGLang 论文https://arxiv.org/abs/2312.07104vLLM 官方架构文档https://docs.vllm.ai/en/stable/design/arch_overview/vLLM PagedAttention 论文https://arxiv.org/abs/2309.06180FlashAttention 论文https://arxiv.org/abs/2205.14135NVIDIA GPU Performance Backgroundhttps://docs.nvidia.com/deeplearning/performance/pdf/GPU-Performance-Background-User-Guide.pdfNVIDIA Long-Context Attention 博客https://developer.nvidia.com/blog/co-designing-ai-model-attention-for-fast-interactive-long-context-inference