ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

什么是 LLM 中的分页注意力(Paged Attention)?它是如何工作的?

什么是 LLM 中的分页注意力(Paged Attention)?它是如何工作的?

分页注意力(Paged Attention)是一种把 KV 缓存(KV Cache)切分成固定大小的小块——称为"页"(page)——来更高效管理显存的技术。它消除了显存浪费,让 LLM 能够同时服务多得多的用户。

本文将围绕分页注意力展开:它解决的正是 KV 缓存的显存浪费问题,让 LLM 得以并发服务更多用户。

我们会先快速回顾 KV 缓存,理解它带来的显存问题;再用一个例子看清传统显存分配如何浪费空间;最后走一遍分页注意力如何借鉴操作系统管理内存的思路来解决这个问题。

快速回顾:KV 缓存

当 LLM 生成文本时,它一次预测一个 token。token 是文本的一小片,可以是一个词、词的一部分,甚至单个字符。

在每一步,模型都会为新 token 计算一个 Key 和一个 Value,同时需要所有此前 token 的 Key 和 Value。

如果没有 KV 缓存,模型在每一步都要为每一个历史 token 重新计算 Key 和 Value,白白浪费算力。KV 缓存的解法是:每个 token 的 Key 和 Value 一旦算完就存下来,后续步骤直接复用。

KV 缓存让文本生成快了很多,但代价随之而来:它要占用显存来保存所有这些 Key 和 Value。

接下来,我们看看它由此带来的显存问题。

问题所在:KV 缓存中的显存浪费

当用户向 LLM 发来一个请求,系统需要为该请求的 KV 缓存预留显存。难点在于:系统事先并不知道回答会有多长。

用户可能问一个简单问题,回答只有 20 个 token;也可能问一个复杂问题,回答长达 2,000 个 token。系统无法提前得知。

传统做法是为每个请求预留一大块显存——所有槽位紧挨在一起——大小按可能的最大回答长度来定。也就是说,哪怕实际只输出 50 个 token,也要先按比如 2,048 个 token 预留。

用一个类比来说明。

酒店类比

想象一家酒店,客人入住时并不知道自己要住几晚。有人住 1 晚,有人住 30 晚。

传统做法:客人一入住,酒店就在同一楼层给他预留 30 间连着的房间,以防他要住满 30 晚。哪怕客人只住 2 晚,这 30 间房也一直占着,不能给别人。

再想象 10 位客人入住,酒店就要预留 30 × 10 = 300 间连着的房间。但如果大多数客人只住几晚,几百间房就空在那里浪费掉了。

传统做法下的 KV 缓存显存,正是这个局面。

浪费体现在两个方面:

内部浪费:显存被预留了却从未使用。如果请求只生成 50 个 token,而显存是按 2,048 个 token 预留的,那么剩下 1,998 个 token 的空间就浪费了。

外部浪费:因为显存必须以一大块连续空间分配(所有槽位紧挨着),散落在各处的空闲小碎片就用不上——哪怕它们加起来足够服务一个新请求。

这种浪费很严重:预留的显存里有很大一部分从未真正被使用。

而显存是有限的,浪费就意味着系统能同时服务的用户变少。如果能把显存用得更省,就能同时处理多得多的请求。

这正是分页注意力要解决的问题。

什么是分页注意力?

分页注意力是一种通过把 KV 缓存切分成固定大小的小块(称为页)来更高效管理显存的技术。这些页在显存中不必相邻。

"分页(Paged)"这个词来自操作系统里的一个概念。操作系统要为电脑上运行的所有程序管理内存。

它并不给每个程序分配一大块连续内存,而是把内存切成固定大小的小片,称为页。每个程序按需拿到若干页,这些页可以散落在内存中的任何位置。操作系统用页表(page table)记录每一页的位置——页表就是一张简单的清单,把程序的每一页映射到它在内存中的实际位置。

分页注意力把同样的思路用在了 KV 缓存上。

我们回到酒店类比,看看它是怎么运作的。

酒店类比:分页做法

分页做法:酒店不再给每位客人预留 30 间连着的房间,而是按需一间一间地给。客人入住先给 1 间;需要再住一晚,就再给一间——哪里有空的就给哪间,不必挨着上一间。酒店维护一张清单,记录哪些房间归哪位客人。

这样,客人只住 2 晚就只用 2 间房,其余房间可以留给别的客人。零浪费。

10 位客人入住,每人只拿自己真正需要的房间。同样的房间数,酒店能接待多得多的客人。

分页注意力在 KV 缓存上用的就是这个办法。

分页注意力是如何工作的

在分页注意力中,KV 缓存的显存被划分为固定大小的小块。每个块能存放固定数量 token 的 Key 和 Value。例如块大小为 4,则每个块可存 4 个 token 的 Key 和 Value。

走一个例子。

假设用户发来一个请求,模型正在生成回答:“I love teaching AI and Machine Learning at Outcome School”。(示例输出保留英文原句,因为后续的 token 计数与槽位对齐依赖英文分词结果。)

不使用分页注意力(传统做法)

系统一次性预留一大块显存,比如 16 个 token:

显存:[__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __] 为该请求预留(16 个槽位)

随着模型生成 token,槽位被逐个填入:

