DeepEP 测试目标 API 系统性参考
定位:本文以"被测 API"为中心,系统描述 DeepEP 测试程序验证的所有关键 API —— 签名、语义、参数约束、测试覆盖方式。
配套文档:测试体系总览 | 测试基础设施 | 主EP测试
1. API 体系全景
DeepEP 测试覆盖两大 Buffer 类 + 一组工具函数,构成完整的 API 测试矩阵:
flowchart TB
subgraph B2["V2: ElasticBuffer (elastic.py)"]
direction TB
B2A["dispatch() / combine()"]
B2B["barrier()"]
B2C["engram_write() / engram_fetch()"]
B2D["all_gather() / reduce_scatter()"]
B2E["pp_send() / pp_recv()"]
B2F["get_theoretical_num_sms() / get_theoretical_num_qps()"]
end
subgraph B1["V1: Buffer (legacy.py)"]
direction TB
B1A["dispatch() / combine()"]
B1B["low_latency_dispatch() / low_latency_combine()"]
B1C["get_dispatch_config() / get_combine_config()"]
end
subgraph U["工具函数 (utils/)"]
U1["refs.dispatch / refs.combine"]
U2["math.per_token_cast_to_fp8 / per_token_cast_back"]
U3["gate.get_unbalanced_scores"]
U4["testing.bench_kineto / flush_l2_cache"]
U5["envs.init_dist / init_seed"]
end
B2 --> U
B1 --> U
2. V2 ElasticBuffer API 详解
2.1 dispatch() — Token 分发
源码位置:deep_ep/buffers/elastic.py:855-1044
函数签名:
def dispatch(self,
x: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]],
topk_idx: Optional[torch.Tensor] = None,
topk_weights: Optional[torch.Tensor] = None,
cumulative_local_expert_recv_stats: Optional[torch.Tensor] = None,
num_experts: Optional[int] = None,
num_max_tokens_per_rank: Optional[int] = None,
expert_alignment: Optional[int] = None,
num_sms: int = 0, num_qps: int = 0,
previous_event: Optional[EventHandle] = None,
previous_event_before_epilogue: Optional[EventHandle] = None,
async_with_compute_stream: bool = False,
allocate_on_comm_stream: bool = False,
handle: Optional[EPHandle] = None,
do_handle_copy: bool = True,
do_cpu_sync: Optional[bool] = None,
do_expand: bool = False,
do_zero_padding: bool = False,
use_tma_aligned_col_major_sf: bool = False
) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]],
Optional[torch.Tensor], Optional[torch.Tensor],
EPHandle, EventOverlap]
核心语义:将本地 token 根据 topk_idx 选择分发到对应 expert 所在的 rank,支持 NVLink(节点内)和 RDMA(跨节点)两种传输路径。
参数矩阵:
| 参数 | 类型 | 默认值 | 语义 | 测试覆盖方式 |
|---|---|---|---|---|
x |
Tensor 或 (Tensor, Tensor) | — | 输入 token [num_tokens, hidden],FP8 模式下为 (fp8_tensor, scales) | use_fp8_dispatch ∈ {0, 1} |
topk_idx |
int64 Tensor [T, K] | None | 每个 token 选择的 expert 索引,-1 表示未选择 | 由 gate.get_unbalanced_scores 生成 |
topk_weights |
float Tensor [T, K] | None | expert 权重 | num_bias ∈ {0, 1, 2} |
num_experts |
int | None | 全局 expert 数 | 从 args.num_experts 传入 |
expert_alignment |
int | None | 每个 expert 接收 token 数对齐粒度 | ∈ {1, 128} |
num_sms |
int | 0 | SM 数(0=自动) | 自动推导 get_theoretical_num_sms() |
num_qps |
int | 0 | RDMA QP 数(0=自动) | 自动推导 get_theoretical_num_qps() |
previous_event |
EventHandle | None | 等待的前置事件(流水线模式) | with_previous_event ∈ {0, 1} |
async_with_compute_stream |
bool | False | 异步执行,compute stream 不等待 | async_with_compute_stream ∈ {0, 1} |
allocate_on_comm_stream |
bool | False | 在 comm stream 上分配张量 | allocate_on_comm_stream ∈ {0, 1} |
handle |
EPHandle | None | 缓存的通信 handle(复用 layout) | do_handle_copy ∈ {0, 1} |
do_expand |
bool | False | 使用 expand 布局(每个 expert 一个 slot) | expand 模式测试 |
do_zero_padding |
bool | False | 对 expand 布局的对齐间隙填零 | expand + alignment 组合 |
返回值:
| 返回值 | 类型 | 说明 |
|--------|------|------|
| recv_x | Tensor 或 (Tensor, Tensor) | 接收到的 token(类型与输入一致) |
| recv_topk_idx | int64 Tensor | 接收到的 expert 索引 |
| recv_topk_weights | float Tensor | 接收到的 expert 权重 |
| handle | EPHandle | 通信 handle(用于 combine) |
| event | EventOverlap | 事件句柄(async 模式下有效) |
测试覆盖:test_ep.py 通过 enumerate_ep_modes() 生成 192~288 种参数组合,覆盖上表所有维度。
2.2 combine() — Token 聚合
源码位置:deep_ep/buffers/elastic.py:1046-1107
函数签名:
def combine(self,
x: torch.Tensor,
handle: EPHandle,
topk_weights: Optional[torch.Tensor] = None,
bias: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]] = None,
num_sms: int = 0, num_qps: int = 0,
previous_event: EventHandle = None,
previous_event_before_epilogue: Optional[EventHandle] = None,
async_with_compute_stream: bool = False,
allocate_on_comm_stream: bool = False
) -> Tuple[torch.Tensor, Optional[torch.Tensor], EventOverlap]
核心语义:将经过 expert 计算的 token 按 handle 记录的原始路由信息聚合回源 rank,执行加权归约(weighted reduction)。
参数矩阵:
| 参数 | 类型 | 默认值 | 语义 | 测试覆盖方式 |
|---|---|---|---|---|
x |
BF16 Tensor [T, hidden] | — | 要发送的 token | dispatch 的输出 |
handle |
EPHandle | — | dispatch 返回的通信 handle | 自动传递 |
topk_weights |
float Tensor | None | 归约权重 | 与 dispatch 共享 |
bias |
Tensor 或 (Tensor, Tensor) | None | 输出偏置(0/1/2 个) | num_bias ∈ {0, 1, 2} |
num_sms |
int | 0 | SM 数(0=复用 dispatch 的) | 自动推导 |
num_qps |
int | 0 | QP 数 | 自动推导 |
previous_event |
EventHandle | None | 前置事件 | 流水线模式 |
async_with_compute_stream |
bool | False | 异步执行 | ∈ {0, 1} |
返回值:
| 返回值 | 类型 | 说明 |
|--------|------|------|
| combined_x | BF16 Tensor [T, hidden] | 聚合后的 token |
| combined_topk_weights | float Tensor | 聚合后的权重 |
| event | EventOverlap | 事件句柄 |
测试覆盖:test_ep.py 测试两种 combine 模式:
- normal combine:标准归约
- reduced combine:allow_multiple_reduction 模式下的多级归约
2.3 barrier() — GPU 级同步
源码位置:deep_ep/buffers/elastic.py:497-508
def barrier(self, use_comm_stream: bool = True, with_cpu_sync: bool = False, sequential: bool = True) -> None
参数矩阵:
| 参数 | 类型 | 默认值 | 语义 |
|---|---|---|---|
use_comm_stream |
bool | True | 使用 comm stream(否则用 compute stream) |
with_cpu_sync |
bool | False | 是否额外调用 cudaDeviceSynchronize |
sequential |
bool | True | 串行模式(单 SM,更安全)vs 并行模式(多 SM) |
底层实现路径:
buffer.barrier() → runtime.barrier() → launch_barrier() → barrier_impl<kSequential>
测试覆盖:test_barrier.py 测量 1000 次连续调用的平均延迟(微秒级)。
2.4 engram_write() / engram_fetch() — RDMA 远端内存访问
源码位置:deep_ep/buffers/elastic.py:569-604
def engram_write(self, storage: torch.Tensor, sf: Optional[torch.Tensor] = None) -> None
def engram_fetch(self, indices: torch.Tensor, num_qps: int = 0,
use_tma_aligned_col_major_sf: bool = False) -> Callable
核心语义:Engram 是 DeepEP 的远端内存访问原语,通过 NCCL Gin(GPU-initiated RDMA)直接读写远端 GPU 的存储窗口。
| API | 方向 | 说明 |
|---|---|---|
engram_write(storage, sf) |
本地 → Window | 将本地数据注册到 NCCL window,供远端读取 |
engram_fetch(indices)() |
远端 → 本地 | 异步发起 RDMA get,返回 callable hook;调用 hook() 阻塞等待完成 |
参数详解:
| 参数 | 类型 | 语义 |
|---|---|---|
storage |
BF16/FP8 Tensor [num_entries, hidden] | 要注册的存储数据 |
sf |
float/int Tensor [num_entries, num_sf_packs] | FP8 scaling factors(FP8 模式必须提供) |
indices |
int Tensor [num_tokens, num_entries_per_token] | 要获取的 entry 索引 |
num_qps |
int | QP 数(0=全部) |
测试覆盖:test_engram.py 验证正确性 + 带宽测量。
2.5 all_gather() / reduce_scatter() — 集合通信
源码位置:deep_ep/buffers/elastic.py:706-726
def all_gather(self, t: Union[torch.Tensor, Sequence[torch.Tensor]])
核心语义:在 AGRS session 内执行 All-Gather,通过 NVLink symmetric memory 将每个 rank 的数据广播到所有 rank。
使用模式:
# 单 tensor 模式
gathered, handle = buffer.all_gather(tensor) # gathered 多一维 num_ranks
# Batched 模式
*gathered_tensors, handle = buffer.all_gather([t1, t2, t3])
# 等待完成
handle()
配套 API:
| API | 说明 |
|---|---|
create_agrs_session() / destroy_agrs_session() |
session 生命周期管理 |
agrs_get_inplace_tensor(shapes, dtype) |
获取 in-place 张量(零拷贝) |
agrs_set_config(max_bytes, max_ags) |
session 参数配置 |
测试覆盖:test_agrs.py 使用 Stress Test 范式,随机操作序列验证正确性。
2.6 pp_send() / pp_recv() — Pipeline Parallel 通信
源码位置:deep_ep/buffers/elastic.py:616-636
def pp_send(self, t: torch.Tensor, dst_rank_idx: int, num_sms: int = 0) -> None
def pp_recv(self, t: torch.Tensor, src_rank_idx: int, num_sms: int = 0) -> None
核心语义:在 PP ring 中向相邻 rank 发送/接收张量(仅支持 prev/next rank)。
测试覆盖:test_pp.py 测量 send/recv 延迟。
2.7 get_theoretical_num_sms() / get_theoretical_num_qps() — 自动资源计算
源码位置:deep_ep/buffers/elastic.py:728-853
def get_theoretical_num_sms(self, num_experts: int, num_topk: int, ...) -> int
def get_theoretical_num_qps(self, num_sms: int) -> int
核心语义:基于带宽建模自动推导最优 SM 数和 QP 数。
SM 计算模型:
flowchart LR
A["输入: num_experts, num_topk"] --> B["计算 expected_topk<br/>(超几何分布)"]
B --> C["估算 RDMA/NVLink 流量"]
C --> D["瓶颈分析<br/>max(rdma_traffic/rdma_gbs,<br/>nvlink_traffic/nvlink_gbs)"]
D --> E["SM = max(sm_read/read_gbs,<br/>sm_write/write_gbs) × 1.25"]
E --> F["对齐到偶数, 至少 4"]
QP 计算规则:
| 模式 | QP 数 |
|------|-------|
| Direct | min(num_sms, 9) |
| Hybrid | num_sms * 16 + 1 |
3. V1 Buffer API 详解
3.1 Buffer.dispatch() — V1 Token 分发
源码位置:deep_ep/buffers/legacy.py:322-405
def dispatch(self, x, handle=None,
num_tokens_per_rank=None, num_tokens_per_rdma_rank=None,
is_token_in_rank=None, num_tokens_per_expert=None,
topk_idx=None, topk_weights=None,
expert_alignment=1, num_worst_tokens=0,
config=None, previous_event=None,
async_finish=False, allocate_on_comm_stream=False) -> \
Tuple[recv_x, recv_topk_idx, recv_topk_weights,
num_recv_tokens_per_expert_list, handle, event]
与 V2 的关键差异:
| 维度 | V1 (Buffer) | V2 (ElasticBuffer) |
|---|---|---|
| 路由计算 | 需手动提供 num_tokens_per_rank, is_token_in_rank |
自动从 topk_idx 推导 |
| 配置 | 需手动创建 Config 对象 |
自动推导 SM/QP |
| 返回值 | 包含 num_recv_tokens_per_expert_list |
通过 handle 间接获取 |
| 内部路径 | 自动分发到 internode_dispatch 或 intranode_dispatch |
统一路径 |
测试覆盖:test_internode.py(跨节点)、test_intranode.py(节点内)。
3.2 Buffer.combine() — V1 Token 聚合
源码位置:deep_ep/buffers/legacy.py:408-470
def combine(self, x, handle, topk_weights=None, bias=None,
config=None, previous_event=None,
async_finish=False, allocate_on_comm_stream=False) -> \
Tuple[combined_x, combined_topk_weights, event]
注意:V1 combine 是无权重归约(纯加法),topk_weights 仅用于返回。
3.3 Buffer.low_latency_dispatch() — 低延迟分发
源码位置:deep_ep/buffers/legacy.py:553-621
def low_latency_dispatch(self, x, topk_idx,
num_max_dispatch_tokens_per_rank, num_experts,
cumulative_local_expert_recv_stats=None,
dispatch_wait_recv_cost_stats=None,
use_fp8=True, round_scale=False, use_ue8m0=False,
async_finish=False, return_recv_hook=False) -> \
Tuple[recv_x, recv_count, handle, event, hook]
核心语义:使用 IBGDA(GPU-initiated RDMA)实现低延迟 dispatch,不经过 NVLink,直接通过 RDMA 网络传输。
关键参数:
| 参数 | 类型 | 语义 |
|---|---|---|
num_max_dispatch_tokens_per_rank |
int | 每 rank 最大 token 数(所有 rank 必须相同) |
use_fp8 |
bool | FP8 模式(默认开启) |
round_scale |
bool | 将 scale 取整为 2 的幂 |
use_ue8m0 |
bool | 使用 UE8M0 scale 格式 |
return_recv_hook |
bool | 返回 hook 而非等待完成(支持流水线) |
测试覆盖:test_low_latency.py。
3.4 Buffer.low_latency_combine() — 低延迟聚合
源码位置:deep_ep/buffers/legacy.py:624-670
def low_latency_combine(self, x, topk_idx, topk_weights, handle,
use_logfmt=False, zero_copy=False,
async_finish=False, return_recv_hook=False,
out=None, combine_wait_recv_cost_stats=None) -> \
Tuple[combined_x, event, hook]
与 normal combine 的关键差异:
| 维度 | Normal Combine | Low-Latency Combine |
|---|---|---|
| 归约方式 | 无权重(纯加法) | 带权重(weighted reduction) |
| 传输路径 | NVLink + RDMA 三阶段 | 直接 RDMA(IBGDA) |
| 额外功能 | — | use_logfmt(10-bit 对数格式)、zero_copy |
4. 工具函数 API
4.1 refs.py — PyTorch 参考实现
| 函数 | 签名 | 语义 | 对应 Kernel |
|---|---|---|---|
dispatch() |
(x, topk_idx, topk_weights, num_experts, num_tokens_per_rank, expert_alignment) → (recv_x, recv_topk_idx, recv_topk_weights, num_recv_tokens_per_expert_list) |
完整的 dispatch 参考实现 | dispatch_impl |
generate_pre_combine_data() |
(src_token_global_idx, topk_idx, topk_weights, num_experts) → (pre_x, pre_topk_idx, pre_topk_weights) |
为 combine 准备参考数据 | — |
ordered_accumulate() |
(data, initial_value) → accumulated |
有序累加(保证 bitwise 确定性) | combine 中的 reduction |
combine() |
(y, topk_idx, topk_weights, num_experts, initial_value) → combined |
完整的 combine 参考实现 | combine_impl |
设计要点:
- 使用 ordered_accumulate 保证累加顺序确定性,与 kernel 计算路径一致
- 不使用 torch.scatter_add(非确定性),而是手动实现有序累加
4.2 math.py — FP8 编解码与数值工具
| 函数 | 签名 | 语义 |
|---|---|---|
per_token_cast_to_fp8(x) → (x_fp8, scales) |
BF16 → FP8 E4M3,per-token 量化 | |
per_token_cast_back(x_fp8, scales) → x |
FP8 → BF16 反量化 | |
calc_diff(x, y) → float |
计算两个张量的相对差异 | |
align(x, y) → int |
将 x 向上对齐到 y 的倍数 | |
ceil_div(x, y) → int |
向上取整除法 | |
count_bytes(*tensors) → int |
计算张量总字节数 | |
hash_tensor(t) → int |
张量内容哈希(用于调试) |
FP8 量化语义(测试关键):
# per_token_cast_to_fp8: 每行独立量化
scales = x.abs().max(dim=1) / fp8_max # per-token scale
x_fp8 = (x / scales.unsqueeze(1)).to(float8_e4m3fn)
# per_token_cast_back: 反量化
x = x_fp8.float() * scales.unsqueeze(1)
4.3 gate.py — MoE 非均衡负载生成
| 函数 | 签名 | 语义 |
|---|---|---|
get_unbalanced_scores(ratio, precise) → scores |
生成非均衡 MoE 路由分数 | |
get_precise_unbalanced_scores(ratio) |
精确控制 token 分布 | |
get_random_unbalanced_scores(ratio) |
随机逼近目标倾斜比 |
ratio 参数语义:
- ratio = 0:完全均匀分布
- ratio = 1:极端倾斜(所有 token 选择同一 expert)
- 中间值:通过二分搜索 factor 逼近
4.4 testing.py — Benchmark 工具
| 函数 | 签名 | 语义 |
|---|---|---|
bench(fn, name, ...) → float |
简单计时(中位数) | |
bench_kineto(fn, name, ...) → float |
Kineto profiler 集成 | |
flush_l2_cache(enabled) |
L2 Cache 刷新(256MB zero) |
bench_kineto 关键参数:
| 参数 | 语义 |
|---|---|
barrier_comm_profiling |
每次迭代前 sleep + barrier(消除 CPU 启动偏斜) |
flush_l2 |
每次迭代前刷新 L2 |
barrier |
用于同步的 barrier 函数 |
4.5 envs.py — 分布式初始化
| 函数 | 签名 | 语义 |
|---|---|---|
init_dist(local_rank, num_local_ranks, seed) → (rank, world_size, group) |
NCCL 进程组初始化 | |
init_seed(seed) |
设置全局随机种子 | |
dist_print(*args) |
仅 rank 0 打印 |
5. API 测试覆盖矩阵
5.1 按 API 维度
| API | 测试文件 | 覆盖方式 | 验证标准 |
|---|---|---|---|
ElasticBuffer.dispatch() |
test_ep.py | 192~288 参数组合 | bitwise identical |
ElasticBuffer.combine() |
test_ep.py | normal + reduced | bitwise identical |
ElasticBuffer.barrier() |
test_barrier.py | 1000 次延迟测量 | 无错误 |
ElasticBuffer.engram_write/fetch() |
test_engram.py | 正确性 + 带宽 | bitwise identical |
ElasticBuffer.all_gather() |
test_agrs.py | Stress test 随机序列 | bitwise identical |
ElasticBuffer.pp_send/recv() |
test_pp.py | 延迟基准 | 无错误 |
Buffer.dispatch() (internode) |
test_internode.py | 跨节点场景 | bitwise identical |
Buffer.dispatch() (intranode) |
test_intranode.py | 节点内场景 | bitwise identical |
Buffer.low_latency_dispatch() |
test_low_latency.py | IBGDA 模式 | bitwise identical |
Buffer.low_latency_combine() |
test_low_latency.py | 带权重归约 | bitwise identical |
get_theoretical_num_sms() |
test_ep.py | 自动推导值使用 | 隐式验证 |
per_token_cast_to_fp8/back() |
test_ep.py | FP8 dispatch 模式 | bitwise identical |
get_unbalanced_scores() |
test_gate.py | 统计分布验证 | 分布符合预期 |
5.2 按测试类型维度
| 测试类型 | 覆盖的 API | 验证方式 |
|---|---|---|
| 正确性验证 | dispatch, combine, all_gather, engram | 与 refs.py 参考实现 bitwise compare |
| 性能基准 | barrier, dispatch, combine, pp_send/recv | bench_kineto 测量延迟/带宽 |
| 压力测试 | dispatch, barrier | do_pressure_test 无限循环 |
| 统计验证 | get_unbalanced_scores | 分布形状验证 |
6. API 演化路径(V1 → V2)
flowchart TD
subgraph V1["V1: Buffer (legacy.py)"]
A1["dispatch()"]
A2["combine()"]
A3["low_latency_dispatch()"]
A4["low_latency_combine()"]
end
subgraph V2["V2: ElasticBuffer (elastic.py)"]
B1["dispatch()"]
B2["combine()"]
B3["engram_write/fetch()"]
B4["all_gather/reduce_scatter()"]
B5["pp_send/recv()"]
end
A1 -->|"统一 + 自动化"| B1
A2 -->|"统一 + 自动化"| B2
A3 -->|"抽象为 Engram 原语"| B3
A4 -->|"不再需要(统一路径)"| B2
关键演化:
1. 三种模式 → 统一路径:V1 需要 internode/intranode/low_latency 三套路径,V2 统一为一套 dispatch/combine
2. 手动配置 → 自动推导:V1 需手动指定 Config(SM 数、chunk 大小),V2 通过 get_theoretical_num_sms/qps() 自动推导
3. 低延迟模式 → Engram 原语:V1 的 low_latency 模式在 V2 中抽象为更通用的 Engram RDMA 原语
4. 新增能力:V2 新增 AGRS(all_gather/reduce_scatter)和 PP(pipeline parallel)支持
7. 测试入口与 API 调用关系
flowchart LR
subgraph Entry["测试入口"]
E1["test_ep.py"]
E2["test_agrs.py"]
E3["test_pp.py"]
E4["test_barrier.py"]
E5["test_engram.py"]
E6["test_gate.py"]
E7["test_internode.py"]
E8["test_intranode.py"]
E9["test_low_latency.py"]
end
subgraph API["被测 API"]
A_Elastic["ElasticBuffer<br/>dispatch/combine/barrier<br/>engram/agrs/pp"]
A_Legacy["Buffer<br/>dispatch/combine<br/>low_latency_*"]
end
subgraph Infra["基础设施"]
I_Ref["refs.py<br/>参考实现"]
I_Math["math.py<br/>FP8 编解码"]
I_Gate["gate.py<br/>负载生成"]
I_Test["testing.py<br/>benchmark"]
end
E1 --> A_Elastic
E2 --> A_Elastic
E3 --> A_Elastic
E4 --> A_Elastic
E5 --> A_Elastic
E6 --> I_Gate
E7 --> A_Legacy
E8 --> A_Legacy
E9 --> A_Legacy
A_Elastic --> Infra
A_Legacy --> Infra
8. 快速查找:我想测什么?
| 场景 | 应关注的 API | 对应测试文件 |
|---|---|---|
| 新增 expert 路由算法 | dispatch() 的 topk_idx 处理 |
test_ep.py |
| 修改 FP8 量化逻辑 | math.per_token_cast_to_fp8/back() |
test_ep.py (FP8 模式) |
| 调整 SM 分配策略 | get_theoretical_num_sms() |
test_ep.py (隐式) |
| 修改 combine 归约逻辑 | combine() + refs.combine() |
test_ep.py |
| 新增 RDMA 传输路径 | engram_write/fetch() |
test_engram.py |
| 修改 barrier 同步 | barrier() |
test_barrier.py |
| 调整 MoE 负载模型 | gate.get_unbalanced_scores() |
test_gate.py |
| 性能回归检测 | testing.bench_kineto() |
所有 test 文件 |