Tencent AngelSpec Speculative Decoding Framework Deep Dive: MTP + Block Diffusion Dual-Draft Strategy and D-cut High-Concurrency Throughput Optimization
1. Introduction: Inference Cost — The True Bottleneck of LLM Deployment
On July 29, 2026, Tencent’s Hunyuan team open-sourced AngelSpec — a full-stack speculative decoding framework covering drafter training, architecture design, and production deployment. The release also includes Hy3-A21B MTP and DFly drafter weights with training code.
AngelSpec’s core insight: no single drafter architecture optimally serves all workloads. The high-entropy nature of conversation and the structured nature of code/math demand fundamentally different speculative decoding strategies. Hence AngelSpec adopts a dual-drafter approach — MTP for high-entropy conversation, DFly for structured code/math — with D-cut dynamic pruning to push throughput further under high concurrency.
2. Theoretical Foundation of Speculative Decoding
2.1 Problem Formulation
The speedup of speculative decoding depends on two factors:
- Accepted Length: tokens accepted by the target model per draft
- Verification Cost: computation cost of parallel verification
$$Speedup = \frac{L}{1 + \frac{T_{draft} \cdot L}{T_{target}} + \frac{1}{1-\alpha}}$$
2.2 The Necessity of Dual-Draft Strategy
import numpy as np
class WorkloadAnalyzer:
"""Quantifies why different workloads need different draft strategies"""
def __init__(self):
self.profiles = {
'conversation': {'entropy': 0.85,
'rates': [0.81, 0.66, 0.52, 0.39, 0.29, 0.22, 0.17]},
'code': {'entropy': 0.35,
'rates': [0.92, 0.85, 0.78, 0.72, 0.66, 0.61, 0.57]},
'math': {'entropy': 0.40,
'rates': [0.90, 0.82, 0.74, 0.67, 0.60, 0.54, 0.49]}
}
def analyze(self):
print("Workload Analysis for Draft Strategy Selection:")
for name, prof in self.profiles.items():
mtp_speedup = self._mtp_speedup(prof['rates'])
block_speedup = self._block_speedup(prof['rates'])
print(f" {name}: entropy={prof['entropy']:.2f}")
print(f" MTP optimal speedup: {max(mtp_speedup):.2f}x")
print(f" Block optimal speedup: {max(block_speedup):.2f}x")
print(f" Best strategy: {'MTP' if max(mtp_speedup) > max(block_speedup) else 'Block'}")
def _mtp_speedup(self, rates):
return [sum(rates[:L]) / (L * 0.1 + 1.0) for L in range(1, len(rates) + 1)]
def _block_speedup(self, rates):
return [sum(rates[:L]) / (0.1 + 1.0) for L in range(1, len(rates) + 1)]
WorkloadAnalyzer().analyze()
3. Core Architecture
3.1 MTP + TTT: Resolving Train-Inference Mismatch
Traditional MTP training uses teacher forcing — feeding ground-truth tokens at each step. But during inference, the drafter must consume its own predictions recursively. This mismatch causes deep-position acceptance rates to collapse.
AngelSpec’s TTT (Training-Time Test) solution: during training, autoregressively unroll the shared MTP block where depth k+1 receives the argmax prediction from depth k, not the ground-truth token.
import torch
import torch.nn as nn
import torch.nn.functional as F
class MTPWithTTT(nn.Module):
"""MTP with Training-Time Test — eliminating train-inference mismatch"""
def __init__(self, hidden_dim=4096, vocab_size=128000, num_depths=3):
super().__init__()
self.num_depths = num_depths
self.mtp_block = nn.Sequential(
nn.Linear(hidden_dim * 2, hidden_dim),
nn.LayerNorm(hidden_dim),
nn.GELU(),
nn.Linear(hidden_dim, hidden_dim),
nn.LayerNorm(hidden_dim),
nn.GELU(),
nn.Linear(hidden_dim, vocab_size)
)
def forward(self, hidden_states: torch.Tensor, embedding_matrix: torch.Tensor):
"""TTT forward: autoregressive unrolling during training"""
batch_size = hidden_states.shape[0]
current_hidden = hidden_states[:, -1:, :].detach()
draft_logits = []
for depth in range(self.num_depths):
if depth == 0:
combined = torch.cat([current_hidden,
torch.zeros_like(current_hidden)], dim=-1)
else:
prev_token = draft_logits[-1].argmax(dim=-1)
token_embed = F.embedding(prev_token, embedding_matrix)
combined = torch.cat([current_hidden, token_embed], dim=-1)
logits = self.mtp_block(combined)
draft_logits.append(logits)
return torch.cat(draft_logits, dim=1)
def compute_loss(self, hidden_states, target_tokens, embedding_matrix):
draft_logits = self.forward(hidden_states, embedding_matrix)
targets = target_tokens[:, :self.num_depths]
loss = sum(F.cross_entropy(draft_logits[:, d, :], targets[:, d])
for d in range(self.num_depths))
return loss / self.num_depths
3.2 DFly: Block Diffusion Architecture
DFly’s three innovations:
- Parallel diffusion backbone — generates all block positions simultaneously
- Correction head — lightweight autoregressive refinement per position
- TV loss training — directly optimizes expected acceptance length
class DFlyDrafter(nn.Module):
"""Block diffusion drafter with parallel generation + correction head"""
def __init__(self, hidden_dim=4096, vocab_size=128000, block_size=8):
super().__init__()
self.block_size = block_size
self.diffusion = nn.TransformerEncoder(
nn.TransformerEncoderLayer(hidden_dim, 32, batch_first=True), 6)
self.pos_embed = nn.Embedding(block_size, hidden_dim)
self.hidden_proj = nn.Linear(hidden_dim, hidden_dim)
self.output_proj = nn.Linear(hidden_dim, vocab_size)
self.correction = nn.Sequential(
nn.Linear(hidden_dim + vocab_size, hidden_dim),
nn.LayerNorm(hidden_dim), nn.GELU(),
nn.Linear(hidden_dim, vocab_size))
def forward(self, target_hidden: torch.Tensor):
B, D = target_hidden.shape
state = self.hidden_proj(target_hidden).unsqueeze(1)
state = state.expand(-1, self.block_size, -1)
state = state + self.pos_embed.weight.unsqueeze(0)
state = self.diffusion(state)
block_logits = self.output_proj(state) # [B, block_size, V]
corrected = []
for pos in range(self.block_size):
if pos == 0:
corrected.append(block_logits[:, 0, :])
else:
prev = F.one_hot(corrected[-1].argmax(dim=-1),
num_classes=block_logits.size(-1)).float()
corr = self.correction(torch.cat([state[:, pos, :], prev], dim=-1))
corrected.append(block_logits[:, pos, :] + corr)
return torch.stack(corrected, dim=1)
4. D-cut: Dynamic Verification Pruning
D-cut solves the core contradiction of speculative decoding under high concurrency: more requests mean less verification budget per request, but speculative decoding gains depend on sufficient verification depth.
class DCutScheduler:
"""Dynamic verification pruning for high-concurrency throughput"""
def __init__(self, max_verify=4096):
self.max_verify = max_verify
self.latency_table = {1: 1.0, 2: 1.2, 4: 1.5, 8: 2.0, 16: 3.0}
def schedule(self, requests: list) -> list:
candidates = []
for req in requests:
conf = req['confidence']
best = max(
((d, np.prod(conf[:d]) * d / self._cost(d))
for d in range(1, len(conf)+1)),
key=lambda x: x[1])
candidates.append({'id': req['id'], 'depth': best[0],
'benefit': best[1], 'cost': self._cost(best[0])})
candidates.sort(key=lambda x: x['benefit'], reverse=True)
scheduled, total = [], 0
for c in candidates:
if total + c['cost'] <= self.max_verify:
scheduled.append(c)
total += c['cost']
return scheduled
def _cost(self, depth):
depths = sorted(self.latency_table.keys())
if depth <= depths[0]: return self.latency_table[depths[0]]
if depth >= depths[-1]: return self.latency_table[depths[-1]]
for i in range(len(depths)-1):
if depths[i] <= depth <= depths[i+1]:
r = (depth - depths[i]) / (depths[i+1] - depths[i])
return self.latency_table[depths[i]] + r * (
self.latency_table[depths[i+1]] - self.latency_table[depths[i]])
return self.latency_table[depths[-1]]
5. Experimental Results
5.1 Acceptance Length Comparison
| Drafter | Math500 | GSM8K | HumanEval | MBPP | LiveCodeBench | MT-Bench | Avg |
|---|---|---|---|---|---|---|---|
| MTP | 3.53 | 3.56 | 3.33 | 3.22 | 3.25 | 2.57 | 3.24 |
| DFlash | 4.97 | 5.54 | 4.77 | 4.50 | 4.46 | 3.16 | 4.57 |
| DFly | 5.23 | 6.42 | 5.52 | 5.06 | 4.79 | 3.65 | 5.41 |
5.2 End-to-End Throughput (Hy3-A21B, TP=8)
| Concurrency | MTP | DFlash | DFly | DFly + D-cut |
|---|---|---|---|---|
| 4 | 1.32× | 1.75× | 1.98× | 1.95× |
| 8 | 1.28× | 1.70× | 2.05× | 2.10× |
| 16 | 1.22× | 1.62× | 2.15× | 2.25× |
| 32 | 1.15× | 1.50× | 2.25× | 2.40× |
| 64 | 1.08× | 1.35× | 2.20× | 2.40× |
6. Conclusion
AngelSpec represents a major milestone in speculative decoding:
- Dual-drafter strategy: MTP for conversation, DFly for code/math
- TTT training: 52.8% → 66.4% acceptance rate improvement
- DFly block diffusion: 1.6× acceptance length vs MTP
- D-cut dynamic pruning: +15.7% throughput under high concurrency
Reference: Tencent Hunyuan Team, “AngelSpec: A Unified Training Framework for Speculative Decoding”, arXiv:2607.25852, 2026. Code: https://github.com/Tencent/AngelSpec.