音乐
暂未播放
Prefix Sliding 完全拆解:前缀 + 滑窗,恒定 KV 内存下的超长推理测试时扩展
引子:当「想得越久越强」撞上内存墙#
2024 年 OpenAI o1 发布之后,推理模型(Reasoning Model)确立了一个新范式:测试时扩展(Test-Time Scaling)——不给模型更多参数,而是在推理阶段给它更多计算量,让它「想得更久」,从而解出更难的题。o1 内部的思路随后被一系列开源工作复现和推进:DeepSeek-R1 用强化学习(Reinforcement Learning,RL)训练出「想得越久越对」的行为;斯坦福 Muennighoff 等人的 s1 项目则展示了更朴素的路径——不需要 RL,用「预算强制(Budget Forcing)」控制思考长度,1 千美元级训练成本就能在 AIME 数学竞赛上追平 o1-preview。
这条路线有一个被反复强调的前提:想得越久,需要记住的东西越多。绝大多数推理模型仍然使用标准的全注意力(Full Attention),模型把整条推理轨迹(Reasoning Trace)完整保存在内存里——也就是 KV Cache。每生成一个新 token,注意力都要扫过历史上所有 token 的 K、V。于是:
- 内存随序列长度线性增长。以 Qwen3-1.7B(28 层、GQA 2 个 KV 头、头维度 128、FP16)为例,每个 token 的 KV 占用约:
其中 L 是层数,hkv 是 KV 头数,dhead 是头维度,b 是每个元素占用的字节数。10 万 token 的推理轨迹就要约 2.8 GB 显存,100 万 token 约 28 GB;换成 7B 级模型(4 个 KV 头)则约 57 KB/token,100 万 token 约 57 GB——单张 80 GB H100 在装下权重后已经放不下。
- 计算也随序列长度线性增长。生成第 n 个 token 时,注意力要对 n 个历史 token 逐一算相似度并读取它们的 KV。序列越长,每个新 token 越贵,生成速度持续下降。vLLM + FlashAttention 在单张 H100 上的实测中,全注意力在序列长到 128K token 时吞吐只剩约 448 token/s(见后文 Table 1),而短序列时轻松上万。
这就是长程推理的「内存墙」:不是模型想不到 10 万步,而是基础设施不允许它想那么久。面对这道墙,社区已经有一批「上下文压缩」式方案:定期把旧内容总结成摘要(Compaction/Summarization,Claude Opus 4.6、GPT 5.4、Cursor Composer 等都在用)、只保留最后 k 个 token(Last-k,也被称为 Markovian Thinking)、纯滑动窗口注意力(Sliding Window Attention,Longformer/Big Bird 的遗产)、以及各种 KV Cache 淘汰策略(H2O、QEvict 等)。它们各有各的问题,后文会逐一对比。
2026 年 8 月 26 日,由 Niklas Muennighoff(s1 的作者)牵头、斯坦福大学、华盛顿大学、加州大学圣芭芭拉分校与 Prime Intellect 合作的团队在 arXiv 上发布了论文 《Prefix Sliding for efficient test-time scaling》(28 页正文 9 页、22 张图、3 张表),给出一个极简的答案:推理时只保留「前缀 + 最近几千年 token 的滑窗」,中间的推理过程全部丢弃。做法简单到可以写进几行注意力掩码,但效果是:不加任何训练就能让现有模型在同等思考时间内快 3 倍且不掉点;配合 RL 训练,模型可以稳定推理超过 10 万 token,成本恒定。
这篇文章把这套方法彻底拆开:先讲支撑它的注意力观察,再讲掩码机制、位置编码处理、FlashAttention kernel 实现、RL 训练细节,最后用论文的实验数据对比三条替代路线并讨论局限。
观察一:中间推理 token 会「过期」#
Prefix Sliding 的整个立论建立在两个注意力观察上。第一个观察来自论文的 Figure 2:对 Qwen3-1.7B 在 AIME25(美国数学邀请赛 2025 年题目)上的一条推理轨迹,统计所有层、所有注意力头平均后的 post-softmax 注意力概率(做了高斯平滑),画出「哪个位置的 token 拿到了多少注意力」:

