DeepEP Elastic Test 源码深度分析
文件:
tests/elastic/test_ep.py(609 行)
目标: 对 DeepEP 的 ElasticBuffer 进行全面的功能正确性验证 + 性能基准测试
测试对象:deep_ep.ElasticBuffer的dispatch()和combine()API
目录
1. 文件结构概览
test_ep.py (609 行)
├── 导入区 (L1-19)
├── enumerate_ep_modes() (L22-31) ← 参数空间笛卡尔积生成器
├── launch() (L34-41) ← 统一调用 dispatch/combine 的封装器
├── fold_expanded() (L44-55) ← expand 模式结果折叠验证
├── test_dispatch_combine() (L59-516) ← 核心测试逻辑
├── test_loop() (L20-561) ← 分布式入口 + 压力测试
└── __main__ (L564-609) ← argparse + multiprocessing.spawn
函数角色总览
| 函数 | 行号 | 角色 |
|---|---|---|
enumerate_ep_modes() |
22-31 | 生成 7 维参数空间的笛卡尔积 |
launch() |
34-41 | 统一封装 dispatch/combine 调用,支持 previous_event 和 async 模式 |
fold_expanded() |
44-55 | 将 expand 模式的结果按 valid_mask 折叠回 non-expand 布局 |
test_dispatch_combine() |
59-516 | 核心测试:构造数据 → dispatch → combine → 对比 reference |
test_loop() |
520-561 | 分布式入口:初始化 → 构造 buffer → 运行测试 → 压力循环 |
2. 测试环境初始化
2.1 分布式初始化 (test_loop, L520-561)
# L522: 通过 envs.init_dist() 初始化 NCCL 进程组
rank_idx, num_ranks, group = init_dist(local_rank, num_local_ranks, seed=args.seed)
init_dist() 的关键行为(envs.py L73-113):
- 读取 MASTER_ADDR, MASTER_PORT, WORLD_SIZE, RANK 环境变量
- 使用 nccl backend 初始化 torch.distributed
- 设置默认 dtype 为 bfloat16,默认 device 为 cuda
- 调用 init_seed(seed) 设置随机种子
- 创建新的通信组 dist.new_group(...)
2.2 ElasticBuffer 构造
# L523-534: construct_elastic_buffer 闭包
def construct_elastic_buffer():
return deep_ep.ElasticBuffer(group,
num_max_tokens_per_rank=args.num_tokens,
hidden=args.hidden,
deterministic=args.deterministic,
allow_hybrid_mode=args.allow_hybrid_mode,
allow_multiple_reduction=args.allow_multiple_reduction,
prefer_overlap_with_compute=bool(args.prefer_overlap_with_compute),
sl_idx=args.sl_idx,
num_allocated_qps=max(args.num_allocated_qps, args.num_qps),
explicitly_destroy=True,
num_gpu_timeout_secs=args.num_gpu_timeout_secs,
num_cpu_timeout_secs=args.num_cpu_timeout_secs)
关键参数说明:
- num_max_tokens_per_rank: 每个 rank 的最大 token 数(用于计算 src_token_global_idx)
- deterministic: 是否启用确定性排序模式
- allow_hybrid_mode: 是否允许 hybrid combine(intra-scaleup reduction)
- allow_multiple_reduction: 是否允许多级 reduction
- num_allocated_qps: 预分配的 QP(Queue Pair)数量
2.3 进程启动
# L608-609: 使用 torch.multiprocessing.spawn 启动多进程
num_processes = args.num_processes
torch.multiprocessing.spawn(test_loop, args=(num_processes, args), nprocs=num_processes)
默认 8 进程(对应 8 GPU),可通过 --num-processes 调整。
3. 参数空间枚举
3.1 enumerate_ep_modes() (L22-31)
def enumerate_ep_modes():
for do_handle_copy in (1, 0): # 是否拷贝 topk_idx
for expert_alignment in (128, 1): # expert 对齐粒度
for use_fp8_dispatch in (1, 0): # FP8 vs BF16 dispatch
for num_bias in (0, 1, 2): # bias 数量
for with_previous_event in (0, 1): # 是否传入 previous_event
for async_with_compute_stream in (0, 1): # 异步 compute stream
for allocate_on_comm_stream in ((1, ) if with_previous_event else (0, 1)):
yield (do_handle_copy, expert_alignment, use_fp8_dispatch, num_bias,
with_previous_event, async_with_compute_stream, allocate_on_comm_stream)
总组合数: 2 × 2 × 2 × 3 × 2 × 2 × (1 或 2) = 192 ~ 288 种组合
注意
allocate_on_comm_stream的条件约束:当with_previous_event=1时只取(1,),否则取(0, 1)。
3.2 参数维度分析
| 参数 | 取值 | 测试目的 |
|---|---|---|
do_handle_copy |
0, 1 | 验证 handle 内部 topk_idx 拷贝行为 |
expert_alignment |
1, 128 | 验证 expert 对齐(128 是 TMA 对齐要求) |
use_fp8_dispatch |
0, 1 | FP8 (E4M3) vs BF16 精度路径 |
num_bias |
0, 1, 2 | 无 bias / 单 bias / 双 bias(tuple) |
with_previous_event |
0, 1 | CUDA event 链式依赖 |
async_with_compute_stream |
0, 1 | 计算流异步执行 |
allocate_on_comm_stream |
0, 1 | 通信流上分配 tensor |
4. 核心辅助函数
4.1 launch() (L34-41) — 统一调用封装
def launch(buffer: deep_ep.ElasticBuffer, name: str,
with_previous_event: int, async_with_compute_stream: int,
params: dict):
if with_previous_event:
params.update(previous_event=buffer.capture()) # 捕获当前 CUDA event
values = getattr(buffer, name)(**params) # 动态调用 dispatch 或 combine
values[-1].current_stream_wait() if async_with_compute_stream else ()
return values
设计要点:
- name 参数化:同一函数可调用 dispatch 或 combine
- previous_event:通过 buffer.capture() 获取 event,实现跨 kernel 依赖
- async_with_compute_stream:控制是否在返回前同步 compute stream
- values[-1] 是返回的 event,调用 .current_stream_wait() 同步
4.2 fold_expanded() (L44-55) — Expand 结果折叠
def fold_expanded(expanded, indices, valid_mask):
if not isinstance(expanded, torch.Tensor):
return tuple(fold_expanded(t, indices, valid_mask) for t in expanded)
gathered = expanded[indices] # 按 slot 索引收集
first_valid_idx = valid_mask.to(torch.int).argmax(dim=1) # 每行第一个有效位置
folded = gathered[torch.arange(gathered.shape[0], device='cuda'), first_valid_idx]
result = (gathered == folded.unsqueeze(1)).all(dim=-1) # 验证所有有效位相同
result = result | (~valid_mask)
assert result.all()
return folded
作用:expand 模式下同一 token 的多个 expert slot 值应相同(broadcast 语义),此函数验证并折叠为单值。
5. 主测试函数 test_dispatch_combine
5.1 执行流程总览
flowchart TD
A[构造 expert 选择: scores → topk] --> B[枚举参数组合 enumerate_ep_modes]
B --> C[生成输入数据 x, bias]
C --> D[Reference 计算: ref_dispatch + ref_combine]
D --> E[DeepEP dispatch: 4 种模式]
E --> F[构造 combine 输入数据]
F --> G[DeepEP combine: normal + reduced]
G --> H[性能基准测试 bench_kineto]
H --> I[正确性验证: bitwise compare]
I --> J{还有更多组合?}
J -->|Yes| C
J -->|No| K[结束]
5.2 配置阶段 (L60-72)
# L61-66: 获取逻辑域大小和计算参数
num_scaleout_ranks, num_scaleup_ranks = buffer.get_logical_domain_size()
num_max_tokens_per_rank, num_tokens, hidden = args.num_tokens, max(1, args.num_tokens - dist.get_rank()), args.hidden
num_topk, num_experts = args.num_topk, args.num_experts
num_local_experts = num_experts // buffer.num_ranks
num_sms = buffer.get_theoretical_num_sms(num_experts, num_topk) if args.num_sms == 0 else args.num_sms
num_qps = buffer.get_theoretical_num_qps(num_sms) if args.num_qps == 0 else args.num_qps
关键设计:
- num_tokens = max(1, args.num_tokens - dist.get_rank()):不同 rank 有不同 token 数,模拟真实场景
- num_sms 和 num_qps 默认自动计算(args.num_sms == 0),也可手动指定
5.3 Expert 选择构造 (L74-81)
# L75-76: 生成不平衡的 expert 选择
scores = get_unbalanced_scores(num_tokens, num_experts, buffer.num_ranks, num_topk,
args.unbalanced_ratio, args.precise_unbalanced_ratio)
topk_weights, topk_idx = torch.topk(scores, num_topk, dim=-1, largest=True, sorted=False)
topk_idx = topk_idx.to(deep_ep.topk_idx_t)
# L78-81: 可选的 masked ratio(模拟 -1 索引)
if args.masked_ratio > 0:
rand_mask = torch.rand_like(topk_idx, dtype=torch.float)
topk_idx.masked_fill_(rand_mask < args.masked_ratio, -1)
topk_weights.masked_fill_(topk_idx < 0, 0)
get_unbalanced_scores 机制(gate.py):
- precise=True: 精确控制每个 rank 的 token 分布比例
- precise=False: 通过 factor 二分搜索逼近目标 ratio
- unbalanced_ratio=1.0 时退化为均匀分布
5.4 Reference 计算 (L107-141)
# L109-111: NCCL reference dispatch
ref_recv_x, ref_recv_topk_idx, ref_recv_topk_weights, \
ref_recv_src_token_idx, ref_num_recv_tokens_per_rank = \
ref_dispatch(x, topk_idx, topk_weights, num_max_tokens_per_rank, num_experts)
# L125-140: Reference combine(两种 recipe)
ref_y = generate_pre_combine_data(...)
ref_reduced_combined_y = ref_combine(ref_y, topk_idx, ..., *reduced_combine_recipe)
ref_combined_y = ref_combine(ref_y, topk_idx, ..., *combine_recipe)
Reference dispatch 算法(refs.py L10-123):
1. 按目标 rank 分组 token
2. dist.all_to_all_single 交换数据
3. 本地 expert 之外的 topk_idx 设为 -1
Reference combine 算法(refs.py L177-243):
- grouped_reduce: 按 group_id 分组累加
- 支持 (reduce_in_local, reduce_in_scaleup) 两种 reduction 级别
5.5 四种 Dispatch 模式 (L143-177)
# L144-153: 标准 dispatch
recv_x, recv_topk_idx, recv_topk_weights, handle, dispatch_event = \
launch(buffer, 'dispatch', with_previous_event, async_with_compute_stream, dispatch_args)
# L157-159: Expanding dispatch(do_expand=True)
expanded_recv_x, expanded_recv_topk_idx, expanded_recv_topk_weights, expanded_handle, expanded_dispatch_event = \
launch(buffer, 'dispatch', with_previous_event, async_with_compute_stream, expanded_dispatch_args)
# L163-170: Cached dispatch(复用 handle)
cached_recv_x, cached_recv_topk_idx, cached_recv_topk_weights, cached_handle, cached_dispatch_event = \
launch(buffer, 'dispatch', with_previous_event, async_with_compute_stream, cached_dispatch_args)
# L173-177: Cached expanding dispatch + zero padding
cached_expanded_recv_x, _, cached_expanded_recv_topk_weights, _, _ = \
launch(buffer, 'dispatch', with_previous_event, async_with_compute_stream, cached_expanded_dispatch_args)
四种模式对比:
| 模式 | do_expand | handle 来源 | 特殊参数 |
|---|---|---|---|
| 标准 | False | None | 完整参数 |
| Expanding | True | None | use_tma_aligned_col_major_sf=True |
| Cached | False | 复用 handle | 仅需 x |
| Cached Expanding + Zero Padding | True | 复用 expanded_handle | do_zero_padding=True |
5.6 Combine 输入构造 (L179-206)
# L186-195: 标准 combine 输入
src_token_global_idx = handle.recv_src_metadata[:num_recv_tokens, 0]
local_y = generate_pre_combine_data(src_token_global_idx, num_max_tokens_per_rank, num_topk, hidden)
local_y[recv_topk_idx[:num_recv_tokens] == -1] = 0
local_reduced_y = ordered_accumulate(local_y)
input_for_combine = torch.empty_like(recv_x_bf16, dtype=torch.bfloat16, device='cuda')
input_for_combine[:num_recv_tokens] = local_reduced_y
# L197-206: Expand combine 输入(按 slot 索引 scatter)
input_for_expand_combine = torch.empty((expanded_recv_x_bf16.shape[0] + 1, hidden), ...)
input_for_expand_combine[expanded_handle.recv_src_metadata[:num_recv_tokens, 2:].flatten()] = local_y_expand.view(-1, hidden)
input_for_expand_combine = input_for_expand_combine[:-1, ...] # 移除额外行
generate_pre_combine_data 设计(refs.py L126-153):
- 使用确定性公式生成:y[j,k] = sin((token_seeds * P % max_seed + 1) / max_seed * (k+1) + sin(seed))
- P=131071(大素数),token_seeds = src_token_global_idx * num_topk + j
- 保证所有 rank 可独立生成相同数据,无需通信
5.7 Combine 执行 (L208-231)
# L209-217: 标准 combine
combined_x, combined_topk_weights, combine_event = \
launch(buffer, 'combine', with_previous_event, async_with_compute_stream, combine_args)
# L219-231: Reduced combine(expand 模式)
reduced_combined_x, reduced_combined_topk_weights, reduced_combine_event = \
launch(buffer, 'combine', with_previous_event, async_with_compute_stream, reduced_combine_args)
6. API 使用模式
6.1 dispatch() 参数完整列表
dispatch_args = dict(
x=x, # 输入 token [num_tokens, hidden]
topk_idx=topk_idx, # top-k expert 索引 [num_tokens, num_topk]
topk_weights=topk_weights, # top-k 权重 [num_tokens, num_topk]
num_sms=num_sms, # 使用的 SM 数量
num_qps=num_qps, # 使用的 QP 数量
num_max_tokens_per_rank=num_max_tokens_per_rank,
num_experts=num_experts,
expert_alignment=expert_alignment, # expert 对齐(128 或 1)
async_with_compute_stream=async_with_compute_stream,
allocate_on_comm_stream=allocate_on_comm_stream,
do_handle_copy=do_handle_copy, # 是否拷贝 topk_idx
do_cpu_sync=args.do_cpu_sync, # 是否 CPU 同步
)
返回值:(recv_x, recv_topk_idx, recv_topk_weights, handle, dispatch_event)
6.2 combine() 参数完整列表
combine_args = dict(
x=input_for_combine, # combine 输入
topk_weights=recv_topk_weights, # dispatch 返回的 topk_weights
bias=bias, # 可选 bias
handle=handle, # dispatch 返回的 handle
num_sms=num_sms,
num_qps=num_qps,
async_with_compute_stream=async_with_compute_stream,
allocate_on_comm_stream=allocate_on_comm_stream,
)
返回值:(combined_x, combined_topk_weights, combine_event)
6.3 Cached Dispatch 模式
# 仅需 x 和 handle,跳过 layout 重计算
cached_dispatch_args = dict(
x=x,
num_sms=num_sms, num_qps=num_qps,
async_with_compute_stream=async_with_compute_stream,
allocate_on_comm_stream=allocate_on_comm_stream,
handle=handle, # 复用之前的 handle
)
7. 验证逻辑
7.1 Handle Copy 验证 (L363-366)
# L364: do_handle_copy=1 时 topk_idx 应该被拷贝(data_ptr 不同)
assert (topk_idx.data_ptr() != handle.topk_idx.data_ptr()) == do_handle_copy
# L365: cached handle 也应拷贝
assert (topk_idx.data_ptr() != cached_handle.topk_idx.data_ptr()) == do_handle_copy
# L366: 两次 dispatch 返回的 handle 共享同一 topk_idx
assert handle.topk_idx.data_ptr() == cached_handle.topk_idx.data_ptr()
7.2 Deterministic 验证 (L388-404)
if args.deterministic:
# 再次 dispatch,结果应 bitwise 相同
recv_x_twice, recv_topk_idx_twice, recv_topk_weights_twice, handle_twice, _ = \
launch(buffer, 'dispatch', ...)
assert torch.equal(recv_x_bf16, recv_x_twice_bf16)
assert torch.equal(recv_topk_idx, recv_topk_idx_twice[:num_recv_tokens])
assert torch.equal(recv_topk_weights, recv_topk_weights_twice[:num_recv_tokens])
7.3 Expert 计数验证 (L445-462)
# L448-453: 验证 cumulative_local_expert_recv_stats
for i in range(num_local_experts if args.do_cpu_sync else 0):
ref_count = (ref_recv_topk_idx == i).sum().item()
aligned_ref_count = align(ref_count, expert_alignment)
assert ref_count == cumulative_local_expert_recv_stats[i].item()
assert aligned_ref_count == handle.num_recv_tokens_per_expert_list[i]
# L454-462: 验证 psum 计数
for i in range(num_local_experts):
ref_count = (ref_recv_topk_idx == i).sum().item()
count = psum_num_recv_tokens_per_expert_list[i + 1] - psum_num_recv_tokens_per_expert_list[i]
expanded_count = (expanded_psum_num_recv_tokens_per_expert_list[i + 1] -
align(expanded_psum_num_recv_tokens_per_expert_list[i], expert_alignment))
assert align(ref_count, expert_alignment) == count
assert ref_count == expanded_count
7.4 Dispatch 数据 Bitwise 验证 (L471-500)
# L476-500: 逐 rank 验证数据 bitwise 一致
for i in range(buffer.num_ranks):
rank_start_idx = sum(ref_num_recv_tokens_per_rank[:i])
rank_end_idx = rank_start_idx + ref_num_recv_tokens_per_rank[i]
sorted_metadata = torch.sort(check_handle.recv_src_metadata[:, 0])
sorted_indices = sorted_metadata.indices[rank_start_idx:rank_end_idx]
# Data should be bitwise identical
for ref_t, t, do_mask in check_list:
ref_t = ref_t[rank_start_idx:rank_end_idx]
t = t[sorted_indices]
if do_mask:
ref_t = ref_t.masked_fill(ref_mask, 0)
t = t.masked_fill(ref_mask, 0)
assert torch.equal(ref_t, t)
7.5 Combine 结果验证 (L502-511)
# L503: 标准 combine 结果 bitwise 一致
assert torch.equal(combined_x, ref_combined_y), \
f'Diff: {calc_diff(combined_x, ref_combined_y)}'
# L505: Reduced combine 结果 bitwise 一致
assert torch.equal(reduced_combined_x, ref_reduced_combined_y), \
f'Diff: {calc_diff(reduced_combined_x, ref_reduced_combined_y)}'
# L507: topk_weights 应还原
assert torch.equal(combined_topk_weights, topk_weights)
7.6 Zero Padding 验证 (L424-428)
# 验证 cached expand 模式的 zero padding 正确性
for expert_idx in range(num_local_experts):
start = expanded_handle.psum_num_recv_tokens_per_expert[expert_idx].item()
end = align(start, expert_alignment)
assert (cached_expanded_recv_x_bf16[start:end] == 0).all()
assert (cached_expanded_recv_topk_weights[start:end] == 0).all()
8. 性能基准测试
8.1 测试矩阵
| 测试项 | kernel_names | 模式 |
|---|---|---|
| dispatch | ('dispatch_impl', 'dispatch_copy_epilogue_impl') |
标准 |
| expanded dispatch | ('dispatch_impl', 'dispatch_copy_epilogue_impl') |
expand |
| cached dispatch | ('dispatch_impl', 'dispatch_copy_epilogue_impl') |
cached |
| combine | ('combine_impl', 'combine_reduce_epilogue_impl') |
标准 |
| reduced combine | ('combine_impl', 'combine_reduce_epilogue_impl') |
expand |
8.2 带宽计算 (L239-336)
# L253-255: dispatch 字节数计算
num_bytes_per_dispatch_token = safe_div(count_bytes(recv_x, recv_topk_idx, recv_topk_weights), recv_topk_idx.size(0))
num_scaleup_bytes = num_bytes_per_dispatch_token * num_scaleup_recv_tokens
num_scaleout_bytes = num_bytes_per_dispatch_token * num_scaleout_send_tokens
get_combine_bytes() 函数 (L291-336):
- 计算 combine 的 scaleout/scaleup/reduction 三部分字节数
- 考虑 allow_multiple_reduction 和 allow_hybrid_mode 的不同路径
- 使用 get_unique_and_valid_dst_count() 去重计算有效目标数
8.3 bench_kineto 调用
# L256-263: dispatch 性能测试
t, copy_t = bench_kineto(lambda: buffer.dispatch(**dispatch_args),
kernel_names=('dispatch_impl', 'dispatch_copy_epilogue_impl'),
barrier_comm_profiling=True, barrier=buffer.barrier,
trace_path=get_trace_path('dispatch'))
bench_kineto 机制(testing.py L111-219):
- 使用 torch.profiler.profile 进行 kineto 级别的性能分析
- barrier_comm_profiling=True:插入 torch.cuda._sleep(2e7) + barrier 消除 CPU 启动不均衡
- 返回每个 kernel 的平均执行时间
- 可选导出 Chrome trace JSON
8.4 输出格式
# L259-263: 性能结果输出
dist_print(f' * EP: {buffer.rank_idx:3}/{buffer.num_ranks} | '
f'dispatch: '
f'{num_scaleout_bytes / t / 1e9:.0f} GB/s (SO), '
f'{num_scaleup_bytes / t / 1e9:.0f} GB/s (SU), {t * 1e6:.3f} us, {num_scaleup_bytes:.0f} bytes | '
f'copy: {2 * num_recv_tokens * num_bytes_per_dispatch_token / copy_t / 1e9:.0f} GB/s, {copy_t * 1e6:.3f} us')
9. 依赖关系
9.1 外部依赖
flowchart LR
A[test_ep.py] --> B[deep_ep.ElasticBuffer]
A --> C[deep_ep.utils.math]
A --> D[deep_ep.utils.gate]
A --> E[deep_ep.utils.envs]
A --> F[deep_ep.utils.refs]
A --> G[deep_ep.utils.testing]
C --> C1[per_token_cast_to_fp8]
C --> C2[per_token_cast_back]
C --> C3[count_bytes]
C --> C4[calc_diff]
C --> C5[align]
D --> D1[get_unbalanced_scores]
E --> E1[init_dist]
E --> E2[init_seed]
E --> E3[dist_print]
F --> F1[ref_dispatch]
F --> F2[ref_combine]
F --> F3[generate_pre_combine_data]
F --> F4[ordered_accumulate]
G --> G1[bench_kineto]
9.2 关键依赖说明
| 模块 | 用途 | 关键函数 |
|---|---|---|
deep_ep.utils.math |
FP8 转换、字节计算、对齐 | per_token_cast_to_fp8, count_bytes, align |
deep_ep.utils.gate |
不平衡 expert 分布生成 | get_unbalanced_scores, generate_rank_count |
deep_ep.utils.envs |
分布式初始化、日志 | init_dist, dist_print, init_seed |
deep_ep.utils.refs |
NCCL reference 实现 | ref_dispatch, ref_combine, generate_pre_combine_data |
deep_ep.utils.testing |
性能分析 | bench_kineto |
10. 关键设计洞察
10.1 测试的完备性设计
该测试文件展现了 工业级通信库测试 的核心特征:
- 参数空间穷举:192~288 种参数组合覆盖所有代码路径
- Reference 对比:使用纯 NCCL
all_to_all_single作为 ground truth - Bitwise 精确:不是 approximate comparison,而是
torch.equalbitwise 一致 - 多模式覆盖:standard / expand / cached / zero-padding 四种 dispatch 模式
- 确定性验证:deterministic 模式下重复执行验证一致性
10.2 generate_pre_combine_data 的巧妙设计
# 确定性公式,无需通信即可生成相同数据
y[j,k] = sin((token_seeds * P % max_seed + 1) / max_seed * (k+1) + sin(seed))
- 基于
src_token_global_idx生成,每个 rank 可独立计算 - 大素数
P=131071保证哈希均匀性 - 避免了在 combine 测试中引入额外的通信开销
10.3 allow_multiple_reduction 的分支逻辑
# L114-124: combine recipe 选择
if args.allow_multiple_reduction:
if args.allow_hybrid_mode and num_scaleout_ranks > 1:
reduced_combine_recipe = (True, True) # Hybrid: local + scaleup reduction
combine_recipe = (True, True)
else:
reduced_combine_recipe = (True, False) # Non-hybrid: local only
combine_recipe = (True, False)
else:
reduced_combine_recipe = (False, False) # No reduction
combine_recipe = (True, False)
这对应 DeepEP 的三种 combine 模式:
- (True, True): Hybrid combine(intra-scaleup reduction + global reduction)
- (True, False): Standard combine(local reduction + global reduction)
- (False, False): Multiple reduction(每个 expert slot 独立)
10.4 launch() 封装的异步模式
# L40: async_with_compute_stream 控制同步行为
values[-1].current_stream_wait() if async_with_compute_stream else ()
async_with_compute_stream=1:返回前等待 compute stream 完成async_with_compute_stream=0:立即返回,不等待
这验证了 DeepEP 的 计算流与通信流重叠 能力。
10.5 压力测试设计
# L548-557: 压力测试循环
for seed in range(int(1e9) if args.do_pressure_test else 0):
if not args.reuse_elastic_buffer:
buffer.destroy()
buffer = construct_elastic_buffer()
init_seed(seed)
test_dispatch_combine(buffer, args)
- 可选的无限循环压力测试(
--do-pressure-test) - 支持 buffer 重建或复用两种模式
- 每个 seed 重新初始化随机分布
10.6 命令行参数完整列表
| 类别 | 参数 | 默认值 | 说明 |
|---|---|---|---|
| 资源 | --num-processes |
8 | 进程数 |
| 资源 | --num-sms |
0 (auto) | SM 数量 |
| 资源 | --num-qps |
0 (auto) | QP 数量 |
| 资源 | --num-allocated-qps |
0 (auto) | 预分配 QP 数 |
| 资源 | --num-gpu-timeout-secs |
100 | GPU 超时 |
| 资源 | --num-cpu-timeout-secs |
100 | CPU 超时 |
| 模型 | --num-tokens |
4096 | Token 数 |
| 模型 | --hidden |
7168 | 隐藏维度 |
| 模型 | --num-topk |
6 | Top-k 专家数 |
| 模型 | --num-experts |
256 | 总专家数 |
| 场景 | --do-cpu-sync |
1 | 是否 CPU 同步 |
| 场景 | --allow-hybrid-mode |
1 | 允许 hybrid 模式 |
| 场景 | --allow-multiple-reduction |
1 | 允许多级 reduction |
| 测试 | --deterministic |
False | 确定性模式 |
| 测试 | --skip-check |
False | 跳过正确性检查 |
| 测试 | --skip-perf-test |
False | 跳过性能测试 |
| 测试 | --do-pressure-test |
False | 压力测试 |
| 测试 | --unbalanced-ratio |
1.0 | 不平衡比例 |
| 测试 | --masked-ratio |
0.0 | 掩码比例 |
附录:测试执行流程图
sequenceDiagram
participant Main as __main__
participant Loop as test_loop
participant Init as init_dist
participant Buf as ElasticBuffer
participant Test as test_dispatch_combine
participant Ref as ref_dispatch/combine
participant DEP as DeepEP kernels
participant Bench as bench_kineto
Main->>Loop: torch.multiprocessing.spawn
Loop->>Init: init_dist(local_rank, ...)
Init-->>Loop: (rank, world_size, group)
Loop->>Buf: construct_elastic_buffer()
Loop->>Test: test_dispatch_combine(buffer, args)
loop 192~288 参数组合
Test->>Test: get_unbalanced_scores()
Test->>Ref: ref_dispatch(x, topk_idx, ...)
Ref-->>Test: (ref_recv_x, ref_recv_topk_idx, ...)
Test->>Ref: ref_combine(ref_y, ...)
Ref-->>Test: ref_combined_y
Test->>DEP: buffer.dispatch(x, topk_idx, ...)
DEP-->>Test: (recv_x, recv_topk_idx, ..., handle)
Test->>DEP: buffer.dispatch(do_expand=True)
DEP-->>Test: expanded_recv_x, expanded_handle
Test->>DEP: buffer.dispatch(handle=handle) // cached
DEP-->>Test: cached_recv_x, cached_handle
Test->>DEP: buffer.dispatch(do_zero_padding=True)
DEP-->>Test: cached_expanded_recv_x
Test->>DEP: buffer.combine(input_for_combine, handle=handle)
DEP-->>Test: combined_x, combined_topk_weights
Test->>DEP: buffer.combine(input_for_expand_combine, handle=expanded_handle)
DEP-->>Test: reduced_combined_x
Test->>Bench: bench_kineto(dispatch)
Bench-->>Test: (t, copy_t)
Test->>Bench: bench_kineto(combine)
Bench-->>Test: (t, copy_t)
Test->>Test: assert torch.equal(combined_x, ref_combined_y)
Test->>Test: assert bitwise checks...
end
Loop->>Buf: buffer.destroy()
Loop->>Init: dist.destroy_process_group()
总结
test_ep.py 是 DeepEP 的 核心验收测试,其设计体现了以下工程哲学:
- 穷举式参数覆盖:7 维参数空间的笛卡尔积确保所有代码路径被测试
- NCCL Reference 对比:使用
dist.all_to_all_single作为 ground truth,而非近似比较 - Bitwise 精确性:所有验证使用
torch.equal,而非 tolerance-based 比较 - 性能与正确性分离:
--skip-check和--skip-perf-test允许独立运行 - 工业级压力测试:支持无限循环 + buffer 重建的 pressure test 模式
- 确定性验证:deterministic 模式下重复执行验证结果一致性
该测试文件不仅是功能验证工具,也是 DeepEP 的 性能回归测试基准,通过 bench_kineto 获取 kernel 级别的时间分解。