DeepEP 辅助工具测试源码分析:Barrier / Engram / Gate
分析范围:
tests/elastic/test_barrier.py、tests/elastic/test_engram.py、tests/utils/test_gate.py
关联实现:csrc/kernels/elastic/barrier.hpp、csrc/kernels/elastic/engram.hpp、deep_ep/utils/gate.py
1. 概览
| 测试文件 | 行数 | 测试对象 | 核心目标 |
|---|---|---|---|
tests/elastic/test_barrier.py |
62 | ElasticBuffer.barrier |
跨 rank GPU barrier 同步延迟 |
tests/elastic/test_engram.py |
124 | ElasticBuffer.engram_write / engram_fetch |
远端内存(RDMA)访问的正确性与带宽 |
tests/utils/test_gate.py |
57 | get_unbalanced_scores |
MoE 负载不均衡分布生成的统计正确性 |
这三个测试是 DeepEP 弹性 EP(Elastic EP) 基础设施的"基石性"测试——barrier 是同步原语,engram 是 RDMA 单边通信原语,gate 是 MoE 负载生成的参考实现。它们共同支撑了主 EP 测试 test_ep.py 中的 dispatch/combine 行为。
2. Barrier 测试分析
2.1 测试目标
test_barrier.py 是一个纯延迟基准:测量 buffer.barrier() 在 1000 次连续调用下的平均耗时(微秒级)。
2.2 关键代码与流程
# L19-26: 核心测试逻辑
def loop_barrier(num_tests=1000):
for i in range(num_tests):
buffer.barrier()
t = bench_kineto(lambda: loop_barrier(), 'barrier',
barrier_comm_profiling=True, barrier=buffer.barrier)
dist_print(f' > EP: {buffer.rank_idx:3}/{buffer.num_ranks:3}, '
f'barrier time: {t * 1e6:.3f} us')
设计要点:
- bench_kineto 的 barrier_comm_profiling=True 会在每次迭代前插入 torch.cuda._sleep(int(2e7))(~10ms)+ buffer.barrier(),消除 CPU 启动不平衡带来的测量噪声(参见 utils/testing.py L163-171)。
- 默认 8 进程(L54 --num-processes 8),对应单节点 8 GPU 场景。
- 支持 --do-pressure-test 无限循环压力测试(L35 int(1e9))。
2.3 底层实现路径
buffer.barrier() (elastic.py L497)
→ runtime.barrier(use_comm_stream, with_cpu_sync, sequential)
→ launch_barrier(...) (barrier.hpp L56)
→ barrier_impl<kIsScaleupNVLink, kNumSMs, kNumThreads,
kNumScaleoutRanks, kNumScaleupRanks,
kNumTimeoutCycles, kSequential> (barrier.cuh L17)
barrier_impl 的关键设计(barrier.cuh L24-39):
| 模式 | 行为 | SM 数 |
|---|---|---|
kSequential=True |
先 scaleout barrier,再 scaleup barrier(串行) | 1 |
kSequential=False |
scaleout + scaleup 并行执行 | 2(仅当 num_scaleout_ranks > 1) |
// barrier.cuh L24-39: 串行 vs 并行
if constexpr (kSequential) {
if constexpr (kNumScaleoutRanks > 1)
comm::gpu_barrier<..., kKernelBarrierTag, false, false, false>(...);
// scaleup barrier 需要 flush RDMA 请求
comm::gpu_barrier<..., kKernelBarrierTag, true, true, false>(...);
} else {
comm::gpu_barrier<..., kKernelBarrierTag, false, false, false>(...);
}
测试默认使用 sequential=True(elastic.py L497 默认值),对应 barrier.hpp L70 的 num_sms = 1,即单 SM 串行执行——这是测试同步场景的最安全选择(参见 elastic.py L505-506 注释)。
2.4 与主 EP 测试的关系
test_ep.py 中 bench_kineto 调用均传入 barrier=buffer.barrier(L258-341),用于:
- 消除 CPU 启动偏斜(launch skew)
- 在 dispatch/combine 各阶段之间建立同步点
3. Engram 测试分析
3.1 Engram 是什么
Engram 是 DeepEP 弹性模式的远端内存访问原语——通过 NCCL Gin(GPU-initiated RDMA)直接读取远端 GPU 的存储窗口(ncclWindow),无需 CPU 介入。
| API | 语义 |
|---|---|
engram_write(storage, sf) |
将本地数据注册到 NCCL window,供远端读取 |
engram_fetch(indices)() |
异步发起 RDMA get,返回 callable hook;调用 hook() 阻塞等待完成 |
3.2 测试流程
# L17-37: 配置与 buffer 创建
num_gpu_bytes, num_cpu_bytes = deep_ep.ElasticBuffer.get_engram_storage_size_hint(
args.num_entries, args.hidden, args.num_tokens * args.num_entries_per_token, dtype)
buffer = deep_ep.ElasticBuffer(
group, num_bytes=num_gpu_bytes + num_cpu_bytes, num_cpu_bytes=num_cpu_bytes,
explicitly_destroy=True, num_allocated_qps=num_qps, ...)
# L40-53: 写入阶段
local_bf16 = torch.randn((args.num_entries, args.hidden), dtype=torch.bfloat16, device='cuda')
if args.use_fp8:
local_storage, local_sf = per_token_cast_to_fp8(local_bf16)
buffer.engram_write(local_storage, sf=sf)
# L56-57: 生成随机索引
indices = torch.randint(0, num_ranks * args.num_entries,
(args.num_tokens, args.num_entries_per_token), device='cuda', dtype=torch.int)
3.3 正确性验证
# L60-71: 基于 all_gather 的参考实现
if not args.skip_check:
global_storage = torch.empty((num_ranks * args.num_entries, args.hidden), dtype=dtype, device='cuda')
dist.all_gather_into_tensor(global_storage, local_storage, group)
ref_data = global_storage[indices.view(-1)].view(args.num_tokens, -1)
for use_tma_aligned_col_major_sf in (False, True) if args.use_fp8 else (False,):
data, fetched_sf = buffer.engram_fetch(indices,
use_tma_aligned_col_major_sf=use_tma_aligned_col_major_sf)()
assert torch.equal(ref_data, data), 'data mismatch'
关键设计:
- 使用 dist.all_gather_into_tensor 作为ground truth,与 RDMA get 结果逐元素比较
- FP8 模式下测试两种 SF layout(row-major vs TMA-aligned column-major)
- 默认参数:num_entries=524288, hidden=128, num_tokens=512, num_entries_per_token=24(L108-111)
3.4 性能测量
# L79-96: issue + wait 端到端测量
def fetch_and_wait():
hook = buffer.engram_fetch(indices, use_tma_aligned_col_major_sf=True)
hook()
issue_t, wait_t = bench_kineto(
fetch_and_wait,
kernel_names=('engram_fetch_impl', 'engram_fetch_wait_impl'),
barrier_comm_profiling=True, barrier=buffer.barrier,
trace_path=...)
mpps = args.num_tokens * args.num_entries_per_token / (issue_t + wait_t) / 1e6
指标:
- issue_t:engram_fetch_impl kernel 耗时(发起 RDMA get)
- wait_t:engram_fetch_wait_impl kernel 耗时(等待完成)
- MPPS:Million Packets Per Second
- GB/s:有效带宽
3.5 底层 kernel 实现
engram_fetch_impl(engram_fetch.cuh L22-80):
// L29-35: 每个 block 对应一个 QP,warp 级并行
const auto qp_idx = static_cast<int>(blockIdx.x);
const auto warp_idx = ptx::get_warp_idx();
const auto global_warp_idx = qp_idx * kNumWarps + warp_idx;
const auto gin = handle::NCCLGin(nccl_dev_comm, nccl_window, qp_idx, NCCL_GIN_RESOURCE_SHARING_CTA);
// L55-74: 每个 warp 协作 fetch 一个 token 的多个 entry
for (int i = global_warp_idx; i < num_tokens * kNumEntriesPerToken; i += kNumQPs * kNumWarps) {
const auto global_idx = __ldg(indices + i);
const auto owner_rank_idx = global_idx / kNumEntriesPerRank;
const auto local_entry_idx = global_idx % kNumEntriesPerRank;
const auto peer_idx = owner_rank_idx / kNumRanksPerRDMAPeer;
gin.get<team_t, ncclCoopThread, ncclGin_SegmentMixed>(...);
}
核心特征:
- 1 block = 1 QP(blockIdx.x 映射到 qp_idx)
- Warp 级分工:每个 warp 负责不同 token/entry 的 RDMA get
- 支持 request 聚合(ncclGinOptFlagsAggregateRequests)与周期性 flush(kGinQPFlushDepth)
- FP8 SF packs 由 elect_one_sync() 选出的 leader warp 串行收集(L78-80 TODO 注释表明未来可优化为 warp 并行)
3.6 与主 EP 测试的关系
Engram 是弹性模式下跨节点参数/激活拉取的实验性原语。test_ep.py 未直接使用 engram,但 engram 的底层机制(NCCL Gin RDMA get)与 dispatch/combine 的 NVLink/RDMA 通信共享同一套 ncclDevComm + ncclWindow 基础设施。
4. Gate 测试分析
4.1 Gate 工具是什么
deep_ep/utils/gate.py 提供 MoE top-k 路由的负载分布生成器,用于:
- 生成具有指定不均衡比(ratio)的 expert 选择 scores
- 为 test_ep.py 提供可控的、可复现的 routing 分布
4.2 测试目标
验证 get_unbalanced_scores 生成的 top-k 分布满足:
1. 实际不均衡比 practical_ratio 与期望 ratio 的相对误差 < 10%
2. rank 间不均匀性 inequality < 1.02(非特殊 rank 间几乎均匀)
4.3 关键代码
# L7-53: 穷举参数空间测试
def test_unbalanced_scores():
for num_tokens in [1, 4096]:
for num_experts_per_rank in [1, 4, 8, 16]:
for num_ranks in [2, 4, 8, 16, 64, 72]:
num_experts = num_experts_per_rank * num_ranks
for num_topk in [1, 2, 4, 6, 8, 9]:
for ratio in [1.0, 2.0, 4.0]:
for precise in [1, 0]:
# L18-25: 边界条件检查
lower_bound_per_token = max(1, ceil_div(num_topk, num_experts_per_rank))
upper_bound_per_token = min(min(num_topk, num_ranks), int((num_ranks - 1) / ratio) + 1)
if lower_bound_per_token > upper_bound_per_token:
continue
# L27-36: 逐 rank 生成并统计
for rank_idx in range(num_ranks):
scores = get_unbalanced_scores(num_tokens, num_experts, num_ranks, num_topk, ratio, precise)
_topk_weights, topk_idx = torch.topk(scores, num_topk, dim=-1, ...)
topk_idx = topk_idx // num_experts_per_rank
... # 统计每个 rank 被选中次数
# L39-52: 验证 ratio 与 inequality
practical_ratio = total_rank_count[0].item() / max(total_rank_count[1:].min().item(), 1)
inequality = total_rank_count[1:].max().item() / max(total_rank_count[1:].min().item(), 1)
if precise:
assert abs(practical_ratio - ratio) / ratio < 0.1 and inequality < 1.02
4.4 底层算法
gate.py 提供两种模式(L176-180):
| 模式 | 函数 | 特点 |
|---|---|---|
precise=True |
get_precise_unbalanced_scores |
精确控制 rank count 分布,保证 ratio 精度 |
precise=False |
get_random_unbalanced_scores |
通过 factor 二分搜索逼近 ratio,更随机 |
generate_rank_count(L32-113)的核心逻辑:
1. 随机生成 top-k indices,统计每个 token 覆盖的 rank 数 a[i]
2. 根据 ratio 计算 special rank 的 token 数 special_token_count
3. 通过 permutation 将 special rank 插入到选定 token 的 top-k 中
4. scatter_add_ 统计最终 rank count
generate_topk_idx(L4-29)将 rank count 映射为具体 expert indices:
- 在每个 rank 内随机排列 expert 顺序
- 按 rank count 截断得到 topk_idx
4.5 与主 EP 测试的关系
test_ep.py L13 导入、L75 调用:
scores = get_unbalanced_scores(num_tokens, num_experts, buffer.num_ranks, num_topk,
args.unbalanced_ratio, args.precise_unbalanced_ratio)
这使得主 EP 测试可以模拟真实 MoE 场景中的负载不均衡(如某些 expert 被过度选中),验证 dispatch/combine 在极端分布下的正确性与性能。
5. API 使用模式总结
5.1 分布式测试模式
三个测试均采用 torch.multiprocessing.spawn 启动多进程:
# 标准入口模式
@torch.inference_mode()
def test_loop(local_rank, num_ranks, args):
rank, num_ranks, group = init_dist(local_rank, num_ranks)
buffer = deep_ep.ElasticBuffer(group, ...)
# ... 测试逻辑
buffer.destroy()
dist.destroy_process_group()
if __name__ == '__main__':
torch.multiprocessing.spawn(test_loop, args=(num_processes, args), nprocs=num_processes)
5.2 性能测量模式
统一使用 bench_kineto + buffer.barrier 消除 CPU 启动偏斜:
t = bench_kineto(fn, kernel_names=...,
barrier_comm_profiling=True, barrier=buffer.barrier,
trace_path=...)
5.3 正确性验证模式
- Barrier:无显式正确性断言(barrier 的正确性由 NCCL 保证),纯性能测量
- Engram:
torch.equal与all_gather参考实现逐元素比较 - Gate:统计检验
practical_ratio与inequality的数值边界
6. 三者协作关系图
┌─────────────────────┐
│ test_ep.py │
│ (主 EP 测试) │
└─────────┬───────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ barrier │ │ engram │ │ gate │
│ (同步原语) │ │ (RDMA 原语) │ │ (负载生成) │
└──────┬──────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ barrier_impl│ │engram_fetch_ │ │generate_rank_│
│ (NCCL Gin │ │impl / wait │ │count / │
│ GPU barrier)│ │(RDMA get) │ │topk_idx │
└─────────────┘ └──────────────┘ └──────────────┘
| 原语 | 角色 | 被谁依赖 |
|---|---|---|
| barrier | 跨 rank 同步 + 性能测量校准 | test_ep.py, test_engram.py |
| engram | 远端内存访问(实验性) | 独立测试,基础设施共享 |
| gate | 可控负载分布生成 | test_ep.py |
7. 关键发现与洞察
7.1 Barrier 的"测试模式"设计
barrier(sequential=True) 是测试专用路径:单 SM 串行执行 scaleout + scaleup barrier,牺牲性能换取同步确定性。这揭示了 DeepEP 的一个设计哲学——生产路径追求并行度,测试路径追求可预测性。
7.2 Engram 的"异步 hook"模式
engram_fetch 返回 callable 而非直接阻塞,这是 lazy evaluation 模式:
hook = buffer.engram_fetch(indices) # 异步发起
# ... 可插入计算 ...
hook() # 阻塞等待,返回 (data, sf)
这与 dispatch/combine 的 event 参数(with_previous_event)设计一脉相承,允许用户重叠计算与通信。
7.3 Gate 的"精确 vs 随机"双模式
precise=True 通过 generate_rank_count 直接构造 rank count 矩阵,保证统计精度;precise=False 通过 factor 二分搜索逼近,更贴近真实 routing 的随机性。这种双模式设计使得测试既能精确验证边界条件,又能模拟真实场景的统计波动。
7.4 测试覆盖的参数空间
test_gate.py 的穷举参数空间极广:
- num_tokens × num_experts_per_rank × num_ranks × num_topk × ratio × precise
- 总计约 2 × 4 × 6 × 6 × 3 × 2 = 1728 种组合(经边界条件过滤后仍有数百种)
这种组合爆炸式测试确保了 get_unbalanced_scores 在各种极端配置下的鲁棒性。
8. 与博客理论的对应关系
| 博客概念 | 测试体现 |
|---|---|
| GPU-centric fabric | engram_fetch_impl 中 SM 直接操作 Gin QP,无 CPU 介入 |
| 三阶段流水线 | barrier_impl 的 scaleout → scaleup 串行执行 |
| 对称内存 | engram_write 注册到 ncclWindow,远端可直接访问 |
| MoE 负载不均衡 | get_unbalanced_scores 的 ratio 参数直接建模 |
9. 文件路径索引
| 文件 | 行数 | 作用 |
|---|---|---|
tests/elastic/test_barrier.py |
62 | Barrier 延迟测试 |
tests/elastic/test_engram.py |
124 | Engram 正确性 + 带宽测试 |
tests/utils/test_gate.py |
57 | Gate 统计正确性测试 |
deep_ep/buffers/elastic.py |
L497-604 | barrier / engram_write / engram_fetch Python API |
csrc/elastic/buffer.hpp |
L181-208 | C++ barrier 实现 |
csrc/kernels/elastic/barrier.hpp |
87 | Barrier JIT runtime |
csrc/kernels/elastic/engram.hpp |
160+ | Engram fetch/wait JIT runtime |
deep_ep/include/deep_ep/impls/barrier.cuh |
42 | barrier_impl CUDA kernel |
deep_ep/include/deep_ep/impls/engram_fetch.cuh |
80+ | engram_fetch_impl CUDA kernel |
deep_ep/utils/gate.py |
181 | 负载分布生成算法 |
deep_ep/utils/testing.py |
L111-190 | bench_kineto 性能测量框架 |