图中有几个明显的高亮区域:
- 最开头的几个 token 拿到大量注意力。这首先是「注意力汇聚点(Attention Sink)」现象——StreamingLLM 发现,无论上下文多长,前几个 token(尤其是第 1-4 个)总会吸收一大块概率质量,因为 softmax 需要把概率归一化,而早期 token 扮演了「溢出阀」的角色。此外,系统提示、任务描述、工具定义就在这一段,是模型必须随时能查的「任务手册」。
<think>分隔符位置也显著高亮。DeepSeek-R1 系列用这个特殊 token 标记推理开始,模型在整条轨迹中持续注意它,因为它承担着「我正处于思考模式」的持续信号作用。- 末尾一小段注意力陡增,尤其是紧挨着正在生成 token 的前一个 token——模型正「看着」当前推理写到哪了。
- 中间大段推理 token 的注意力概率整体压得很低,而且随轨迹推进还在继续走低。
第二个观察是:中间 token 的信息价值会快速衰减。推理的本质是逐步改写状态:算完一步,这步的推导过程就不再需要,只有结果进入后续步骤。论文里给了一个直观例子:解「((42 + 84) × 4) - 5」,一旦 42 + 84 算完,这一步的推理过程就失去了价值,下一步只需要结果 126;类似地,解一道几何题时,「构造辅助线」的思考过程在作图完成后就没用了。
两个观察合起来指向一个反直觉的结论:为了「万一有用」而把整条推理轨迹留在内存里,是巨大的浪费。中间 token 既不被模型注意,也大概率不会被未来用到——那么把它们丢掉,是不是可以?
这个「丢弃」与纯滑窗的差别在于:纯滑窗把前缀也一起丢掉了。而前缀恰恰是模型最容易失去的信息——任务是什么、能用什么工具、约束条件是什么。这就是 Prefix Sliding 名字的由来:前缀(Prefix)不许动,滑窗(Sliding window)负责跟随当前推理。
核心机制:注意力掩码上的两个区间#
Prefix Sliding 的机制一句话可以说完:推理过程中,每个新 token 只能注意到「前缀」和「最近 W 个 token 的滑窗」两个区间,中间的 token 对注意力完全不可见。下图是论文 Figure 3 的机制示意:

