1.3 两条互补的优化路线:算子层与系统层


文档摘要

1.3 两条互补的优化路线:算子层与系统层 读者读完这一节,应该能回答一句:为什么 FlashAttention-2 和 PagedAttention 不是二选一,而是同一目标下的"双引擎",以及本教程为什么把它们放在一本书里讲。 前面两节我们已经把瓶颈拆清楚了:一个是"单步注意力算得慢、中间矩阵占显存"(1.1),一个是"KV Cache 长期占显存、还被笨分配浪费掉"(1.2)。有意思的是,这两件事几乎不重叠——解决前一个,对后一个帮助有限;解决后一个,也救不了前一个。这就自然引出两条相对独立的优化路线。把它们想清楚,你就不会再在选型时犯"二选一"的错误;而且你会理解,为什么业界最成熟的推理引擎(比如 vLLM)从来都是两者一起上。 1.3.

1.3 两条互补的优化路线:算子层与系统层

读者读完这一节,应该能回答一句:为什么 FlashAttention-2 和 PagedAttention 不是二选一,而是同一目标下的"双引擎",以及本教程为什么把它们放在一本书里讲。

前面两节我们已经把瓶颈拆清楚了:一个是"单步注意力算得慢、中间矩阵占显存"(1.1),一个是"KV Cache 长期占显存、还被笨分配浪费掉"(1.2)。有意思的是,这两件事几乎不重叠——解决前一个,对后一个帮助有限;解决后一个,也救不了前一个。这就自然引出两条相对独立的优化路线。把它们想清楚,你就不会再在选型时犯"二选一"的错误;而且你会理解,为什么业界最成熟的推理引擎(比如 vLLM)从来都是两者一起上。

1.3.1 两条路线分别打哪

两个互补的优化方向

路线一 · 算子层面(计算 + 显存带宽)
目标是让"单步注意力"这个具体算子本身又快又省。对应问题:1.1 里的 O(n²) 物化、HBM 反复读写。答案就是 FlashAttention-2——通过 tiling 把数据留在 SRAM、不写出整张 S、并重新安排循环顺序让 GPU 的线程分工更饱和。它不改变数学结果(输出和朴素实现逐位一致,在训练精度下),只改变"怎么算",典型收益是 2~4 倍端到端加速、显存从 O(n²) 降到 O(n)。它管的是"算得快不快"。

路线二 · 系统层面(显存管理)
目标是让"KV Cache 这块长期状态"不再浪费,从而支撑更大 batch、更高吞吐。对应问题:1.2 里的预留浪费、碎片、跨请求不共享。答案是 PagedAttention(vLLM 的核心)——像操作系统管理内存一样,把 KV 缓存切成固定大小的"页(block)",按需分配、跨请求共享、用引用计数管理。它不改注意力数学,改的是"显存怎么管"。它管的是"显存浪不浪费、并发上不上得去"。

1.3.2 为什么不互斥,反而常一起用

这是新手最容易误解的地方:以为"用了 FlashAttention 就不需要 PagedAttention"或者反过来。其实它们是不同层的事,用一个比喻:PagedAttention 像"仓库管理员",决定 KV 这堆货怎么摆放最省地方、谁都能借用;FlashAttention-2 像"流水线工人",拿到货物后怎么用最快的动作完成装配。仓库摆得再好,工人动作慢,整体还是慢;工人再快,仓库乱得找不到货,也白搭。

同一目标下的双引擎

一个真实推理系统的执行层次是这样的:

  1. 调度层:continuous batching 决定哪些请求这一步可以一起算;
  2. 显存管理层:PagedAttention 负责把各请求的 KV 以"页"为单位摆到显存里,避免碎片;
  3. 计算内核层:真到算注意力时,底层调用 FlashAttention-2(或类似融合内核)完成单步计算。

换句话说,PagedAttention 解决"数据放哪、怎么放得不浪费",FlashAttention-2 解决"拿到数据后怎么算得快"。vLLM 这类引擎正是把两者组装在一起:用 PagedAttention 管理显存,用 FlashAttention-2 做算子加速。少了任何一个,系统都达不到最优——只上 FlashAttention 不解决碎片,只上 PagedAttention 不解决单步带宽瓶颈。

