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 核心创新

  1. Pre-speculation:在验证期间,draft模型预测可能的验证结果
  2. Saguaro Cache:存储预先生成的token序列
  3. 即时返回:如果预测命中,立即返回,无等待延迟

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] = result

3.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_probs

3.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_ACCEPT

4. 算法流程: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.bonus

4.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 generated

5. 理论分析

5.1 无损性保证

定理(SSD分布保真)

SSD在期望意义上保持与标准SD相同的输出分布。

证明概要

  1. SSD等价于在Saguaro Sampling分布下执行标准SD
  2. Saguaro Sampling的边缘分布与target分布一致
  3. 因此最终输出分布与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 Decoding50 tok/s1.0x
Llama-3.1-70B (TP=4)Standard SD165 tok/s3.3x
Llama-3.1-70B (TP=4)Saguaro SSD250 tok/s5.0x

6.2 与基线对比

在多种任务上的平均性能:

任务类型Standard SDSaguaro SSD提升
数学推理142 tok/s185 tok/s+30%
代码生成138 tok/s178 tok/s+29%
对话生成156 tok/s203 tok/s+30%
摘要生成148 tok/s192 tok/s+30%

6.3 不同批量大小下的表现

批量大小Standard SDSaguaro SSD提升
1180 tok/s235 tok/s+31%
8420 tok/s510 tok/s+21%
16680 tok/s790 tok/s+16%
32950 tok/s1050 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 SDHSDSaguaro SSD
核心创新Draft+Verify分层验证并行推测
无损性
硬件需求分离设备分离设备分离设备
主要收益接受率接受率消除延迟
集成复杂度
适用场景通用高接受率需求大批量推理

9. 总结

主要贡献

  1. 消除串行依赖:首次实现draft与verify的真正并行执行

  2. Saguaro框架:系统性地解决预测、权衡、fallback三大挑战

  3. 显著加速:平均比最强SD基线快30%,比AR解码快5倍

  4. Pareto最优:在throughput-latency权衡曲线上达到更优边界

适用场景

  • 大批量LLM推理部署
  • 有分离硬件资源的场景
  • 对延迟和吞吐量都有较高要求的应用

参考文献