具体来说,设前缀长度为 Lprefix(系统提示 + 用户任务 + 工具定义),滑窗大小为 W(论文实验中取 512 到 16384 不等),那么任意时刻模型能看到的 token 总数被硬性封顶为:
Ltotal=Lprefix+W这个值与已经生成了多少 token 完全无关。生成第 100 个 token 和生成第 100 万个 token,注意力开销、KV 内存开销完全一样——每生成一个新 token 的成本是 O(1)(相对序列长度),而不是全注意力的 O(n)。论文把这称为「有界(Bounded)」成本:只要序列长度超过 Ltotal,每个新 token 的成本就进入恒定区间,这是「无限测试时扩展」的必要条件——想让模型想几周,每个 token 的成本必须是常数。
实现上不需要改模型结构,只需要改注意力掩码。设第 i 个生成的 token 为查询,合法注意力位置集合为:
A(i)={j∣j≤Lprefix 或 i−W<j≤i}即前缀区间 [1,Lprefix] 加上滑窗区间 (i−W,i],其余位置的注意力分数在 softmax 前被掩码成 −∞。注意两个细节:
- 前缀和滑窗之间的 token 并不是被「悄悄忽略」,而是被显式地隐藏。这与「降低注意力权重」的软性方法不同——权重可以通过重归一化再分配,但 KV 内存是实打实的,隐藏意味着这些 token 的 KV 直接不被存储(或者存储后不再被读取)。
- 滑窗的起点是前缀之后。窗口跟随生成位置 i 前进,最老的内容被移出窗口。前缀永远固定,所以模型永远记得任务是什么。
还有一个「预热(Warm-up)」阶段:当序列总长还没超过 Ltotal 时,掩码退化为全注意力——反正所有 token 都在前缀和窗口范围内,此时与普通模型行为完全一致。这个阶段对短任务很重要,后文会看到它决定了 Prefix Sliding 的收益边界。
从系统角度看,Prefix Sliding 相当于给 KV Cache 画了一条显存上限:无论推理轨迹多长,KV 占用不超过 Lprefix+W 个 token 的 KV。配合 vLLM 的分页式块管理(PagedAttention 的思路,本博客已有拆解),每个序列都能以固定上限预分配内存,调度和批处理都变得更可预测。
与已有稀疏注意力方案的关系#
这个机制在文献里并不突兀,它是三条老思路的「并集」:
- Longformer / Big Bird:最早提出「全局 token + 滑窗」的混合注意力模式。它们的全局 token 通常是少量分散的(Big Bird 随机挑一些、Longformer 用分隔符),而 Prefix Sliding 的全局 token 是一整段连续的前缀——因为它服务的场景(推理轨迹)里,关键信息恰好集中在开头这一段。
- StreamingLLM:只保留开头固定几个 token(比如 4 个)作为注意力汇聚点,其余全部用滑窗。Prefix Sliding 可以看作把「几个 token」扩展为「整段任务前缀」的 StreamingLLM——前缀不仅是 sink,还承载真实的任务信息。
- 纯滑窗注意力(gpt-neo、gpt-oss 等):只有窗口、没有前缀。正因为模型会忘记任务,gpt-oss 用「全注意力层 + 滑窗层交错」来补救——Prefix Sliding 则用一个掩码同时拿到两者的优点。
关键区别在于:上述方案大多是训练期就定死的结构(模型从头就以这种注意力训练),而 Prefix Sliding 是直接套在现成模型上的推理期掩码,一行配置就能启用,不需要任何训练;训练则作为可选的加强项。这一点与之前拆解过的 NSA(原生稀疏注意力)形成鲜明对照:NSA 主张「稀疏模式必须在预训练时原生内置」,而 Prefix Sliding 证明了推理期硬掩码对「推理轨迹」这类特殊序列足够有效,因为推理轨迹的注意力结构比普通文本更极端、更可预测。
位置编码:Reset PE 还是 Continue PE?#
推理模型几乎都用旋转位置编码(RoPE,旋转位置编码,Rotary Position Embedding)。RoPE 把位置信息注入每个 token 的表示,而 Prefix Sliding 会改变 token 之间的相对位置关系——中间一段被丢弃后,「前缀最后一个 token」和「窗口第一个 token」在位置上变成了邻居,这带来了位置编码的两种处理方案:
- 重置位置(Reset PE):把窗口内的 token 重新编号,让它们看起来紧跟前缀(前缀结尾是位置 Lprefix,窗口开头是 Lprefix+1,依次类推)。好处是位置号连续,模型看到的位置分布和训练时一致;坏处是每个窗口内 token 的位置都变了,已缓存的 KV 全部失效,必须重算。
- 延续位置(Continue PE):每个 token 保持它原始的位置号。窗口 token 的位置号巨大且与相邻 token 不连续(中间隔着几万号的缺口),但KV 缓存可以直接复用,不需要任何重算。
直觉上 Reset PE 更「干净」,但论文在附录 D 的消融里发现两者在 AIME25 上的性能差异统计上不显著(Figure 14,滑窗 2048,误差带重叠),因此选了 Continue PE——理由非常工程化:KV 复用省掉的算力是实打实的,而且训练时 Reset PE 几乎不可行(见训练一节)。
有意思的是,位置号的缺口并不会让模型崩溃:注意力本身对绝对位置不敏感(RoPE 依赖的是相对位置差),而模型在预训练中见过各种上下文截断,对「位置号跳跃」相当鲁棒。论文还指出一个更激进的未来简化方向:结合 DroPE 直接去掉位置编码(论文引用 Gelberg et al. 2025 的工作表明预训练模型可以丢弃位置编码而几乎不掉点),或者从头训练无位置编码的模型——那样连 Continue/Reset 的选择都消失了。
Kernel 实现:两层级过滤#
Prefix Sliding 不能靠「改一下注意力公式」就拿到全部收益,还得有一个快的 kernel。论文在 Hopper 架构上基于 FlashAttention 写了一个定制注意力 kernel(代码在开源仓库中,FlashAttention 的分块思想本博客已有完整拆解,这里直接沿用「HBM 块 / 片上 SRAM 块」的术语)。核心是两层级过滤:
- 块内掩码(Intra-tile masking):FlashAttention 把序列切成块(tile)逐块处理。对那些部分重叠合法注意力区(前缀 ∪ 滑窗)的边界块,逐元素施加掩码,保证只有合法的 (q,k) 对参与 softmax 与输出。这一层不改变 FlashAttention 的分块策略,只加一个逐元素 mask,保证数学正确性。
- 块间跳过(Inter-tile skipping):对完全落在丢弃区的块,直接跳过——不加载、不算。具体做法是把「生产者-消费者」流水线重构为遍历两个不相交的块区间:前缀块区间和窗口块区间。序列越长,被跳过的块占比越大,节省越可观。
两层合起来的效果是:Prefix Sliding kernel 的速度与标准滑窗注意力 kernel 相当(论文 Figure 6,单张 80GB H100、vLLM 自动批大小、窗口 4096):两者的吞吐都在序列超过窗口后稳定在约 5000 token/s 附近,而全注意力在 32K token 时已掉到约 1477 token/s、128K 时只剩约 448 token/s。Prefix Sliding 略慢于纯滑窗的差距来自前缀部分的额外读取——前缀必须每个 token 都参与注意力计算。
有意思的是吞吐曲线都有一个「先降后平」的形状:生成的 token 数还没超过窗口大小时,处于预热阶段,每个新 token 仍然要扫过全部历史(这阶段 PS 等价于全注意力),所以速度一路下滑;一旦超过窗口大小,成本进入恒定区间,曲线拉平。而全注意力的曲线永远在降——这就是「有界 vs 无界」在实测中的直接体现。
代码层面的启用方式也非常轻量。官方仓库给出的是基于定制分支的 vLLM + FlashAttention 构建(Muennighoff/vllm 的 st29 分支、Muennighoff/flash-attention 的 st29 分支),推理代码本身只是改模型配置:
1model = "Qwen/Qwen3-1.7B"2length = 32_0003w = 40964hf_overrides = {5 "use_sliding_window": True,6 "sliding_window": w,7 "max_position_embeddings": length * 2,8}9os.environ.update({"SWF": str(w)})10
11from vllm import LLM, SamplingParams12from transformers import AutoTokenizer13
14tokenizer = AutoTokenizer.from_pretrained(model)15prompt = "Prime factorize 806912."16messages = [{"role": "user", "content": prompt}]17text = tokenizer.apply_chat_template(18 messages,19 tokenize=False,20 add_generation_prompt=True,21 enable_thinking=True,22)23text += "<think>\n"24
25model = LLM(model, hf_overrides=hf_overrides)26s = SamplingParams(temperature=1, top_p=0.95, max_tokens=length)27output = model.generate([text] * 1, sampling_params=s)仓库说明里特别强调:Prefix Sliding 在 vLLM / flash-attn / RL 侧都是「简单修改」,容易移植到更新版本——方法本身的复杂度不在 kernel,而在掩码语义。
无训练推理:3 倍加速到底从哪来#
论文最「诱人」的结果是:什么都不训练,把掩码换上,现有模型就能在同样思考时间内显著提分。注意这里的措辞——Figure 1(下图)展示的是「准确率 - 平均思考时间」曲线:

