Speculative Speculative Decoding (Saguaro)
概述
Speculative Speculative Decoding (SSD) 是由斯坦福大学Tri Dao团队于2026年提出的创新方法,旨在消除标准Speculative Decoding中draft与verification之间的串行依赖瓶颈。该工作实现了draft模型与target模型验证的真正并行执行。
论文信息:
- 标题:Speculative Speculative Decoding
- 作者:Tanishq Kumar, Tri Dao, Avner May(Stanford, Together AI)
- arXiv: 2603.03251
- 会议:ICLR 2026
- GitHub: ssd
1. 问题背景
1.1 标准Speculative Decoding的瓶颈
即使draft模型与target模型在不同硬件上,标准SD仍存在内在的串行依赖:
标准SD时间线:
┌─────────────────────────────────────────────────────────────┐
│ Draft 1: [====] → Verify 1: [====] → Draft 2: [====] │
│ ↑ │
│ 必须等待 │
└─────────────────────────────────────────────────────────────┘
问题:Verifier必须等待draft完成才能开始验证
1.2 问题量化
设:
- :Draft模型推理时间
- :Target模型验证时间
- :纯自回归解码时间
加速比上界:
当时,加速比被限制在
1.3 关键观察
“能否在验证进行期间,提前准备下一个推测?“
2. 核心思想:并行推测
2.1 思想来源
SSD的核心灵感来自分支预测(Branch Prediction):
- CPU在分支结果出来前就预测并提前执行
- 如果预测正确,节省等待时间
- 如果预测错误,使用备用路径
2.2 SSD的工作原理
SSD时间线(理想情况):
┌─────────────────────────────────────────────────────────────┐
│ Draft 1: [===================] │
│ ↗ 生成多个可能的验证结果 │
│ Verify 1: [==========]──────────────────────────── │
│ ↑ 期间draft预先生成 │
│ Result: [====✓====]──────────────────────────── │
│ 命中缓存,立即返回 │
└─────────────────────────────────────────────────────────────┘
2.3 核心创新
- Pre-speculation:在验证期间,draft模型预测可能的验证结果
- Saguaro Cache:存储预先生成的token序列
- 即时返回:如果预测命中,立即返回,无等待延迟
3. 三大挑战与解决方案
3.1 挑战1:预测验证结果
问题:不仅需要预测接受多少token,还需预测bonus token
验证结果定义:
@dataclass
class VerificationOutcome:
k: int # 接受的token数量
bonus: int # bonus token
tokens: list # 被接受的token序列解决方案:Saguaro Cache
class SaguaroCache:
"""
使用draft logits预测bonus token
"""
def __init__(self):
self.cache = {}
def predict_bonus(
self,
draft_logits: torch.Tensor,
target_logits: torch.Tensor
) -> int:
"""
预测bonus token
"""
# 使用draft logits的top-k预测bonus
top_k = torch.topk(draft_logits, k=3, dim=-1)
# 假设bonus在draft最可能的token中
# 准确率可达90%
predicted_bonus = top_k.indices[0].item()
return predicted_bonus
def lookup(
self,
cache_key: str
) -> Optional[PreSpeculatedResult]:
"""查找预先生成的结果"""
return self.cache.get(cache_key)
def store(
self,
cache_key: str,
result: PreSpeculatedResult
):
"""存储预先生成的结果"""
self.cache[cache_key] = result3.2 挑战2:接受率与预测准确率的权衡
问题:
- 高接受率的draft → 宽预测集合 → bonus预测困难
- 低接受率的draft → 窄预测集合 → 预测准确但加速少
解决方案:Saguaro Sampling
提出新的采样分布平衡二者:
直觉理解:
def saguaro_sampling(draft_probs, residual_probs):
"""
Saguaro采样:平衡接受率和预测准确性
- draft_probs高 → 容易接受
- residual_probs高 → bonus概率大
几何平均平衡两者
"""
saguaro_probs = torch.sqrt(
draft_probs * residual_probs.clamp(min=1e-8)
)
saguaro_probs = saguaro_probs / saguaro_probs.sum()
return saguaro_probs3.3 挑战3:Cache Miss处理
问题:预测失败时的fallback策略
分析:
| 批量大小 | Miss率 | 最优策略 |
|---|---|---|
| 1 | 低 | 立即重新speculate |
| 8-16 | 中等 | 批量重新speculate |
| >32 | 高 | 使用partial结果 |
解决方案:自适应Fallback
def adaptive_fallback(
miss_info: MissInfo,
batch_size: int
) -> FallbackStrategy:
"""
根据批量大小自适应选择fallback策略
"""
if batch_size == 1:
return FallbackStrategy.IMMEDIATE_RETRY
elif batch_size <= 16:
return FallbackStrategy.BATCHED_RETRY
else:
return FallbackStrategy.PARTIAL_ACCEPT4. 算法流程:Saguaro
4.1 完整算法
class SaguaroSSD:
"""
Saguaro: Optimized Speculative Speculative Decoding
"""
def __init__(
self,
draft_model,
target_model,
draft_device: str = 'cuda:0',
target_device: str = 'cuda:1'
):
self.draft = draft_model
self.target = target_model
self.draft_device = draft_device
self.target_device = target_device
self.cache = SaguaroCache()
self.saguaro_stream = torch.cuda.Stream(device=draft_device)
def forward_round(
self,
input_ids: torch.Tensor,
max_lookahead: int = 8
) -> Tuple[torch.Tensor, int]:
"""
SSD一轮执行
"""
# === Phase 1: 并行启动 ===
with torch.cuda.stream(self.saguaro_stream):
# Draft模型生成(异步)
draft_output = self._async_draft(
input_ids,
max_lookahead
)
# === Phase 2: Target验证(同步) ===
with torch.no_grad():
target_output = self.target(input_ids)
target_logits = target_output.logits[:, -1]
# === Phase 3: 检查缓存 ===
cache_key = self._compute_cache_key(input_ids)
cached = self.cache.lookup(cache_key)
if cached and self._verify_hit(cached, target_logits):
# Cache命中!立即返回
return cached.tokens, cached.bonus
# === Phase 4: 标准验证 ===
accepted, bonus = self._standard_verify(
input_ids,
draft_output.tokens,
target_logits
)
# === Phase 5: 更新缓存 ===
self.cache.store(
cache_key,
PreSpeculatedResult(
tokens=accepted,
bonus=bonus,
draft_logits=draft_output.logits
)
)
return accepted, bonus
def _async_draft(
self,
input_ids: torch.Tensor,
max_length: int
) -> DraftOutput:
"""
异步draft生成
"""
# 等待上一个验证完成
torch.cuda.current_stream(self.draft_device).wait_stream(
torch.cuda.current_stream(self.target_device)
)
with torch.no_grad():
draft_output = self.draft.generate(
input_ids,
max_length=max_length,
return_dict=True
)
return draft_output
def _verify_hit(
self,
cached: PreSpeculatedResult,
target_logits: torch.Tensor
) -> bool:
"""
验证缓存命中
"""
# 检查bonus是否匹配
predicted_bonus = self.cache.predict_bonus(
cached.draft_logits[-1],
target_logits
)
return predicted_bonus == cached.bonus4.2 多轮生成
def generate(
self,
input_ids: torch.Tensor,
max_new_tokens: int = 100,
eos_token_id: int = 2
) -> torch.Tensor:
"""
使用Saguaro SSD进行完整生成
"""
generated = input_ids.clone()
round_count = 0
while generated.shape[1] - input_ids.shape[1] < max_new_tokens:
# 单轮执行
accepted, bonus = self.forward_round(
generated,
max_lookahead=8
)
# 追加结果
if accepted.numel() > 0:
generated = torch.cat([generated, accepted.unsqueeze(0)], dim=1)
generated = torch.cat([generated, bonus.unsqueeze(0).unsqueeze(0)], dim=1)
# 检查终止
if bonus == eos_token_id:
break
round_count += 1
if round_count > max_new_tokens // 4: # 防止无限循环
break
return generated5. 理论分析
5.1 无损性保证
定理(SSD分布保真):
SSD在期望意义上保持与标准SD相同的输出分布。
证明概要:
- SSD等价于在Saguaro Sampling分布下执行标准SD
- Saguaro Sampling的边缘分布与target分布一致
- 因此最终输出分布与target模型相同
5.2 加速比分析
预期加速比:
其中是Saguaro Cache命中率。
理想情况():
即完全消除draft开销!
5.3 Cache大小设计
def optimal_cache_size(
batch_size: int,
hit_rate: float,
memory_limit: float
) -> int:
"""
确定最优缓存大小
"""
# 经验公式
base_size = 16
# 批量越大,需要更多缓存条目
batch_factor = int(np.log2(batch_size + 1))
# 命中率高时增加缓存
hit_factor = int(hit_rate * 4)
size = base_size + batch_factor + hit_factor
# 考虑内存限制
return min(size, int(memory_limit / (1024 * 1024)))6. 实验结果
6.1 端到端性能
| 配置 | 方法 | 速度 | 加速比 |
|---|---|---|---|
| Llama-3.1-70B (TP=4) | AR Decoding | 50 tok/s | 1.0x |
| Llama-3.1-70B (TP=4) | Standard SD | 165 tok/s | 3.3x |
| Llama-3.1-70B (TP=4) | Saguaro SSD | 250 tok/s | 5.0x |
6.2 与基线对比
在多种任务上的平均性能:
| 任务类型 | Standard SD | Saguaro SSD | 提升 |
|---|---|---|---|
| 数学推理 | 142 tok/s | 185 tok/s | +30% |
| 代码生成 | 138 tok/s | 178 tok/s | +29% |
| 对话生成 | 156 tok/s | 203 tok/s | +30% |
| 摘要生成 | 148 tok/s | 192 tok/s | +30% |
6.3 不同批量大小下的表现
| 批量大小 | Standard SD | Saguaro SSD | 提升 |
|---|---|---|---|
| 1 | 180 tok/s | 235 tok/s | +31% |
| 8 | 420 tok/s | 510 tok/s | +21% |
| 16 | 680 tok/s | 790 tok/s | +16% |
| 32 | 950 tok/s | 1050 tok/s | +11% |
7. 硬件要求与部署
7.1 硬件配置
SSD需要分离的硬件运行draft和target模型:
推荐配置:
Target Model:
- GPU: A100/H100 (80GB)
- 用途: 大模型推理验证
Draft Model:
- GPU: A10/H20 或更小
- 用途: 快速draft生成7.2 通信优化
class OptimizedComm:
"""
Draft与Target间的通信优化
"""
def __init__(self):
self.p2p_enabled = True
def transfer_kv_cache(
self,
from_device: str,
to_device: str,
kv_cache: Tuple[torch.Tensor, torch.Tensor]
) -> None:
"""
利用GPU P2P进行高效KV缓存传输
"""
if self.p2p_enabled:
# 使用CUDA P2P拷贝
k_cache, v_cache = kv_cache
k_out = k_cache.to(to_device, non_blocking=True)
v_out = v_cache.to(to_device, non_blocking=True)
else:
# 回退到主机拷贝
k_out = kv_cache[0].cpu().to(to_device)
v_out = kv_cache[1].cpu().to(to_device)8. 与其他方法的对比
| 特性 | Standard SD | HSD | Saguaro SSD |
|---|---|---|---|
| 核心创新 | Draft+Verify | 分层验证 | 并行推测 |
| 无损性 | ✓ | ✓ | ✓ |
| 硬件需求 | 分离设备 | 分离设备 | 分离设备 |
| 主要收益 | 接受率 | 接受率 | 消除延迟 |
| 集成复杂度 | 低 | 中 | 高 |
| 适用场景 | 通用 | 高接受率需求 | 大批量推理 |
9. 总结
主要贡献
-
消除串行依赖:首次实现draft与verify的真正并行执行
-
Saguaro框架:系统性地解决预测、权衡、fallback三大挑战
-
显著加速:平均比最强SD基线快30%,比AR解码快5倍
-
Pareto最优:在throughput-latency权衡曲线上达到更优边界
适用场景
- 大批量LLM推理部署
- 有分离硬件资源的场景
- 对延迟和吞吐量都有较高要求的应用