作者主张:如果你在做推理优化选型,我的建议是"两者都要,先用 PagedAttention 把吞吐天花板抬起来,再用 FlashAttention-2 把单步延迟压下来"。因为对大多数在线服务,先卡你的是"并发上不去"(显存碎片),而不是"单步慢半拍"。等并发满了,再抠算子效率。这是经过很多真实部署验证的优先级。

1.3.3 一个对照表:各自解决什么、不解决什么

为了不混淆,把两者的边界列清楚:

维度 FlashAttention-2(算子层) PagedAttention(系统层)
主要目标 单步注意力更快、更省显存 KV Cache 不浪费、并发更高
改变的是 算子的计算/访存方式 显存分配与管理方式
对 S/P 矩阵 不再物化全矩阵 不直接影响
对 KV 碎片 不解决 彻底解决
数学结果 与朴素实现一致 与朴素实现一致
典型收益 2~4x 加速、显存 O(n²)→O(n) 吞吐大幅提升、近 0 碎片浪费
部署位置 计算内核 推理引擎/调度层

看到这张表应该很清楚了:两列几乎没有重叠的格子。它们不是竞争关系,是合作关系。

1.3.4 组合起来的收益为什么不是简单相加

有人会想:"两个都上,加速是不是 3 倍加 4 倍等于 7 倍?"不是的,加速是乘法关系,但收益体现在不同维度、且受最慢那段限制。可以这样理解:

  • FlashAttention-2 把"单步注意力"从带宽受限拉向算力受限,单步延迟降 2~4 倍;
  • PagedAttention 把"能并发的请求数"从 batch=4 抬到 batch=12,吞吐(总 tokens/s)涨约 3 倍;
  • 端到端看,单请求延迟改善主要来自 FlashAttention-2,系统总吞吐改善主要来自 PagedAttention。两者叠加,整体系统既"单条快"又"并发高",但最终数字取决于你的负载画像:偏延迟敏感(少并发)看 FlashAttention-2,偏吞吐敏感(高并发)看 PagedAttention。

这也解释了为什么"只上一个"的工程常常让人失望:你上了 FlashAttention-2,单请求快了,但卡并发;上了 PagedAttention,并发上去了,但单步还是慢。只有两个一起,才既过得了延迟关、又过得了吞吐关。

1.3.5 什么时候"可以只上一个"

虽然我主张两者都上,但也要诚实地说,存在只上一个就够的场景,免得你过度设计:

  • 纯离线批处理、序列短、batch 固定且已知:碎片问题不突出,PagedAttention 收益有限,上一个 FlashAttention-2 就能拿到主要加速;
  • 极长序列、单请求独占一张卡、不在意并发:KV 碎片不是主要矛盾,重点是单步算得快、省显存,FlashAttention-2 是主角;
  • 已经在用现成引擎(vLLM、TensorRT-LLM、SGLang 等):这些引擎默认就把两者打包好了,你几乎不需要自己选,只要理解它们分别在哪一层起作用即可。

换句话说,"两者都要"是针对"自己从零搭推理服务"或"要极限压榨吞吐"的人说的;如果你只是调用成熟引擎,那理解这一节的意义是"知道引擎内部在替你做什么",方便你排障和调参,而不是"再去手写一遍"。

1.3.6 本教程的路线:基础 → 核心 → 实战

把视野拉到整本书,章节安排正是顺着"问题 → 原理 → 落地"的递进:

全教程路线(基础到实战)
  • 第 1 章(你在这):先把瓶颈定位准——慢在哪、费显存在哪;
  • 第 2 章 · 核心模块:正面拆解 FlashAttention-2 的 tiling 与流水线、PagedAttention 的分页式 KV 管理;
  • 第 3 章 · 进阶原理:性能模型(算术强度、kernel 效率)、数值稳定性等更深的话题;
  • 第 4–5 章 · 实战与总结:把两者装进 vLLM / HuggingFace 真实推理栈,给出可复用配置,最后做工程权衡总结。

1.3.7 带着问题读下去

请带着两个具体问题进入第 2 章:

  1. 为什么"不把整个分数矩阵写出来"也能算对 softmax?(这解开 1.1 的伏笔,也是 FlashAttention 的灵魂)
  2. 把 KV 切成"页"之后,注意力计算要怎么在不连续的内存上正确完成?(这是 PagedAttention 最巧妙的地方)

把这两个问题想通,你就真正拿到了"推理加速"的钥匙,而不是只会调 API 的"调包侠"。下一章我们就一个一个拆开讲。

1.3.8 动手验证:拆开一个推理引擎看两层都在哪