两条曲线用的是同一个 Qwen3-1.7B 模型,唯一区别是注意力掩码。Prefix Sliding 曲线整体在左上方。论文在 Figure 1 的图注里把原因说得很直白:Prefix Sliding 表现更好,不是因为它生成的每个 token 质量更高,而是因为在同样的思考时间内它能生成更多 token——预算强制(Budget Forcing,即按时间/预算截断思考的推理控制手段)按「思考时间」而非「token 数」预算计算,于是每个 token 更便宜的一方,在同一预算内思考得更深、更远。
更准确地说,3 倍加速来自三个叠加因素:
- 每 token 计算量恒定。全注意力下第 n 个 token 要处理 n 个历史 KV,Prefix Sliding 永远只处理 Lprefix+W 个。序列越长,差距越大:Table 1(论文主结果表)显示在 32K 序列时窗口 4096 的 Prefix Sliding 吞吐 5479 token/s,是全注意力 1477 token/s 的 3.7 倍;到 128K 时是 5224 对 448,11.7 倍。
- 显存释放 → 批大小更大。单条序列的 KV 上限固定后,同一张卡能容纳的并发序列数大大增加,批处理(连续批处理,Continuous Batching,本博客已有拆解)可以维持更大的批次,进一步摊薄固定开销、提高吞吐。
- 波动小、可规划。全注意力下 KV 需求随序列增长而增长,调度器需要不断为长序列扩容;Prefix Sliding 的每序列内存上限固定,显存分配可预测,碎片和 OOM 风险大幅降低。
下面把窗口尺寸扫描表(论文 Table 1 的核心数据)完整展开,Qwen3-1.7B、budget forcing、avg@64(每个样本采样 64 次取平均正确率):
| 滑窗大小 | AIME25 avg@64 | AIME25 平均长度 | GPQA avg@64 | GPQA 平均长度 | MATH500 avg@64 | MATH500 平均长度 | 吞吐 @32K (tok/s) | 吞吐 @128K (tok/s) |
|---|---|---|---|---|---|---|---|---|
| 2048 | 27.7 | 47643 | 35.9 | 30107 | 89.8 | 9310 | 8973 | 8737 |
| 4096 | 33.9 | 29943 | 37.0 | 16707 | 91.5 | 7069 | 5479 | 5224 |
| 8192 | 35.8 | 19373 | 38.0 | 13605 | 91.4 | 6229 | 3291 | 2788 |
| 16384 | 35.3 | 19872 | 38.2 | 14378 | 91.5 | 6160 | 2441 | 1420 |
| 全注意力 | 34.2 | 19158 | 37.6 | 11403 | 91.7 | 6056 | 1477 | 448 |
这张表值得逐行读:
- 窗口 8192 是甜蜜点:AIME25 达到 35.8,比全注意力的 34.2 还高;MATH500、GPQA 也基本打平(91.4 vs 91.7、38.0 vs 37.6)。此时平均生成长度 19373 与全注意力 19158 几乎相同,说明模型没有为了凑 token 而重复劳动,纯粹的掩码更换没有损失推理质量。
- 窗口 2048 太小:AIME25 掉到 27.7,而平均长度暴涨到 47643——模型在反复「重新推导」。原因很直观:滑窗太小,一步推理刚算完就滑出窗口,后续步骤找不到中间结果,只能重算一遍。token 便宜了(吞吐最高,8973 tok/s),但质量崩了,总账算不过来。
- 窗口 16384 不涨反降:AIME25 回落到 35.3。窗口大意味着每 token 成本高(吞吐 2441 vs 3291),而收益(更完整的中间信息)已经饱和——8192 窗口已经能覆盖「模型真正需要的最近上下文」,再多只是花钱买用不上的内存。
- 速度对比的结论:在保持性能的前提下,8192 窗口在 128K 序列时比全注意力快 6.2 倍(2788 vs 448)。而论文摘要里说的「3 倍」是综合多个任务、常规序列长度的保守数字。
论文在评估方法上的一个细节值得注意:效率指标用「平均思考时间(秒)」而不是 FLOPs 或总 token 数。因为 FLOPs 和 token 数衡量的是「做了多少计算」,无法反映内存差异导致的批大小和延迟差异;而用户真正感知到的是「等了多久」。这是推理系统评估里一个重要的口径选择。
RL 训练:把「丢中间」训练进模型#
无训练版本已经很好用,但它有一个上限:模型在预训练/对齐阶段从没见过「中间 token 会消失」这件事,它会在窗口内依赖一些很快会被丢弃的信息,或者反复重述早前结论来对抗遗忘(窗口 2048 时 47643 的平均长度就是这种行为的证据)。如果让模型在知道中间 token 会被丢掉的约束下强化学习(RL),它会学会主动把关键信息组织进滑窗内(比如不写超长代码注释、把中间结论写进窗口内保持「可见」),从而在更小窗口下维持更长的有效推理。
论文的实验(Figure 8)用 GRPO(Group Relative Policy Optimization,DeepSeek 提出的组相对策略优化)训练 Qwen3-1.7B,同步版本用 Hugging Face 的 trl 实现,异步版本用 Prime Intellect 的 prime-rl 实现。训练数据是自建数学题集,用三个准则过滤:可猜性(Guessability)——小模型不思考 8 次内就能答对的题删掉;可验证性(Verifiability)——含「How」「Explain」这类无法客观判分的题删掉;难度(Difficulty)——弱模型永远答对和强模型永远答不对的题都删掉(前者太简单、后者可能无解)。
超长 rollouts 的反向传播:截断回传#
RL 训练长推理轨迹的真正难点在反向传播:一条 10 万 token 的 rollout 完整回传梯度,激活值会把 trainer 的显存撑爆——这正是业界普遍「把超长生成截断丢弃」的原因(DAPO 等系统都讨论过这个取舍)。Prefix Sliding 用截断反向传播(Truncated Backpropagation)解决:只对轨迹最后一段回传梯度。
为什么只回传最后一段就够了?这依赖滑窗的「感受野」性质:跨 L 层的滑窗注意力,理论上感受野是 W×L(每层向前传播 W 个位置的信息,L 层串联),但由于信息瓶颈,实际感受野只有约 1.5×W(这个结论来自滑窗注意力专门研究者的博客分析,论文引用了 Guangxuan Xiao 的分析)。也就是说,滑窗内一个 token 的梯度,最多只依赖它前面约 1.5×W 个 token 的内容——更早的 token 对它没有任何因果影响,回传了也得不到非零梯度。
因此,要算准最后 W 个 token 的梯度,只需把「最后 4×W 个 token」传给 trainer。论文给的例子:一条 10 万 token 的轨迹、窗口 2048,采样器只把最后 8192 个 token 发给 trainer;trainer 用前 6144 个 token 作为上下文(计算它们的前向),只在最后 2048 个 token 上计算 token 级 RL loss——实现上就是一个 loss mask:
1loss_mask = torch.zeros_like(logprobs) # 前 6144 个 token 的 loss 置零2loss_mask[-2048:] = 1.03loss = -(logprobs * loss_mask * advantages).mean()autograd 从被掩码的 loss 正常回传,梯度只更新最后 2048 个 token 对应的路径。这些梯度是在「比滑窗大 4 倍的上下文」上算出来的,与在全量 10 万 token 上回传的梯度非常接近(附录 E 用 DeepSeek-R1-Distill-Qwen-7B 验证:32768 个 token 传入 trainer,Prefix Sliding 只回传最后 8192 个,性能与全量回传相当)。
「传多少 token 给 trainer」本身是个超参数,论文做了专门的 KL 消融(KL 衡量 generator 里模型分布与 trainer 里模型分布的失配程度):
- 只传最后 2K(正好一个窗口):KL 超过 0.1,明显失配——窗口内的 token 依赖窗口外的历史,梯度是错的;
- 传 4K(2 倍窗口):KL 显著下降;
- 传 8K(4 倍窗口):论文选定值,与传 16K(8 倍)几乎持平,且略优。
残留的 KL 部分来自数值差异:generator 用的是定制的 FlashAttention kernel,trainer 用的是 PyTorch 的 FlexAttention 实现,两种 kernel 的浮点运算顺序不同,logits 有微小出入。这个 4 倍经验法则在大窗口上也成立(附录 E 的 7B 实验:窗口 8192、乘子 4)。
论文还提到了另一条更昂贵的替代路径:分块回传(Chunked Backpropagation)——把轨迹切成若干块逐块回传并累积梯度,理论上与完整回传近似等价,但实现复杂得多。论文选择截断回传,并提示更大规模、更长的链可能需要分块回传。
为什么训练中必须用 Continue PE#
无训练推理时选 Continue PE 的理由是效率;训练时它近乎是唯一选择。原因在教师强制(Teacher Forcing,训练时用真实 token 而非模型自生成的 token 作为下一步输入)上:训练时每个位置都要同时计算「以任意前缀为条件的概率」,如果重置位置编码,那么同一个 token 在不同样本里、不同上下文长度下,位置号会随它前面被重置的 token 数而变——每个 token 看到的「位置历史组合」都不同,批内计算无法对齐,位置编码的 KV 也无法跨步骤复用。Continue PE 下每个 token 的位置号固定,模型自然学会了「位置号有缺口」的分布。这也是论文坚持「训练与推理都用 Continue PE」的工程理由。
训练收益:同等内存下更长的推理#
Figure 7 展示了训练的直接收益:把全注意力模型的内存预算限制为最多 8192 token、Prefix Sliding 模型用 8192 的滑窗,两者「同内存」竞技——此时 Prefix Sliding 可以继续推理远超 8192 token(因为中间 token 不占内存),而全注意力模型在 8192 token 后必须停止。结果 Prefix Sliding 训练模型在 AIME25 上持续领先,因为它把同样的显存预算花在了更长的推理轨迹上。这正是「测试时扩展」在工程上的意义:算力(时间)可以无限给,显存不能无限给,而 Prefix Sliding 把瓶颈从显存搬到了算力/延迟上。
与三条替代路线的对比:为什么它们都差一口气#
能支撑「无限测试时扩展」的既有方案不止 Prefix Sliding 一个,论文选了三个最能打的代表做对比(全部在 AIME25 上、最大生成长度 262144、窗口/阈值 4096、avg@64,见 Figure 9):

