SRT: Speculative Rollout with Tree-Structured Cache

Cornell · UIUC · Tsinghua · UW · ByteDance · Chang & Zhu et al. · NeurIPS 2025 Workshop · 2026-01-14 · arXiv:2601.09083
关键词: RL post-training · speculative decoding · model-free draft · per-prompt token tree · run-ahead generation · GRPO/DAPO/PPO/ReTool

速读卡片 (TL;DR)

一句话:同一个 prompt 在不同训练 epoch、不同 group rollout 之间产出的 response 高度相似 — SRT 把这些历史 token 序列存成每个 prompt 一棵的 token 树,用它充当 model-free draft 给当前 policy 做 speculative decoding,免训练、零损失、即插即用。

2.08×
rollout 端到端最高加速
0
draft model 参数量 (model-free)
~70%
RL step 中 generation 占比

立场:SRT 不需要训 draft、不动 sampling 分布、不破坏 on-policy。它把"加速"交给一棵 CPU 内存里的 token 树 + 两条把树喂饱的策略 (on-the-fly insert + run-ahead during bubbles)。


1 · 动机

1.1 历史脉络:rollout 怎么从"小头"变成"大头"

2024 年之前的 RLHF:reward model + PPO,response 长度多在 256–512 token,generation 在一个 RL step 里只占 30–40%,大家把功夫花在 critic loss、reference model KL、replay buffer 上。

2025 年起 reasoning 模型起飞 (DeepSeek-R1, GLM-4.5, Kimi-1.5, Seed1.5-Thinking, Qwen3),trajectory 长度普遍 4k–32k token,GRPO/DAPO 还要求每个 prompt 同时采 5–16 个 response。论文 Figure 2a 给出的硬数据:在 PPO/GRPO/DAPO/ReTool 四个算法上,rollout 平均吃掉 65% (有时 >70%) 的 RL step 时间。这就是 Amdahl 视角下"必须被攻击的部分"。

更糟的是 long-tail 问题:

batch 内 K 条 rollout (DAPO, K=16, 仅画 8 条): EOS @ 1k EOS @ 1.3k EOS @ 1.6k EOS @ 1.7k EOS @ 1.9k EOS @ 2.1k tail @ 4k tail @ 8k ↓ GPU bubble: 6/8 rollout 早结束,GPU 在等长尾 ↓
典型 GRPO/DAPO batch 的 rollout 时间分布:大半 GPU 在 bubble 里等长尾。SRT 的 run-ahead 策略就是来填这块 bubble 的(把空闲算力用来"预热"下一批 prompt 的 cache)。

1.2 别人的招数为什么"够呛"

加速手段动了什么代价
Async RL (AReaL, PipelineRL)off-policy: 让 generation 滞后 learner k 步policy lag → 收敛性可能受损
Selective rollout (GRESO, Act-Only-When-it-Pays)跳过没信息量的 prompt改了训练分布
FP8 / 量化 rollout降精度生成rollout 与 train 分布 mismatch
EAGLE-3 (NVIDIA NeMo-RL)训一个轻量 draft head 复用 hiddenlossless · 需独立 offline init + 维护对齐
SuffixDecodingretrieval-based draft, 把全语料后缀当 draftmodel-free · 未利用 RL 跨 epoch / 跨 group 的同一 prompt 高重叠性
RhymeRL (并行工作)缓存上一 epoch 的完整 response, offline async 更新first-time prompt cold-start;cache 与本步 in-flight 状态脱节
SRT (本文)per-prompt token tree + on-the-fly insert + run-aheadlossless · on-policy · model-free · 无 cold-start

1.3 为什么 tree-structured cache 是"非平凡的解锁"

把"用历史 rollout 当 draft"这件事说出来不难,真正非平凡的是怎么存、怎么查、怎么填。考察以下三个观察:

  1. 跨 epoch 相似度高,但不是 verbatim 重复。Figure 2c 实测同一 prompt 在第 t 步和前 t-1 步累积 n-gram 的 overlap 随 epoch 单调上升,后期可达 60%+。意味着子串命中率高,但整段 response 命中率低 — 必须以"路径"为单位检索,不能以"句子"为单位。
  2. GRPO/DAPO 同一 prompt 一次产 K=5–16 个 rollout。这 K 条彼此当下就高度相似 (相同 prompt + 相同 policy + 不同 sample,只差 stochastic decoding)。如果只在 epoch 边界更新 cache,会错过同一 batch 内这 K-1 条对第 K 条的帮助 — 这正是 RhymeRL 的盲区。
  3. Long-tail bubble 是免费算力。Run-ahead generation 不会增加 wall-clock(它本来就 idle),但能给即将出现的 prompt 把 cache 提前烧热,消除 first-time cold-start。