显存:[I love teaching AI and Machine Learning at Outcome School __ __ __ __ __ __] 已用:10 个 token 浪费:6 个槽位

剩下 6 个槽位被预留却从未使用——显存就这样浪费了。

使用分页注意力

系统按每块 4 个 token 分配显存,且只在需要时才分配:

第 1 步:模型开始生成。系统在显存中任意可用位置分配块 1:

块 1:[I love teaching AI]

第 2 步:块 1 满了。系统分配块 2,它不必挨着块 1:

块 1:[I love teaching AI] 块 2:[and Machine Learning at]

第 3 步:块 2 满了。系统分配块 3:

块 1:[I love teaching AI] 块 2:[and Machine Learning at] 块 3:[Outcome School __ __]

块 3 里只浪费了 2 个槽位,而传统做法浪费了 6 个。而且没有任何显存是被提前预留后闲置的。

块表

可块都散落在显存各处,模型怎么找到它们?

系统用块表(block table)记录哪些块属于哪个请求,就像操作系统用页表一样。

请求 1 的块表: 块 0 → 显存位置 5 块 1 → 显存位置 12 块 2 → 显存位置 3

块按顺序编号(0、1、2),每个编号指向它在显存中的实际位置。当模型需要读取某个历史 token 的 Key 和 Value 时,就用块表找到对应的块。

这就是为什么即使块散落在显存各处,模型依然能访问任意历史 token 的 Key 和 Value。

分页注意力为何如此有效

分页注意力解决了前面提到的两类显存浪费:

内部浪费几乎被消除。显存按块、按需分配,唯一的浪费在最后一个块——它不一定每次都填满。在上面的例子里,最后一块 4 个槽位只浪费了 2 个,而传统做法 16 个槽位里浪费了 6 个。

外部浪费被彻底消除。因为块之间不需要相邻,显存中任何空闲块都能用上,不存在用不了的碎片空隙。

结果就是:分页注意力能把显存用得省得多。这意味着在同样的显存量下,系统能同时服务明显更多的请求。

注意,分页注意力并没有让 KV 缓存本身变小,它只是阻止了缓存周边的显存被浪费。让缓存本身缩小,是 KV 缓存压缩(KV Cache Compression)要干的事。

跨请求的内存共享

分页注意力还带来一项重要的优化:内存共享。

假设两个用户问了同一个问题。两个请求的输入 token(也就是问题本身)完全相同。有了分页注意力,两个请求可以共享输入 token 对应的同一批块,因为这些 token 的 Key 和 Value 是一样的。

系统只存一份,两个请求各自在块表里指向同一批块,而不必存两份。这又省下一大块显存。

这在以下场景中尤其有用:

并行采样(parallel sampling):有时我们希望模型对同一个问题生成多个不同的回答,再从中挑最好的。由于所有回答对应的问题相同,输入部分的块可以在这些回答之间共享。

束搜索(beam search):有时模型会同时探索多条可能的续写路径,以找出最好的一条。同样,共享的输入只需存一份,供所有并行序列复用。

在这些场景下,内存共享会显著降低总显存需求。

SGLang 把"共享相同前缀"这个思路做得更进一步:它用基数树(radix tree)组织共享文本,从而更精确地匹配共享前缀。

分页注意力是 vLLM 的核心思想——vLLM 是当前流行的高吞吐推理服务引擎。同样的分块思路也出现在 NVIDIA 的 TensorRT-LLM 中,名为分页式 KV 缓存(paged KV cache)。

至此,我们已经理解了分页注意力,以及它如何借用操作系统的分页思路来高效管理 KV 缓存显存、消除浪费,并同时服务更多用户。

与分页注意力配套的是连续批处理(Continuous Batching):只要有一个老请求结束,就立刻把新请求塞进批次,让 GPU 始终忙碌。

常见问题

分页注意力会让 KV 缓存变小吗?

不会。分页注意力并没有让 KV 缓存本身变小,它只是阻止了缓存周边的显存被浪费。让缓存本身缩小,是 KV 缓存压缩(KV Cache Compression)的职责。

系统为什么不干脆按每个请求的实际需要来预留显存?

因为系统事先并不知道回答会有多长。一个简单问题可能只需 20 个 token 的回答,一个复杂问题可能需要 2,000 个。所以传统做法只能按最大可能长度预留,其中很大一部分最终用不上。

分页注意力能消除所有显存浪费吗?

几乎可以。外部浪费被消除——块不需要相邻,任何空闲块都能用上。内部浪费也几乎被消除——显存是一块一块按需分配的。剩下的唯一浪费在最后一个块,它不一定每次都填满。

分页注意力和 vLLM 是同一个东西吗?

不是。分页注意力是一种技术,vLLM 是建立在其之上的一个流行高吞吐推理服务引擎,分页注意力正是 vLLM 的核心思想。同样的分块思路也出现在 NVIDIA 的 TensorRT-LLM 中,名为分页式 KV 缓存。

分页注意力如何与连续批处理配合?

连续批处理与分页注意力配对,让 GPU 保持忙碌:任何一个老请求结束的那一刻,就把新请求加进批次。而分页注意力通过更高效地使用显存,为这些额外请求腾出了空间。

返回列表