Last-k:重复处理是原罪#
做法:生成到阈值 n 后,把除了最后 k 个 token 以外的内容全部删除,用这 k 个 token 接着生成,如此反复。这本质上是 agent 系统里常见的「删旧轮次」操作,也被独立提出为「Markovian Thinking(Delethink)」。
问题:
- 重复处理:最后 k 个 token 会被处理两次——第一次是生成它们的时候,第二次是上下文变化(前面内容被删)之后它们作为新上下文的开头被重新处理。k 越大,重复处理的量越大。
- 有用信息可能被删:k 太小时,模型正在用的中间结果可能已经滑出保留区,于是重新推导一遍(论文 Figure 21 展示了 AIME 样本上模型「从头再来」的例子)。
- 内存锯齿:每删一次上下文,显存用量骤降——内存曲线是锯齿状的(Figure 10),调度器难以让资源利用率保持平滑,GPU 利用率规划困难。
论文扫了 k(64 到 1024,Table 2),选 k=256:MATH500 60.8、AIME25 4.2,是「保留内容小 + 性能尚可」的折中——但这个最优值依赖上下文窗口和允许的轮次,实际部署中无法预先知道轮次,只能固定一个 k 打天下。
Summary(摘要/压缩):模型记不住自己的摘要#
做法:生成到阈值 n 后,让模型自己(或外部模型)把当前上下文总结成摘要,连同提示词一起作为新上下文继续。Opus 4.6、GPT 5.4、Cursor Composer 的「上下文压缩(Compaction)」走的都是这条路。
问题:
- 长链摘要的信息丢失:理论上模型可以从当前上下文中提取重要信息带到下一轮,但实证表明模型在多轮压缩中保留重要信息的能力很差——最近的系统研究(Lost in Compaction)证实了这一点。论文 Figure 20 展示了一个典型失败:模型看似生成了摘要,但下一轮它完全无视了自己的摘要,从头开始解题。
- 超参数爆炸:阈值 n、摘要长度、摘要提示词、摘要模型、摘要放在新上下文的哪个位置……每个都是要调的旋钮。
- 额外开销:摘要本身要生成 token,摘要模型越大越贵;与 last-k 一样有重复处理问题(摘要和上下文都处理两次),内存同样锯齿状。
- 论文的提示词消融(Table 3):把摘要当工具调用、并给模型一个「如何使用工具和上下文」的例子(prompt 2)效果最好,AIME25 准确率 26.4、覆盖率 53.3;只给工具定义(prompt 1)只有 23.2/33.3;再额外解释上下文(prompt 3)反而略降。也就是说摘要路线连「怎么写提示词」都要仔细调。
纯滑窗:模型忘了自己在干嘛#
做法:就是 Prefix Sliding 去掉前缀的版本。
问题:滑窗滑过头,任务描述、工具定义、约束条件全部滑出窗口——模型开始「迷失」,在长推理任务上性能随思考时间先涨后平(Figure 9 左下)。论文的原话是:模型忘了自己在解哪道题、能用哪些工具。这正是 gpt-neo/gpt-oss 这类纯滑窗模型需要「全注意力层交错」来补偿的原因——它们把滑窗当作训练期的稀疏化结构,而不是推理期的遗忘管理器。
对比结果#
Figure 9 的四个面板里,Prefix Sliding 的「准确率-思考时间」曲线在四个对比中全部位于左上方:同样的思考时间,Prefix Sliding 准确率最高;同样的准确率,它花的时间最少。纯滑窗最快拉平,last-k 和摘要能爬但爬得慢且成本结构差(锯齿)。而且 Prefix Sliding 只引入一个超参数(窗口大小 W),相比之下 last-k 有 n、k 两个,摘要有一串。论文还对比了 H2O(保留高频注意力 token 的 KV 淘汰策略),指出它「向后看」(依据历史注意力保留 token)而 Prefix Sliding「向前看」(前缀被无条件保留,即使它长时间没被注意——工具定义可能几千年 token 都没被用过,但一旦要用就必须在),两者思路可以结合:保留前缀 + 高频中间 token + 滑窗,可能更强,但 RL 训练下实现困难。
局限与未解决的问题#
论文诚实地列出了四条边界,都值得展开:
1. 中间 token 偶尔真的重要:LiveCodeBench 案例。 代码生成任务(LiveCodeBench)需要窗口至少 16384 才能追平全注意力。案例分析(Figure 22)显示原因很具体:模型在推理阶段先把函数实现写了一大段,然后用注释的形式思考了上千个 token——等它回来继续写代码时,函数开头已经滑出窗口。这类「长距离引用」正是滑窗的盲区。论文给出的出路:RL 训练让模型学会别在注释里思考(把结论写进代码本身);或者提供一个机制让模型把重要 token 主动追加到前缀。
2. 短任务几乎没有收益。 如果任务平均只需要 2086 个 token(HealthBench 实测),而窗口是 2048,那么绝大多数样本还没进入「滑动」阶段就结束了——全程等价于全注意力,只是多了个掩码。论文 Figure 12(下图)验证了这一点:两条曲线几乎重合。Prefix Sliding 的收益与生成长度强相关,短任务上它不是免费的,只是没有损失。