把这三个观察落到数据结构上,Trie / token tree 是几乎唯一合理的选择:

"tree-structured" 的真实含义. 注意:这棵树不是 KV cache 的拓扑,也不是 SpecInfer 风格的 draft token tree (那个是验证拓扑)。SRT 的树是per-prompt 的 token-frequency trie,存活在 CPU 主存里,跨训练 step 持续生长。它和 GPU 上的 paged KV cache 完全解耦。

2 · 背景速查

术语含义
SRTSpeculative Rollout with Tree-structured cache (本文)
Per-prompt tree T_pSRT 为每个 prompt p 维护的一棵 token-frequency trie,存所有历史 rollout 的 token 子序列
Model-free draft不需要任何参数化模型,直接从 cache 取候选 token
On-the-fly insert当前 batch 的 rollout 一边生成一边把 token 插进 T_p
Run-ahead generation当 batch 大半 rollout 已 EOS,用空闲 GPU 给"未来 batch"的 prompt 提前生成 partial rollout 喂 cache
Verifier (target)当前 RL policy π_θ,负责验证 draft tokens 是否被接受
SuffixDecodingOliaro et al. 2025: 用全局后缀树做 model-free draft,本文主要 baseline 之一
RhymeRLHe et al. 2025: 并行工作,只用上一 epoch 完整 response offline 更新 cache
N-gram draftingYang et al. 2023 (LLMA): 把全语料 n-gram 当 draft,本文另一 baseline
DAPOByteDance 数学 RL 算法,K=16/prompt,max_response=8k
ReToolFeng et al. 2025: 多轮 tool-use RL,max_turns=8, max_response=16k
VerlHybridFlow 的开源 RLHF 框架 (Sheng et al. 2025),本文集成进去

2.1 标准 speculative decoding 复习 (一行)

Draft 给 k 个 token → target 一次并行验证 k+1 个位置 → rejection sampling (Leviathan 2023) 保证输出分布与直接从 target 采样等价。SRT 只是把"draft 模型"换成"从 token tree 沿路径抽 token"。


3 · 核心机制:per-prompt token tree + 路径打分

3.1 数据结构

对每个 prompt p,维护一棵 trie T_p:

Prompt p = "Q: 1+1=?" → 树 T_p root "The" c=12 "Let" c=8 "Ans" c=2 "answer" c=10 "sum" c=2 "me" c=8 "is" c=9 "=" c=1 "2" ★ 当前 generation 已产出 "The answer"; 在 T_p 中匹配到节点 "answer"; 贪心抽 "is" (c=9 / 11 = 0.82); 继续抽 "2" → draft = ["is", "2"] C(v) = count(v)/Σ count(siblings) 路径分 = Π C(v) 沿路
Per-prompt tree T_p 的具体形状。蓝节点 = depth-1, 黄节点 = depth-2, 绿节点 = depth-≥3 (用作 draft 候选)。蓝色 root 是空 prefix。SRT 当前在 prompt p 上生成到 "The answer", 沿树走匹配,从节点 "answer" 出发贪心采 "is"→"2" 作为 draft 提议给 verifier 验证。

3.2 查询过程 (drafting)

当前 partial continuation 是 y₁:t,SRT 这样查 T_p:

  1. 最长后缀匹配:从 y_t 往前回溯,找最长的 q 使得 y_{t-q+1:t} 是 T_p 上某条路径。这一步决定 "我们站在树的哪个节点 u_q"
  2. 如果连 q=1 都没匹配上 (e.g. 前所未见的 token),退化为正常 1-step 自回归,下一步再试
  3. 贪心扩展:从 u_q 出发,反复挑选 conditional 概率最高的子节点 v,即:
    C(v) = count(v) / Σ_{w ∈ children(parent(v))} count(w)
    路径分 = 沿路 C 的乘积 (类似 beam 但只保留 top-1)
  4. 扩展到预算 B(q) 用尽 (depth 限制 / 或路径分阈值) — 论文没给精确公式,但实现里通常是 fixed budget = 几十个 token
  5. 把这一束 draft tokens 一次性丢给 verifier (current policy) 做 spec decoding 标准验证

