在 Ascend 910C 上,一轮投机解码会经过 Draft、Verify、Accept 和状态提交。Graph Replay 可以减少重复提交开销,多流可以重叠独立工作,但候选依赖、缓存准备和通信等待仍会约束下一步何时开始。
本文沿 DeepSeek-V4 的 EAGLE/NextN 路径标出这些依赖:图里捕获了哪些 forward,Plan Stream 能与谁重叠,DeepEP 的等待点在哪里。源码可以确定执行关系;哪一段成为实际关键路径,还要把设备时间线与这些关系对齐。
源码版本与阅读范围
源码基线:本文固定基于
sgl-project/sglang @ 5c69e32abe013fa1b913022682a3104c79105f37,核对日期为 2026-09-21。主线讨论 DeepSeek-V4 + EAGLE / NextN + Ascend 910C +dsv4backend。图中展示由 Stream、Event、Wait 和 Graph 调用推导的依赖关系;耗时与重叠比例由设备时间线测量。
一、Graph、Stream 与通信各自负责什么
Graph、Stream 与通信分别处理不同的执行成本。先看它们在一轮 EAGLE 中的位置:
表格可左右滑动 →
| 机制 | 本文对应的执行方式 |
|---|---|
| Draft Graph | 捕获完整 draft_forward(),执行 num_steps - 1 次 NextN Forward |
| Verify Graph | 按请求数选择档位,每请求保留固定 Verify 宽度 |
| Plan Stream | Verify Prep 等待 Draft;Draft-Extend Prep 可与已提交的主流尾部重叠 |
| Target 多流 | 重叠 Q、KV 与 Indexer 等独立工作;NextN 未接入这组 alt streams |
| DeepEP 异步通信 | 分开发起与等待,在中间安排独立计算 |
| Draft-Extend Graph | 用重复启动成本的节省,交换捕获与 workspace 内存开销 |
后文依次展开捕获宽度、等待位置和配置入口。
把六个结论放在一起,可以先得到一轮稳态 EAGLE 的骨架:
上一轮 confirmed state
│
▼
Draft Graph
(num_steps - 1 次 NextN forward)
│
▼
Verify Planning
│
▼
Target Verify Graph
│
▼
Accept / Commit
│
├──────── Draft-Extend Planning
│ 可与 main-stream tail overlap
▼
Draft Extend Forward / Graph
│
▼
下一轮 candidate1
优化问题因此变成:
每一个 Fork 最后在哪里 Join?哪条 branch 最长?哪些 communication / planning 已经被其他工作覆盖,哪些仍然暴露在 Critical Path 上?
二、Draft Graph 捕获哪些 NextN Forward
DeepSeek-V4 的 EAGLE Draft 在 NPU 上有专门 Runner:EAGLEDraftNpuGraphRunner
固定源码:
eagle_draft_npu_graph_runner.py
它继承通用 EAGLEDraftCudaGraphRunner,但在 NPU 路径上底层使用的是 torch.npu.NPUGraph。这里沿用历史 CUDA 类名,底层运行的是 NPU Graph。
从 Capture Body 可以数清捕获的 Forward 次数。
EAGLEDraftCudaGraphRunner.capture_one_shape() 最终捕获:
def run_once():
...
ret = self.eagle_worker.draft_forward(
forward_batch
)
...
return ret
也就是说,一次 Draft Graph Replay 包住的是整个 draft_forward(),而不是单独某一次 draft_runner.forward()。
固定源码:
EAGLEDraftCudaGraphRunner.capture_one_shape()
draft_forward() 的 NextN Forward 次数
主循环:
for i in range(self.speculative_num_steps):
...
select_top_k_tokens(...)
if i == self.speculative_num_steps - 1:
break
logits_output = self.draft_runner.forward(
forward_batch
).logits_output
固定入口:
EagleDraftWorker.draft_forward()
对应计数为:
NextN model forward count inside Draft Graph
=
speculative_num_steps - 1
例如官方常见:
num_steps = 2
topk = 1
num_draft_tokens = 3
这一轮 Draft Graph 实际是:
进入 Graph 前:
candidate1 已由上一轮 Draft Extend 准备好
Graph Replay:
candidate1
↓
1 × NextN forward
↓
candidate2
↓
organize draft result
不是两次 NextN forward。
如果 num_steps=3,则是:
candidate1
↓
NextN forward #1
↓
candidate2
↓
NextN forward #2
↓
candidate3
Graph Capture 覆盖完整 rollout,其中候选阶段数与模型 Forward 次数分别为:
candidate stage 数 = num_steps
真正的 NextN model forward 数 = num_steps - 1
第一枚 candidate 的成本属于上一轮 Draft Extend,而不是这一轮 Draft Graph。
Draft Graph 的静态宽度是什么
Draft Decode 的 per-request width 统一由:
resolve_num_tokens_per_req(
phase="draft_decode"
)
解析为:speculative_eagle_topk
固定源码:
因此当前官方 topk=1 时,一次 NextN Draft forward 的输入宽度是 1 token / request,但 Graph body 内会执行 num_steps-1 次 NextN forward。
这正是 Graph 对轻量 Draft 特别有价值的原因:
NextN compute 很轻
↓
固定 Python / launch / metadata overhead
占比容易变高
↓
Capture 整个小循环
比只优化某个单独 kernel 更有意义
Graph/Eager 选择本身也可能是分布式一致性状态
EAGLEDraftNpuGraphRunner.can_run_graph() 在特定 DSA IndexShare + DP Attention 场景下,会把本地 can_run_graph && seed_ready 做成一个整数,然后对 model TP group:
torch.distributed.all_reduce(
decision,
op=MIN,
group=tp_group,
)
原因是 Draft forward 里仍包含 TP / EP collective。
如果同一个 distributed forward 中出现:
Rank0 → Graph
Rank1 → Eager
collective 调用次序可能失去一致性。
因此:
Graph/Eager route 本身也可能属于 speculative control plane,必须在协作 Rank 间收敛。
三、Verify Graph 的 Batch Bucket 与 Token 宽度
Draft 完成以后,Target Verify 会进入通用 DecodeCudaGraphRunner / NPUGraphRunner。
固定 NPU 入口:
Target Runner 初始化时:
self.captured_req_width
=
model_runner.decode_num_tokens_per_req(
num_draft_tokens=...
)
对于普通 EAGLE Target:
captured_req_width
=
speculative_num_draft_tokens
固定来源:
因此如果 num_draft_tokens=3,每个请求 Target Verify 的 Graph 宽度固定是 3 rows。
当前 EAGLE 不是 Ragged Verify
这一点必须和 DSpark 区分。
SpeculativeAlgorithm.supports_ragged_verify() 在固定版本中:
return self.is_dspark()
固定源码:
所以当前 EAGLE / NextN Target Verify 不是:
Req0 verify 2 tokens
Req1 verify 3 tokens
Req2 verify 1 token
↓
按 sum(verify_lens) 选择 token bucket
而是每个 request 固定 Verify num_draft_tokens rows。
Accept Length 在 Verify 之后产生,因此本轮 EAGLE Verify 仍按捕获的固定宽度执行,接受较短只影响后续提交。
普通 EAGLE Graph 首先按 Request Batch Size 选 Bucket
非 Ragged path 的 load_batch():
raw_num_token
=
raw_bs * captured_req_width
bs
=
_pad_to_bucket(
raw_bs,
capture_bs,
)
padded_num_tokens
=
bs * captured_req_width
固定源码:
DecodeCudaGraphRunner.load_batch()
假设:
raw_bs = 5
num_draft_tokens = 3
Graph request buckets:
1, 2, 4, 8
则:
真实 Verify rows = 5 × 3 = 15
Graph bucket = 8 requests
Graph replay rows = 8 × 3 = 24
其中 9 rows 来自 bucket padding。
所以 Graph 的收益和代价同时存在:
收益:
减少 launch / Python / control overhead
代价:
执行 padded request slots 对应的额外 token rows
DP Attention 下 admission 甚至不是本 Rank 的 raw_bs
如果需要 MLP TP gather,can_run_graph() 会用:
cuda_graph_bs
=
max(
forward_batch.original_global_num_tokens_cpu
)
也就是 Attention-DP 各 Rank 原始 request count 的最大值。
随后相关 Rank 进入兼容这个 global-max geometry 的 Graph bucket。
因此对 DP Attention + Speculation,Graph padding 还承担一个分布式目的:
让协作 Rank 在同一 captured collective geometry 上 replay。
分析 Target Verify Graph 时,将本地请求数与下面几项一起核对:
DP group 最大 Request Count
×
固定 Verify Width
×
Capture Bucket
--cuda-graph-bs-decode 在这里是 Request Bucket,不是 EAGLE Verify Token Bucket
固定 DeepSeek-V4 Ascend best-practice 中可以看到:--cuda-graph-bs-decode 1 2 4 8
或:1 2 4 8 10
在当前普通 EAGLE 路径里,这些 key 首先表示 captured request slots。
若 num_draft_tokens=3,Target Verify 对应的 token rows 才是:bucket_bs × 3
DSpark Ragged Verify 则按 graph_num_tokens tier 组织工作,相关成本模型在 DSpark 专题中展开。
四、Plan Stream 与计算的依赖关系
环境变量:SGLANG_ENABLE_OVERLAP_PLAN_STREAM=1
会创建:
plan_stream
=
torch.get_device_module(device).Stream()
固定源码:
但“有第二条 Stream”并不意味着任何两个阶段真的在设备上并行。
关键要看 wait_stream 在哪里。
Verify Planning 等待 Draft 结果
run_eagle_verify() 一开始拿到当前 compute stream:
fwd_stream = current_stream()
随后:
with plan_stream_ctx:
plan_stream.wait_stream(
fwd_stream
)
eagle_prepare_for_verify(...)
源码注释明确说明:Verify Prep 会读取 Draft 生成的 Tree metadata,因此不能在 Draft frontier 完成以前开始。
Plan 完成后,main stream 又:
current_stream().wait_stream(
plan_stream
)
才启动 Target Verify。
固定源码:
Device dependency 实际是:
Main Stream
Draft
████████████
│
▼
Plan Stream
Verify Plan
███████
│
▼
Main Stream
Target Verify
███████████████
因此不存在 Draft Compute 与 Verify Planning 的设备重叠。
Plan Stream 在这里的意义更接近:
- 把 planning device work 放到独立 stream;
- 减少不必要的 Host blocking;
- 明确 metadata ownership;
- 用 Stream dependency 而不是 Host 同步表达执行顺序。
但真实的数据依赖仍然位于 critical path。
Draft-Extend Planning:这里反而真的可以跨阶段 overlap
Verify 完成后,_draft_extend_for_decode() 先构造 EagleDraftExtendInput、select_index 和 next_token_ids,然后直接:
with self.plan_stream_ctx:
forward_batch = prepare_for_draft_extend(...)
注意这里在 planning 之前没有:
plan_stream.wait_stream(
current_stream
)
固定源码:
Python 从 run_eagle_verify() 返回,并不表示之前 enqueue 的 NPU kernels 已经执行完成。设备执行是异步的,因此 Plan Stream 上的 Draft-Extend Planning 可以与 Main Stream 上尚未完成的 Target Verify / Accept tail 形成并发,只要 Planning 本身不读取那些尚未 ready 的 Device value。
源码还专门把:
next_token_ids
=
batch_result.next_token_ids.to(torch.int64)
放在进入 Plan Stream 之前,并写明这是为了避免在 Plan Stream 内发生额外 cross-stream synchronization / data race。
随后进入 Draft Extend Forward 前:
current_stream().wait_stream(
self.plan_stream
)
因此更准确的 Timeline 是:
Main Stream
Target Verify
████████████████
Accept kernels
████████
│
Plan Stream
Draft-Extend Planning
███████████
│
Main Stream
wait(plan)
│
▼
Draft Extend Forward
███████████
两种 Planning 的依赖分别是:
Verify Prep:
Draft → Plan → Verify
严格依赖
Draft-Extend Prep:
Target Verify / Accept main-stream tail
∥
Draft-Extend Planning
↓
Join
↓
Draft Extend Compute
五、Target 与 Draft 的多流路径
SGLANG_NPU_USE_MULTI_STREAM=1 控制 DeepSeek-V4 单个模型 Layer 内部的局部并行。Plan Stream 处理投机阶段的计划工作,两者的作用范围不同。
Target DeepSeek-V4 Model 初始化时,只要 NPU 环境变量开启,就创建一组 alt_streams,并把它传进每个 DeepseekV4DecoderLayer。
固定源码:
Attention Layer 收到 alt streams 后,NPU eligibility 是:
_is_npu
and SGLANG_NPU_USE_MULTI_STREAM
and self.alt_streams is not None
and x.shape[0] <= self._multi_stream_bs_limit
and not forward_batch.forward_mode
.is_extend_or_draft_extend_or_mixed()
固定源码:
这里有两个容易误读的地方。
TARGET_VERIFY 没有被排除
ForwardMode.is_extend() 确实把 TARGET_VERIFY 看成 extend-family。
但 Multi-Stream 条件调用的不是 is_extend(),而是:is_extend_or_draft_extend_or_mixed()
这个 helper 的默认参数:include_draft_extend_v2 = False
所以默认只排除:
EXTEND
MIXED
SPLIT_PREFILL
并不排除 TARGET_VERIFY。
固定源码:
所以 Target Model 的:
DECODE
TARGET_VERIFY
都可以满足 mode 这一层条件。
DRAFT_EXTEND_V2 也没有被 helper 默认排除,但 NextN 自己没有 alt streams
更反直觉的是,DRAFT_EXTEND_V2 在默认参数下也不被这个 helper 排除。
ForwardMode 之外,还要检查模型是否创建并传入 alt streams。
但 DeepSeek-V4 NextN Model 构造唯一 decoder layer 时显式:
self.decoder = DeepseekV4DecoderLayer(
...,
alt_streams=None,
...
)
固定源码:
所以 NextN layer 内 self.alt_streams=None。
最终实际可达性是:
表格可左右滑动 →
| Phase | ForwardMode | Model | Attention NPU Multi-Stream |
|---|---|---|---|
| 普通 Target Decode | DECODE |
Target | 可达,还需 env / token rows / layer 条件满足 |
| EAGLE Target Verify | TARGET_VERIFY |
Target | 可达 |
| EAGLE Draft Decode | DECODE |
NextN | 不可达,NextN decoder 的 alt_streams=None |
| EAGLE Draft Extend | DRAFT_EXTEND_V2 |
NextN | 不可达,同上 |
| 普通 Prefill | EXTEND |
Target | 该 NPU Attention Multi-Stream path 被 mode 条件排除 |
Target 多流中的独立计算分支
进入 _forward_prepare_multi_stream_npu() 后:
stream_kv
→ KV projection / Norm / RoPE / KV cache store
stream_q
→ Q projection / RMSNorm / RoPE
current stream
→ Indexer / Compressor
最后:
current_stream.wait_stream(stream_kv)
current_stream.wait_stream(stream_q)
才进入 downstream Attention。
固定源码:
_forward_prepare_multi_stream_npu()
因此局部 Critical Path 理想模型更接近:
T_prepare
≈
T_prefix
+
max(
T_Q,
T_KV,
T_indexer/compressor
)
而不是三者相加。
这些分支可并发提交,实际重叠程度随 Cube / Vector 资源竞争、HBM 带宽、Kernel Occupancy、CANN 调度与 Token 行数变化。用 Profiler 对齐开始、结束与汇合点,就能量出其中暴露的等待。
另外固定源码中非 Blackwell 的 Multi-Stream row limit 是 64。这里判断的是当前 Layer 的 x.shape[0],不是服务器的 max_running_requests。
六、DeepEP 从异步 Launch 到等待结果
DeepSeek-V4 Target Verify 还会进入 MoE。
官方 Ascend recipe 使用:
--moe-a2a-backend deepep
--deepep-mode auto
FusedMoE 创建 DeepEP dispatcher 时传入:
async_finish=True
return_recv_hook=True
固定入口:
但这两个参数并不意味着:
Dispatch
∥
Routed Expert GEMM
天然并行。
Normal DeepEP:通信异步 Launch,但 Expert 前仍必须 Wait
Normal dispatcher:
previous_event = Buffer.capture()
buffer.dispatch(
...,
async_finish=True,
allocate_on_comm_stream=True,
)
通信可以在 Communication Stream 上启动。
但 dispatch_b() 随后:
event.current_stream_wait()
然后才返回 Expert 输入。
固定源码:
因此真实依赖仍然是:
DeepEP Dispatch
↓
recv complete barrier
↓
Routed Expert GEMM
async_finish=True 将 Launch 与 Wait 分开;Routed Expert 仍在接收完成后开始计算。
Combine 也是同样结构:
Combine async launch
↓
event.current_stream_wait()
↓
combined hidden consumer
所以 Normal mode 的 async API 主要提供:
把 communication 放到 communication stream 执行的能力;能隐藏多少,取决于 Wait 之前有没有别的 independent work。
Shared Expert 正好是一个独立 branch
DeepSeek MoE 在 Shared Expert 没有 fuse 到 routed path 时,可以把 Shared Expert 放到 alt stream:
self.alt_stream.wait_stream(
current_stream
)
with alt_stream:
shared_output
=
_forward_shared_experts(...)
随后主路径继续:
TopK
→ DeepEP Dispatch
→ Routed Experts
→ Combine
最终再等待 Shared Expert output。
固定源码:
DeepseekV2MoE.forward_deepep()
因此可能形成:
Routed path
Router
↓
Dispatch ───────── Expert ───── Combine
██████████████████████████████████████
Shared side stream
Shared Expert
████████████████
这里 Shared Expert 可以覆盖 Routed Path 的一部分时间,其中也可能包含 Communication。
对齐耗时时,同时记录 DeepEP 总时长与以下等待:
通信结束以后
还有多少 Tail
没有被 independent compute 覆盖
Low-Latency DeepEP:recv_hook 把 Wait 推迟到 Consumer Boundary
Low-latency dispatch 调用:
buffer.low_latency_dispatch(
...,
async_finish=False,
return_recv_hook=True,
)
得到:
event
hook
外层 DeepEPDispatcher.dispatch() 的顺序是:
dispatch_a()
↓
deepep_dispatch_hooks
↓
dispatch_b()
而 Low-Latency dispatch_b():
hook()
才等待 / 完成接收。
固定源码:
_DeepEPDispatcherImplLowLatency
这提供了一个真实 overlap slot:
low_latency_dispatch launch
↓
[ dispatch hook 可执行 independent work ]
↓
recv_hook()
↓
Expert consumes received tokens
SBO 路径正是利用这个 slot,把 Shared Expert 等独立计算放进 Communication 等待窗口。
recv_hook 将等待留到接收数据被消费的位置:
把 Wait Point 从 Communication Launch 处推迟到消费 recv buffer 的边界,让中间可以插入与 recv 数据无关的 Compute。
Combine 侧也是同样思想;有 overlap_args 时 Low-Latency combine 可以切到 overlap stream,最终仍在 Consumer Boundary Join。
应该看 Exposed Communication,不是 Total Communication
如果某段 Communication 时间是 T_comm,同时独立计算能覆盖 T_independent,理想情况下暴露在 Critical Path 上的是:
T_exposed_comm
=
max(
0,
T_comm - T_independent
)
如果 async launch 后立刻 Wait:T_independent = 0
那么:T_exposed_comm ≈ T_comm
所以“async”并不自动带来性能收益。
七、Draft-Extend Graph 的配置与版本差异
固定版本 Ascend DeepSeek-V4 best-practice 中,部分 workload 可以看到:
export SGLANG_DISABLE_DRAFT_EXTEND_GRAPH=1
同时还开启:
export SGLANG_ENABLE_OVERLAP_PLAN_STREAM=1
export SGLANG_NPU_USE_MULTI_STREAM=1
并使用 EAGLE、NPU Graph 和 DeepEP。
第一眼很容易解释成:
“这个 workload 实测发现 Draft Extend Graph 更慢,所以官方关闭。”
实际调用路径还需要进一步区分。
Runtime 读取的环境变量名
固定 environ.py 注册的是:SGLANG_DISABLE_DRAFT_EXTEND_CUDA_GRAPH
其源码注释:
Kill-switch for draft-extend cuda graph. Escape hatch for setups where the capture's memory pool costs more than the graph saves, e.g. DeepEP MoE workspace captured at full dispatch capacity.
固定源码:
EAGLE Worker 创建 Draft-Extend Graph 时检查的也是:
not envs
.SGLANG_DISABLE_DRAFT_EXTEND_CUDA_GRAPH
.get()
固定入口:
EagleDraftWorker._capture_cuda_graphs()
但固定版本 best-practice 页面写的是:SGLANG_DISABLE_DRAFT_EXTEND_GRAPH
少了 _CUDA_。
在这个 commit 的仓库里没有找到旧名字对应的 EnvBoolWithAlias、consumer 或 translation。
因此这条文档配置存在明显的:doc / runtime env-name drift
该版本应使用 Runtime 读取的 SGLANG_DISABLE_DRAFT_EXTEND_CUDA_GRAPH 控制开关。部署脚本若沿用旧名,需要在启动日志中检查实际 Graph 状态。
但“为什么需要这个 Kill Switch”有明确源码依据
正确 Runtime flag 的注释已经给出设计动机:
Draft-Extend Graph Capture
↓
静态 memory pool / workspace
↓
某些 MoE / DeepEP workspace
按 full dispatch capacity 捕获
↓
常驻 footprint 变大
↓
Graph 节省的 launch latency
可能不值这份 memory cost
Draft Extend 本身又只是 NextN 的一段相对短 Forward。
所以它容易落入这样的 Trade-off:
Graph benefit
=
saved launch/control overhead
-
capture memory cost
-
padding cost
-
replay preparation cost
当:
Draft Extend compute 很短
+
DeepEP workspace capture 很大
时:
Draft Decode Graph ON
Target Verify Graph ON
Draft Extend Eager
完全可能是合理 operating point。
但要强调:
这是 Runtime Kill-Switch 的设计动机,不是对固定 best-practice 那个旧变量名已经生效的证明。
Draft-Extend Graph 自己也有独立静态宽度
它的 Graph Runner EAGLEDraftExtendCudaGraphRunner 不是按 num_steps + 1 捕获。
源码明确:
captured_req_width
=
resolve_num_tokens_per_req(
phase="draft_extend"
)
也就是:speculative_num_draft_tokens
固定源码:
EAGLEDraftExtendCudaGraphRunner
这意味着即使最终 accept_lens 较短,Draft Extend Graph 的 Buffer / Capture Geometry 仍按照完整 speculative width 设计; LM Head 只在 selected last-accepted rows 上做后续处理。
评估 Draft-Extend Graph 时,将捕获和 workspace 的内存开销与实际节省的启动时间一起比较。
八、将执行依赖与实测时间线对齐
下图只表达阶段依赖,不按长度表示耗时。实际重叠范围需要与 Profiler 时间线对照。
flowchart TD
D["Draft 完成"] --> VP["Verify Plan"]
VP --> V["Host 提交 Verify 与 Accept"]
V --> A["Main Stream 执行验证与接受"]
V --> EP["Plan Stream 准备 Draft Extend"]
A --> J["所需结果与 Plan 就绪"]
EP --> J
J --> E["Draft Extend"]
表格可左右滑动 →
| 层内分支 | 独立工作 | 汇合依赖 |
|---|---|---|
| Target Attention | Q、KV、Indexer / Compressor 的对应分支 | Attention 消费前等待所需结果 |
| Routed MoE | Dispatch、Expert GEMM、Combine | GEMM 等待收到 Token;后续消费等待 Combine |
| Shared Expert | 可独立执行的专家计算 | 与 routed 输出合并前完成 |
Plan Stream 是否形成跨阶段重叠,取决于调用与等待位置。NextN Draft 没有接入这组 alt streams,使用自身的执行路径。
拿 Ascend Profiler 分析一轮 speculative decode 时,可以按这个顺序看:
表格可左右滑动 →
| 观察点 | 对应源码问题 |
|---|---|
| Draft | Graph replay 还是 eager?num_steps 对应几次 NextN forward? |
| Draft Graph | raw request count pad 到哪个 bucket? |
| Verify seam | Draft 结束到 Verify start 中间,Verify Plan gap 多长?这段不能与 Draft overlap。 |
| Target Verify | 固定 Verify width 是多少?bucket_bs × num_draft_tokens 有多少 padding rows? |
| Plan overlap | Draft-Extend Plan 是否真的与 Verify / Accept tail 同时出现在 NPU timeline? |
| Attention | Q / KV / Indexer 三条 branch 哪条最长?Multi-Stream Join 最终等谁? |
| MoE | DeepEP 总 duration 中,有多少被 Shared Expert / SBO 覆盖? exposed tail 是多少? |
| Rank control | 是否因为某 Rank Graph fallback 导致整个 TP group 一起 eager? |
| Draft Extend | Graph 还是 eager?Capture memory / workspace 是否值得? |
把一轮时间线对齐以后,再用有效输出量衡量收益:同一批次条件下,每个输出 Token 的平均成本约为本轮关键路径耗时除以有效推进长度。Graph 减少启动与控制开销,多流利用独立工作,异步通信将等待推迟到消费边界;这些机制能否缩短周期,要看最终汇合点仍在等待谁。
记录实验时,将候选宽度、图档位、接受长度与依赖等待一起保留。只看 acceptance rate,或者把各 Stream 的耗时直接相加,都会遗漏影响端到端性能的部分。
源码阅读入口
表格可左右滑动 →
| 目标 | 固定版本源码 |
|---|---|
| EAGLE Worker | eagle_worker_v2.py |
| Draft NPU Graph Runner | EAGLEDraftNpuGraphRunner |
| Draft Graph Capture Body | EAGLEDraftCudaGraphRunner |
| Draft Multi-Step Loop | EagleDraftWorker.draft_forward() |
| Spec Phase Static Width | resolve_num_tokens_per_req() |
| Target NPU Graph | NPUGraphRunner |
| Target Graph Fixed Width / Bucket | DecodeCudaGraphRunner |
| EAGLE / DSpark Ragged Verify 边界 | SpeculativeAlgorithm.supports_ragged_verify() |
| Verify Preparation | eagle_prepare_for_verify() |
| Verify + Plan Stream Dependency | run_eagle_verify() |
| Plan Stream Creation | get_plan_stream() |
| Draft-Extend Planning Overlap | _draft_extend_for_decode() |
| Draft-Extend NPU Graph | EAGLEDraftExtendNpuGraphRunner |
| Draft-Extend Graph Buffer / Width | EAGLEDraftExtendCudaGraphRunner |
| Target NPU Multi-Stream Pool | DeepseekV4Model.__init__() |
| NPU Attention Multi-Stream | _forward_prepare_multi_stream_npu() |
NextN Explicit alt_streams=None |
deepseek_v4_nextn.py |
| ForwardMode Boundary | ForwardMode |
| DeepEP Normal Async Boundary | _DeepEPDispatcherImplNormal |
| DeepEP Low-Latency Recv Hook | _DeepEPDispatcherImplLowLatency |
| DeepSeek Shared-Expert Overlap | DeepseekV2MoE.forward_deepep() |
| Draft-Extend Graph Kill Switch | environ.py |
| Ascend DeepSeek-V4 Best Practice | deepseek_v4_flash.mdx |