3. 系统输出与多轮对话。 agent 场景里模型可能读文件、读网页,一次性灌入几万 token 的「外部内容」会淹没整个滑窗——如果内容比窗口还大,模型严格意义上读不完;更糟的是窗口被外部内容占满,正在进行的推理反而被挤出去。多轮场景里,用户后续的指令应该追加到前缀还是任它滑走?论文承认这些问题在摘要、last-k 方案下同样存在,可能的解法包括:RL 让模型学会分步读取(用 head 而不是 cat)、在系统侧对超长输出做护栏。
4. Prefill 成本不受益 + 规模验证有限。 Prefix Sliding 只作用于生成阶段的 KV,长提示词的 Prefill 成本一点没省(附录 A 明确指出,并建议长文档别塞进提示词,给文件路径让模型按需读取)。另外,论文的规模验证止步于 7B 模型、数十万 token 的轨迹,更大的模型和更长的链还需要进一步实验。
小结#
Prefix Sliding 的全部内容可以压缩成一句话:推理轨迹中,前缀和最近几千年 token 几乎承载了全部注意力与信息价值,中间的推理过程可以安全丢弃,从而把每个新 token 的内存与计算成本从 O(n) 变成 O(1)。
它的位置在推理优化谱系里很特殊:不碰模型权重(无训练版)、不碰量化、不碰并行策略,只改注意力掩码 + 一个 kernel,就能拿到 3 倍加速和「推理长度不再受显存限制」的性质;而训练版(RL + 截断回传)又把「模型适应丢弃」这件事做进了行为里,让 10 万 token 以上的推理轨迹成为日常可能。把它和本博客讲过的几个概念放一起看,脉络很清楚:
- 它和 FlashAttention 系列是同一枚硬币的两面:FlashAttention 解决「如何高效算注意力」,Prefix Sliding 解决「哪些注意力根本不用算」——其 kernel 就是 FlashAttention 分块体系加了两层过滤;
- 它和 PagedAttention 互补:一个在掩码层封顶每序列 KV,一个在内存管理层消除碎片;
- 它和 NSA 形成对照:NSA 说稀疏注意力要原生训练,Prefix Sliding 证明推理轨迹这种「注意力高度结构化」的序列,硬掩码在现成模型上就能用;
- 它和 GQA 一样属于「用更少的内存换来接近全注意力的质量」,只是维度不同:GQA 压的是每个 token 的 KV 宽度,Prefix Sliding 压的是 KV 的序列长度。
对推理服务系统而言,它的启示可能比方法本身更广:测试时扩展的终极形态要求「每 token 成本有界」——无论是滑窗、摘要、RNN 还是状态空间模型,做不到这一点的架构在「想几周」的尺度上都会出局。Prefix Sliding 的价值在于用最少的工程改动(一个掩码)把一个已有的全注意力模型拖进了「有界成本」俱乐部。
参考资料#
- Prefix Sliding for efficient test-time scaling(论文原文,arXiv:2608.26070)
- Muennighoff/prefix-sliding(官方代码仓库:推理、RL、数据与评估脚本)
- s1: Simple test-time scaling(Muennighoff et al., 2025,arXiv:2501.19393)
- DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning(arXiv:2501.12948)
- Efficient Streaming Language Models with Attention Sinks(StreamingLLM,arXiv:2309.17453)
- Longformer: The Long-Document Transformer(arXiv:2004.05150)
- Big Bird: Transformers for Longer Sequences(arXiv:2007.14062)
- H2O: Heavy-Hitter Oracle for Efficient Generative Inference of Large Language Models(arXiv:2306.14048)
- DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models(GRPO 原始论文,arXiv:2402.03300)
- DAPO: An Open-Source LLM Reinforcement Learning System at Scale(arXiv:2503.14476)
- Qwen3 Technical Report(arXiv:2505.09388)
- FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning(arXiv:2307.08691)
- FlexAttention: The Flexibility of PyTorch with the Performance of FlashAttention(arXiv:2412.05496)
- Why stacking sliding windows can’t see very far(Guangxuan Xiao 博客,滑窗感受野分析)
- The Markovian Thinker(Delethink,arXiv:2510.06557)
- Extending the Context of Pretrained LLMs by Dropping their Positional Embeddings(DroPE,arXiv:2512.12167)
- prefixsliding/evals(论文评估结果数据集)
- prefixsliding/train_v6_filtered(RL 训练数据集)
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分内容可能已过时
评论区
分享你的想法,与大家交流讨论
音乐
暂未播放