3.3 验证过程 (一次 forward, classical spec decoding)

Verifier (current π_θ) 把 [y₁:t, t̂₁, t̂₂, ..., t̂_K] 全部一次 forward,得到每个位置的 p_target。然后:

这里有个 subtle 点:p_draft 是什么? SRT 的 draft 来自 cache 频率,不是一个真正的 distribution。论文 (隐含) 用 C(v) 作为 p_draft;这样 rejection sampling 的数学保证依然成立 — 因为 rejection sampling 只要求 p_draft 是某个合法分布,无所谓它有多差 (差只意味着接受率低,不意味着输出分布偏)。这就是"lossless"成立的关键

反向论证:如果不做 rejection sampling,直接把 cache 里频率最高的 token 当输出 — 那 SRT 就退化成"非分布等价"的近似采样,会改 GRPO 的训练分布,违反 on-policy。所以 "tree-cache 提议 + rejection sampling 验证" 这两件事必须捆绑。

4 · Cache 维护:on-the-fly + run-ahead

树本身只是数据结构;怎么把树喂饱才是 SRT 的真正贡献。这一节讲两条互补的 maintenance 策略,以及它们各自解决什么。

4.1 On-the-fly insert (跨 group 内即时受益)

当前 batch 的 K 条 rollout 在生成的同时就把 token 流式插进 T_p:

Prompt p, GRPO K=5 同时采样 rollout 1: 已完成 EOS → 全部插进 T_p rollout 2: 已完成 → 插进 T_p rollout 3: 生成中 rollout 4: 生成中 rollout 5: 才开始 on-the-fly stream insert T_p (cache) rollout 4/5 立刻受益 从 1/2/3 学到的 prefix RhymeRL 等到 epoch 结束才更新 → rollout 4/5 拿不到来自 1/2/3 的 hint
GRPO/DAPO 一个 prompt 同时跑 K 条 rollout。先 EOS 的 rollout 1, 2 直接把整段 token 流插进 T_p;同 batch 还在生成的 rollout 3, 4, 5 立刻可以从这棵被刚刚喂饱的树里抽 draft。RhymeRL 错过的就是这个"同 batch 内"的快速反馈通道。

4.2 Run-ahead generation (用 long-tail bubble 预热未来)

另一条:当 batch 内只剩零星 long-tail rollout 还在跑、其它 GPU 已经空,SRT 不让 GPU 闲着,而是从 data loader 的 lookahead 窗口 / active prompt queue 里取下一个待训练的 prompt p',对它跑一段 partial generation:

step t (DAPO batch) long-tail rollout ↓ GPU bubble 同时:run-ahead 给未来 prompt 预热 partial gen for prompt p' (next batch) → 插进 T_p' step t+1 真正轮到 p' 时,T_p' 已不是空树 → 消除 cold-start
Run-ahead 的本质是"把 long-tail bubble 这块免费算力转移到未来 prompt 的 cache 预热"。这些 token 不计入训练梯度,只生只填。代价:可能多用一点 KV cache (但反正 GPU 闲着) + 一些调度复杂度。

论文 §4.2 给出消融:enable run-ahead 后 mean accepted tokens / step 进一步上升,而单独 history-only baseline (RhymeRL 风格) 由于 cold-start 在前几个 epoch 几乎没收益。


5 · Worked example:一个 prompt 跨多 step 的完整生命周期

把上面所有机制串起来,我们追踪一个具体 prompt p = "Q: 求 ∫x² dx" 在三步 RL 训练中 cache 是怎么生长、查询、命中的。

Step t=1 (cold start)

Step t=2 (on-the-fly 受益)

Step t=2.5 (run-ahead 预热)

Step t=3 (持续累积)

关键张量/形状细节(展开)

6 · 实验关键结果

6.1 主表 (Table 1, Qwen2.5-1.5B, on-policy)

MethodStep time (s) ↓Generation time (s) ↓
PPOGRPODAPOReToolPPOGRPODAPOReTool
Baseline47.542.981.774.831.531.844.149.0
N-gram47.342.184.569.331.431.146.045.0
SuffixDecoding35.930.710369.218.419.762.538.8
SRT31.526.268.758.815.215.431.528.7

读法重点:

6.2 Ablation: K 越大 SRT 越甜 (Table 3)