与其空谈「两者都要」,不如真的打开一个成熟引擎的源码或文档,确认这两层确实都在。以 vLLM 为例(请以其当前官方文档为准),你可以这样建立证据链:在它的注意力实现里搜索 pagedblock table,你会找到负责 KV 分页管理的系统层代码;再搜索 flash attentionflash_attn,你会找到真正调用融合内核做单步计算的算子层代码。当你亲眼看到「一个在计算前先去查 block table 拼出 KV,再交给 flash attention 内核算」这段调用链,1.3.2 那张「仓库管理员 + 流水线工人」的图就不再是比喻,而是你能在代码里指认的两个函数。这一步非常值得做——它能把你从这节的「道理」变成「看得见的东西」。

1.3.9 心智模型:一个修车厂的两道工序

最后用一个修车厂的比喻收尾,帮你把整章串起来。注意力优化像经营一家修车厂:FlashAttention-2 是「技师的手速和工具摆放」——同一辆车在技师手里拆装更快、更不占工作台;PagedAttention 是「车间的车位调度」——让更多车能同时进场、不浪费空位。手速再快,车位调度烂,厂里一次只放得下两辆车,总产出上不去;车位调度再好,技师手慢,单车修半天,客户也骂。两者都做好,厂子才又忙又顺。本教程后面就分别深讲「技师怎么摆工具」(第 2、3 章)和「车位怎么调度」(第 2、3 章),最后在第 4 章把它们装进同一家厂。

1.3.10 本节你该记住的三句话

同样给这一节一个可带走的浓缩版:第一,注意力瓶颈分两层,算子层(FlashAttention-2)管「算得快不快」、系统层(PagedAttention)管「显存浪不浪费」,两层几乎不重叠;第二,它们是合作关系不是二选一,现代引擎默认两者同用,收益是乘法且分别改善「延迟」与「吞吐」;第三,自搭高并发服务时,优先级是先上 PagedAttention 抬吞吐天花板,再上 FlashAttention-2 压单步延迟;若只是调现成引擎,理解它俩在引擎内部的位置更有助于排障调参。把这三句记牢,你就不会在选型会上再说出「到底用 FlashAttention 还是 PagedAttention」这种把两个不同层混为一谈的话。

1.3.11 本节小结

  • 注意力瓶颈拆成两条独立路线:算子层(FlashAttention-2)管"算得快不快",系统层(PagedAttention)管"显存浪不浪费";
  • 两者不互斥,现代推理引擎同时采用,是"仓库管理员 + 流水线工人"的关系;
  • 加速是乘法而非相加,且分别改善"延迟"与"吞吐"两个维度;某些场景可只上一个(短序列离线批、单请求独占、或用现成引擎);
  • 选型建议:自搭服务先用 PagedAttention 抬吞吐天花板,再用 FlashAttention-2 压单步延迟;
  • 教程路线:基础(定位瓶颈)→ 核心(两大模块)→ 进阶(性能模型)→ 实战(落地)。

读后自测(1.3):合上这一节自问——① FlashAttention-2 和 PagedAttention 分别在哪一层起作用?记住一句话:一个管「算」,一个管「存」。② 为什么「两个都上」的收益是乘法而非相加?因为它们改善的是延迟和吞吐两个不同的维度,短板效应下只能叠加不能抵消。③ 你自己的场景属于「必须两者都上」还是「可以只上一个」?想清楚这三点,下一章你就能带着「我要重点看哪一层」的目标去读,而不是被信息洪流推着走。

延伸阅读提示:如果你读到这里已经迫不及待想看代码,建议先把第 2 章的原理啃完再看实现,否则很容易被各种 kernel 细节带偏。原理不清时看代码,就像没看图纸就拆发动机——能拆,但装不回去。

一句话带走 1.3:FlashAttention-2 和 PagedAttention 不是竞争对手,而是同一目标下的双引擎——一个让单步算得快,一个让显存不浪费;真正的高吞吐推理系统,从来都是两者一起上。记住这句话,你就超过了还在纠结「二选一」的绝大多数人。

小结前的提醒:本节所有结论都建立在「注意力有两个独立瓶颈」这个判断上。若你将来遇到某个新加速器宣称「一个技术通吃」,请用本节那张对照表逐格检验它是否真的同时解决了算子层和系统层——多数情况下它只解决了一半。


发布者: 作者: 转发
评论区 (0)
U