GRPO settingBaseline genSRT genimprovement
n=5 responses/prompt31.815.421.8% (gen)
n=10 responses/prompt47.120.433.1% (gen)

这是 SRT 论点的核心实验性证据:每个 prompt 同时采的 rollout 越多,on-the-fly insert 提供的早期 hint 越多 → 后产生的 rollout 命中率越高。意味着 DAPO (K=16) / GSPO (K=64) 这种"高分组数"的现代算法上 SRT 收益会进一步放大。

6.3 Ablation: run-ahead 的边际收益 (Figure 5)

论文用仿真比较:


7 · 与同类工作对比

工作Draft 来源Cache 更新损失/语义定位
SRT (本文)per-prompt token tree (model-free)on-the-fly + run-aheadlossless, on-policyRL rollout 专属
RhymeRL (并行工作)per-prompt 历史 response仅 epoch 末 offline 更新losslessRL rollout, 但有 cold-start
SuffixDecoding (Oliaro 2025)全局后缀树静态/语料级lossless通用 inference,非 RL
N-gram / LLMA (Yang 2023)全语料 n-gram retrieval静态lossless通用 inference
SPEC-RL训一个 EAGLE-style draftRL 训练时同步更新 draftlosslessRL rollout, 带参 draft
ReSpecEAGLE draft + reward-weighted adaptationonline + reward-awarelosslessRL rollout, 自适应 k/draft
NVIDIA NeMo-RL × EAGLE-3EAGLE-3 draft headoffline init + 可选 online detach 更新lossless, verifier-exact生产 RL,32→2048 GPU
DAS动态/自适应 draft 配置losslessRL rollout 调度

SRT 在这条线里的独特位置

互补,不互斥. 理论上可以把 SRT 的 tree-cache 作为 EAGLE-3 draft head 之上的 retrieval-augmentation,先查 cache 命中长 prefix,失败再 fallback 到 EAGLE-3 head。论文没做这个组合,但工程上是显然的下一步。

8 · 局限 / 个人 take / 待验证问题

论文承认的局限 / 我观察到的

我的疑问 (落地后想验证)

  1. Cache 漂移问题:RL 训到几百 step 后 policy 已经显著演变,早期插入的 token 可能"过期"。SRT 用 frequency 但没有 recency decay。在 long horizon RL (1k+ step) 上 acceptance 是否会衰退?
  2. 探索 vs 利用矛盾:如果 cache 里某条路径出现频率极高,SRT 会不断提议它 — 而 rejection sampling 虽然分布等价,但方差不一样;高方差 prompt 上 effective sample 数会变少,对 GRPO 的 advantage 估计可能引入偏置 (虽不破坏期望)。
  3. 多轮 (ReTool) 场景下,树是 per-prompt 还是 per-(prompt, turn-history)?论文没说清楚。如果 per-prompt 但 turn 之间状态差异大,cache 会很乱。
  4. 与 EAGLE-3 / ReSpec 组合:cache 命中率高时直接取,低时 fallback 到 EAGLE 模型。系统上是否值得这复杂度?ablation 缺位。
  5. Run-ahead 的"未来 prompt"可能已经不是 next batch 的 prompt:大规模分布式训练里,sampler 从全局打乱,run-ahead 怎么准确预测?
  6. KV cache 与 paged-attention 的交互:verify 一束 draft tokens 时,K+1 个位置一起进 attention,在 vLLM 的 page table 里要预留连续 page。SRT 在 long-context (32k+) 下的 OOM 风险?

记忆点

立场 RL rollout 的 redundancy 是金矿;tree-cache + on-the-fly + run-ahead 是不需要训 draft 的 lossless 加速
数据结构 per-prompt token-frequency trie,CPU 内存,跨 step 持续生长
两条 maintenance on-the-fly (跨 K rollout 即时受益) + run-ahead (用 long-tail bubble 预热未来)
公式 draft 路径分 = Π C(v) = Π count(v)/Σ count(siblings)
实测 Qwen2.5-1.5B, GRPO/DAPO/PPO/ReTool, gen 时间 1.5–2.08× 加速
甜区 K 大 (DAPO K=16) + prompt 多次重复 + 长 response
死穴 prompt 极多样 / no replay / 短 response → cache 喂不饱

精读笔记 v1 · 2026-05-07 · 配套论文 PDF 在 /data/szhang967/papers/spec-rl/SRT_2601.09083.pdf