MPI RDMA Burst 流量测试方案

架构概述

之前使用过chatgpt和claude协助写过一些测试脚本,比如早期的:RDMA测试脚本 by ChatGPT?!,后来优化的就没更新在blog中了。以至于后来研发的同事帮忙优化的测试脚本,虽然目前可以达到一定需求,但python精度太差,都是ms级精度控制,效率不是很高。因此需要一套高性能RDMA测试脚本,满足不同场景下针对交换机的测试需求。目前看上去使用openmpi来控制perftest的思路比较合理,而且有的用户也在使用类似的方式,所以就尝试走这条路。以下是五一期间跟claude合作开发的测试系统,里面很多细节还有待完善中

设计思想

本方案采用 MPI 指挥 + perftest 执行的分离架构:

  • MPI(mpi4py):只做”指挥官”——进程启动、全局同步(Barrier)、决策广播(Bcast)
  • perftest(ib_write_bw):做”士兵”——真正发送 RDMA Write 流量,由 HCA 硬件直接执行
  • 流量控制:通过 SIGSTOP/SIGCONT 信号(training 模式,毫秒级突发)、perftest 内置 rate_limit/burst_size(storage 模式,微秒级突发,但并行打流时机会错开)、或两者结合(sync_burst 模式,微秒级突发,同步并行打流)

文件结构

/root/rdma_burst/
├── config.py           # 服务器/网卡配置(扩展时只改这个文件)
├── run.py              # 部署+启动入口(生成 hostfile、nic_map,rsync,调 mpirun)
├── perftest_burst.py   # MPI 主程序(每个 rank 启动/控制 perftest 子进程)
├── nic_wrapper.sh      # MPI rank 的 NIC 绑定 + CPU 亲和性
├── libsigburst.c       # C 共享库源码(sync_burst 模式用,μs 精度信号控制)
├── libsigburst.so      # 自动编译:deploy 时在每台服务器上 gcc 编译
├── hostfile            # 自动生成:MPI hostfile
└── nic_map.txt         # 自动生成:rank → NIC/IP/CPU 映射

执行流程

run.py
  ├── 1. 读取 config.py → 生成 hostfile + nic_map.txt
  ├── 2. rsync 所有文件到每台服务器
  ├── 3. gcc 编译 libsigburst.so(每台服务器)
  ├── 4. pkill 残留 perftest 进程
  └── 5. exec mpirun → 每台服务器启动 N 个 rank
          │
          └── 每个 rank (perftest_burst.py)
                ├── 读取 nic_map.txt 确定自己的 NIC/IP
                ├── 根据 mode 计算流量对 (我要跟谁通信)
                ├── Phase 1: 启动 perftest server 进程 (接收端)
                ├── Barrier 同步
                ├── Phase 2: 启动 perftest client 进程 (发送端)
                │     └── TCP 握手用管理网 IP (10.26.x.x)
                │         RDMA 数据由 -d mlx5_X 决定走哪张卡
                ├── Phase 3: 根据模式初始化
                └── Phase 4: burst 循环或等待 duration

通信路径

系统中存在三条独立的通信路径:

  1. SSH 路径(启动用):run.py 通过 SSH 管理网(10.26.4.x)把文件 rsync 到远程服务器,mpirun 通过 SSH 启动远程 rank 进程。仅用于部署和进程启动。
  2. MPI 控制路径(同步用):MPI 的 Barrier/Bcast 走 RDMA DC transport(UCX),通过 RDMA 网卡(mlx5_X)通信。hostfile 中的管理网 IP 仅用于 SSH 启动进程,MPI 运行时通过 nic_wrapper.sh 的 UCX_NET_DEVICES 自动发现 RDMA 路径。MPI 走 RDMA 而不走管理网 TCP,是因为 RDMA Barrier 延迟 ~3μs,TCP 要 50-100μs,同步精度差距大
  3. perftest 数据路径(流量用):perftest 的 TCP 握手(交换 QP 元数据)走管理网 10.26.4.x(避免环境中使用VRF隔离的复杂场景),RDMA Write 数据传输由 -d mlx5_X 决定走哪张卡

队列隔离机制

不管是RC亦或是UC模式,MPI 控制报文始终走 queue 0(tclass=0),perftest 数据走 queue 1(tclass=32)。这解决了如下场景中遇到的问题:

  • UC/RC 模式 MPI 也在 queue 1(tclass=32),无 WRED 时 tail drop 无差别丢弃 MPI 和 perftest 包,导致MPI 崩溃

ucx_info -c 显示 UCX_DC_MLX5_TRAFFIC_CLASS=auto 是编译时默认值,运行时由 mpirun -x UCX_IB_TRAFFIC_CLASS=0 覆盖,已在run.py中实现:

root@tme92:~/rdma_burst# ucx_info -c | grep -i TRAFFIC_CLASS
UCX_DC_MLX5_TRAFFIC_CLASS=auto
UCX_RC_VERBS_TRAFFIC_CLASS=auto
UCX_RC_MLX5_TRAFFIC_CLASS=auto
UCX_UD_VERBS_TRAFFIC_CLASS=auto
UCX_UD_MLX5_TRAFFIC_CLASS=auto

RC vs UC 传输模式(–transport)

区别

RC(Reliable Connection,默认):有 ACK、有重传。丢包后 QP 检测到 NAK/超时 → 重传 → 有效速率下降。即使关闭服务器 DCQCN(roce_rp/roce_np),RC 本身的重传机制仍然会降速,CX7 仍然响应 ECN CE bit 生成 CNP。这意味着前几轮 burst 是 400G,后续 burst 因重传导致速率逐渐降低

UC(Unreliable Connection):支持 RDMA Write,但不做 ACK、不做重传——发送端”发了就忘”。丢了就丢了,速率不受任何影响。每轮 burst 都是真正的全新 400G 全速

使用场景

目标用 RC 还是 UC
测交换机 Buffer/OQ 水线(不受端侧干扰)UC
测 ECN+DCQCN 端到端收敛效果RC
模拟真实训练/存储业务RC

三种流量模式

all2all(全互联)

每个端点向所有其他端点发送。N 个端点 → N×(N-1) 条单向流,每个 egress port 压力被分散到多个目的端口

4 端点:12 条流,每卡:3 出 + 3 入
16 端点:240 条流,每卡:15 出 + 15 入

allreduce(双向 Ring)

端点排成环,正反两个方向同时通信。N 个端点 → 2×N 条单向流。每卡只跟环上两个邻居通信。

incast(N 打 1)

所有端点向同一个目标发送,N-1 条流全部汇聚到1个 egress port。此场景中目标端口 egress buffer 承受的压力最大。

各规模下的流数速查

配置端点数all2all 每卡出流allreduce 每卡出流incast 入流(目标卡)
2台4卡4323
8台16卡1615215
9台18卡1817217

突发模型 – Training 模式(默认)

控制机制

MPI 用 SIGSTOP/SIGCONT 信号冻结/解冻 perftest 进程

时间精度

毫秒级。瓶颈在于 SIGCONT 后 perftest 不知道何时停——它会不停地发,直到被 SIGSTOP。而 SIGSTOP 从 Python 调 os.kill() 到内核投递信号到进程被挂起,延迟和抖动在几十到几百 μs,无法精确控制 5μs 级的 burst。所以 training 模式最小可靠 burst ~1-2ms。

Burst 期间行为

所有 perftest 进程被 SIGCONT 唤醒后以 400G 线速连续发送。所有端口通过 MPI Barrier全局同步 burst——burst 开始瞬间所有端口同时从 0 跳到 400G。Grafana 上看到的带宽 = 400G × 占空比,假设burst=100ms,pause=0-500ms

python3 run.py all2all --burst-ms 100 --pause-min 0 --pause-max 0 --duration 60
rank bw = 400G x burst_ms / (burst_ms + 平均 pause_ms)
        = 400G x 100 / (100+250)
        = 400G x 28.6%
        = ~114 Gbps/卡

时间轴

|===100ms burst===|---273ms pause---|===100ms burst===|
     400G 线速          0 流量             400G 线速
  (不停 post WQE)      (SIGSTOP)        (不停 post WQE)
  (Python全程sleep,除了负责触发定时信号:SIGCOUNT和SIGSTOP)

模式流程

整体过程概述:
1. Barrier         → 所有 rank 同步
2. SIGCONT         → 唤醒 perftest(开始发数据)
3. sleep(burst_ms) → Python sleep 100ms,这期间 perftest 在全速 400G 发包
4. SIGSTOP         → Python 醒来后冻结 perftest(停止发数据)
5. Bcast(pause-min,pause-max) → rank0 广播 pause 时长,广播时长3-5us
6. sleep(pause-min,pause-max) → Python 睡,这期间 perftest 已被冻结,无流量
7. 回到 1
启动阶段:
1.  启动所有 perftest server (--run_infinitely)
2.  Barrier → 所有 rank 同步, 确保 server 就绪
3.  启动所有 perftest client (--run_infinitely, 无 rate_limit)
4.  等待连接建立 (sleep 4s)
5.  SIGSTOP 所有 perftest → 冻结, 准备受控 burst
6.  Barrier → 所有 rank 确认就绪

Burst 循环 (每轮 MPI 都参与):
7.  Barrier → 所有 rank 同步
8.  SIGCONT → 唤醒所有 perftest (开始发数据)
9.  sleep(burst_ms) → Python sleep xxxms, perftest 全速 400G 发包
10. SIGSTOP → Python 醒来后冻结 perftest (停止发数据)
11. Bcast(pause-min,pause-max) → rank0 广播 pause 时长,广播时长 ~3-5μs
12. sleep(pause-min,pause-max) → Python 睡, perftest 冻结, 无流量
13. elapsed < duration → 回到 7; 否则跳出

结束阶段:
14. kill 所有 perftest 进程
15. MPI Finalize

突发模型 – Storage 模式

控制机制

perftest 内置的 –rate_limit + –burst_size。MPI 不做 SIGSTOP/SIGCONT。

时间精度

纳秒-微秒级。perftest 的 C 代码在用户态直接向 HCA post WQE,全程不经过内核。

Burst 期间行为

每条流的 perftest 进程独立运行,各自以 burst_size 为单位发送——发完一批后等到 rate_limit 计时器到期再发下一批。各流没有 MPI Barrier 同步,启动后自然漂移,burst 时刻互相错开(多条流由独立 perftest 进程驱动,各进程 CPU 调度、HCA 仲裁有微小差异,burst 时自然散开)。单条流是 bursty 的,但多条流叠加后端口持续有流量,微观上是 bursty。Grafana 上看到的带宽 = rate_limit × 每卡出流数(上限 400G),假设msg_size=4096B,burst_size=61,rate_limit=25Gbps:

python3 run.py all2all --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 25 --duration 60

burst 时长 = burst_size × msg_size × 8 / 400Gbps
cycle 时长 = burst_size × msg_size × 8 / rate_limit
pause 时长 = cycle 时长 - burst 时长

burst time1 = burst_size × msg_size × 8 / 400Gbps
            = 61 x 4096 x 8 / 400Gbps
            = 0.000005 s
            = 5us

burst time2 = burst_size × msg_size × 8 / 25Gbps
            = 61 x 4096 x 8 / 25Gbps
            = 0.00007995 s
            = 79us

pause time = burst time2 - burst time1 = 79-5 = 74us

时间轴

|===5us burst===|---74us pause---|===5us burst===|---74us pause---|
   400G 线速         0 流量           400G 线速        0 流量
 (HCA满速61个WQE)  (不 post WQE)    (HCA满速61个WQE)  (不 post WQE)
场景每卡出流rate_limitGrafana 每端口
4 卡 all2all3120~360 Gbps
16 卡 all2all1525~375 Gbps
16 卡 allreduce2200~400 Gbps

注意事项

  • rate_limit 必须是整数(10.0 不行,必须是 10)
  • rate_limit 和 –run_infinitely 不兼容,storage 模式 client 自动改用 -D
  • rate_limit/burst_size 是 client 端参数,server 端不能传
  • rate_units 用字符串:g(Gbps)、M(Mbps)、p(Mpps)
  • CX7 HW rate limit 支持离散值:2.5, 5, 10, 14, 20, 25, 30, 40, 56, 60, 80, 100, 112, 120, 168, 200, 300 Gbps。不匹配时自动 fallback 到 SW rate limiter(对测试无影响)
  • rate_limit 是单条流平均速率,Grafana = rate_limit × 每卡出流数(上限 400G),打满 400G 时 16 卡 all2all 用 25,allreduce 用 200
  • 此模式下各 perftest 进程独立运行,burst 时刻自然漂移,统计复用把压力平滑化

模式流程

启动阶段:
1.  启动所有 perftest server (--run_infinitely, 等待 client 连接)
2.  Barrier → 所有 rank 同步, 确保 server 就绪
3.  启动所有 perftest client (带 --rate_limit, --burst_size, -D duration+10s)
    │
    └── client 启动后内部自动执行:
        a. TCP 握手 (连接管理网 IP, 交换 QP 元数据, ~1-2s)
        b. QP ready → 进入 burst 循环 ← 流量从这里开始,持续 -D 自行退出或被 kill
           ├── post burst_size 61 个 WQE, 每个4096B → HCA 400G 线速 burst 5us
           ├── 61×4096 = 249,856 B, 由于rate_limit 25Gbps 应分摊79us
           ├── 但实际只用了 5μs,pending 74us后才能post 下一批 
           ├── 轮询 clock_gettime(), 空等 74μs, 不 post 任何 WQE, 无流量
           └── 74μs 到了 → 回到步骤 1, post 下一批 61 个 WQE
4.  sleep(4s) → Python 等连接建立, 此时 perftest 可能已经在发数据了
5.  检查进程存活

MPI 监控 (与步骤 6-8 并行):
6.  每 10s 检查 perftest 进程是否存活
7. elapsed ≥ duration → 跳出

结束阶段:
8. kill 所有 perftest 进程
9. MPI Finalize

突发模型 – sync_burst 模式

控制机制

MPI Barrier 跨节点同步 + C 库 killpg(SIGCONT) 并行唤醒同一服务器所有 client + perftest burst_size 硬件控制 burst 时长。SIGCONT 只负责”开始”,burst_size 负责”结束”。

时间精度

training 模式 burst 时长精度 ±1ms(不够细),storage 模式各流不同步(不产生同步冲击)。sync_burst 结合两者优点。Burst 时长由 HCA 硬件保证(纳秒级),跨节点同步窗口 ~10-15μs(MPI Barrier ~3μs + killpg 信号投递 ~5-10μs)。最小可用 burst ~20μs(需 > 同步窗口才能保证所有流重叠)。pause 精度 ~1ms(受 Python sleep + MPI Barrier 限制,但 pause 不需要精确)。

Burst 期间行为

所有端口通过 MPI Barrier 同步后,C 库一次 killpg 唤醒本节点所有 perftest client。各 client 恢复后发现 rate_limit 计时器已到期,立即 post burst_size 个 WQE,HCA 以 400G 线速发出。发完后进入 rate_limit 内部 pause,随即被 C 库的 killpg(SIGSTOP) 冻结,等待下一轮 MPI Barrier。假设同 msg_size=65536B,burst_size=61,sync-pause-ms 2 默认值:

python3 run.py incast --burst-profile sync_burst --msg-size 65536 --burst-size 61  --transport UC --sync-pause-ms 2.0 --duration 120

burst 时长 = burst_size × msg_size × 8 / 400Gbps
cycle 时长 = burst_size × msg_size × 8 / rate_limit
pause 时长 = cycle 时长 - burst 时长

burst time1 = burst_size × msg_size × 8 / 400Gbps
            = 61 x 65536 x 8 / 400Gbps
            = 80us

wait_us time= 80 + 100 = 180μs

rate_limit < burst_data / (burst_time1 + 100μs)
           < (61 × 65536 × 8)/ wait_us time
           < 31,981,568 / 180us
           <  177Gbps
           → HW 向下对齐 → 168 Gbps

burst time2 = burst_size × msg_size × 8 / rate_limit
            = 61 x 65536 x 8 / 168Gbps
            = 190us

rate_limit:sync_burst 模式内部自动计算 rate_limit,使 perftest 的内部 pause 远短于 sync_pause_ms。计算值会向上对齐到 CX7 HW rate limiter 支持的离散值,避免 fallback 到 SW rate limiter(UC + SW rate limiter 有兼容问题会导致进程崩溃)。CX7 HW 支持的 rate_limit 值同training:2.5, 5, 10, 14, 20, 25, 30, 40, 56, 60, 80, 100, 112, 120, 168, 200, 300 Gbps

wait_usburst_then_stop() 的 busy_wait 参数设为 burst_us + 100μs(perftest_burst.py 里的硬编码)。100μs 是给 SIGSTOP 信号投递留的余量窗口,跟 burst 时长无关,不需要调整。cycle 输出中包含这个余量:cycle = burst + 100μs + 信号开销 ~10μs

重叠问题

重叠指的是”所有流同时在发包的时间段”。只有在重叠窗口内,所有流同时打向交换机同一个 egress port,才能制造 buffer 冲击,比如:同步窗口 = 15μs (最早的流 t=0 开始, 最晚的流 t=15μs 开始),burst 时长必须 > 同步窗口才能保证所有流在时间上重叠

5μs burst (不重叠):
  flow1:  t=0  |=5μs=|                    发完了
 flow15:  t=15          |=5μs=|           才开始
  → 流1 和流15 从来没有同时在发 → 不制造同步冲击

50μs burst (重叠 35μs):
  flow1:  t=0  |==========50μs==========|
 flow15:  t=15          |==========50μs==========|
                        |====35μs====|
                        ↑ 这 35μs 内所有流同时在打 → buffer 被冲击

其中跨节点同步窗口10~15us是根据信号链路估算的:

MPI Barrier 完成 (各节点时钟对齐, 抖动 ~3μs)
  → Python 调用 C 库 (~1μs)
    → C 库 killpg() 系统调用 (~3μs)
      → 内核遍历进程组, 发 IPI 中断 (~3-5μs)
        → 各 CPU 唤醒进程, post WQE (~3-5μs)

最早的流(本节点第一个被唤醒的)到最晚的流(另一节点最后一个被唤醒的)之间的总延迟 ≈ 10-15μs。实际值可以从输出中间接验证——如果 burst_size=20μs 时开始看到丢包效果,说明重叠存在,同步窗口 < 20μs。

不过要注意,这里所谓的“丢包效果”,都是指进入burst循环。在程序刚启动时,为了保障所有节点都正常建立(从第一个node开启client,到最后一个node开启client),会预留一些时间,保障不会出现burst开始循环时,还有client还没开始建立。因此在incast场景中的这个时刻,很容易把buffer打超而产生丢包,过了这段时间后,就可以观察“丢包效果”了。

时间轴

|===50μs burst===|---------~2ms pause---------|===50μs burst===|
   400G 线速        perftest 被 SIGSTOP 冻结        400G 线速
  (61 个 WQE)       Python: sleep + Barrier       (61 个 WQE)
   (C 库控制)             (Python 控制)             (C 库控制)

一轮完整时间线,如下红色flag是可以控制的:

|←Barrier→|←── C 库 burst_then_stop() ──→|←── Python sleep ──→|
   ~3μs    SIGCONT  burst   wait  SIGSTOP     sync_pause_ms
           ~5-15μs  ~80μs   ~100μs  ~5μs          ~2ms
            |        |              |
            |  HCA 400G send        |
            |  (burst_size 61 WQE)  |
            |                       |
            └────── key path ───────┘
             全程 C 代码, 零 Python
         busy_wait 总计 = 80+100 = 180μs

关于中间的burst和wait的时间,还有后面2ms 的关系:2ms sync_pause 发生在 SIGSTOP 之后——perftest 已经被冻结了。问题出在 SIGCONT 到 SIGSTOP 之间的 180μs 窗口内,perftest 是自由运行的,没有任何外部控制。如果它的内部 cycle < 180μs,它会在这个窗口内自行启动第二轮 burst

t=0μsMPI Barrier不可控, RDMA 固有 ~3μs
t=3μsC 库: killpg(SIGCONT)不可控, 内核信号 ~5-10μs
t=8-18μs各 client 陆续恢复 (同步窗口) 【不可控, ~10-15μs】
检查 rate_limit 计时器 → 已到期
立即 post 61 个 WQE
不可控, ~10-15μs
t=8-98μsHCA 400G 线速发出 61 个 WQE
15 条流同时打向 rank 0 端口
重叠窗口 ≈ 80 – 15 = 65μs
可控: –burst-size × –msg-size
t=98μsburst 结束, 进入 rate_limit 内部 pause (perftest 轮询等 240μs 计时器到期,按照100Gbps算的)
(但不会等完, 因为 SIGSTOP 会提前打断)
不可控, 由 rate_limit 自动决定
t=183μsC 库: killpg(SIGSTOP)
冻结所有 client
(此时内部 pause 才过了 85μs, 还剩 155μs)
(perftest 不知道自己被冻了, 下次醒来继续看计时器)
不可控, wait_us=burst+100 硬编码
t=183μs+Python: time.sleep(2ms)
perftest 冻结, 无流量
可控: –sync-pause-ms
t=2183μsPython 醒来, MPI Barrier
rank0 Bcast: 继续?
不可控
t=2190μs下一轮 SIGCONT
client 恢复, 被冻结了 2000μs
内部 pause 只需 240μs, 早已到期
→ 立即 post 下一批 61 个 WQE

其中240μs 的来源就是 burst 数据量 / rate_limit - burst 时长。rate_limit=100G 意思是”这批数据按 100G 的速率应该分摊到 320μs”,但实际 80μs 就发完了,所以 perftest 空等 240μs 凑够 320μs。但这个 240μs 的等待会被 SIGSTOP 在 85μs 处打断,永远等不完——所以 240μs 这个值对实际行为没有任何影响,只要它 > 85μs(保证 SIGSTOP 落在 pause 里)就行

模式流程

启动阶段:
1.  启动所有 perftest server (--run_infinitely)
2.  Barrier → 所有 rank 同步, 确保 server 就绪
3.  启动所有 perftest client (带 --rate_limit, --burst_size, -D duration+10s)
    │
    └── client 启动时加入同一个进程组 (第一个 client 创建, 后续加入)
        QP 建立后立刻开始发数据 → 立刻被步骤 4 的 SIGSTOP 冻结
4.  sleep(4s) → 等连接建立
5.  SIGSTOP 所有 perftest → 冻结, 准备受控 burst
6.  Barrier → 所有 rank 确认就绪
7.  加载 C 库 libsigburst.so

Burst 循环:
8.  rank0 判断是否继续 → Bcast 给所有 rank (避免各 rank 独立判断导致 MPI 死锁)
9.  Barrier → 所有 rank 同步 (~3μs)
10. C 库 burst_then_stop(pgid, wait_us):
    ├── killpg(SIGCONT)        一次 syscall 并行唤醒本节点所有 client (~5-10μs)
    │   └── perftest 恢复, 检查 rate_limit 计时器 → 已到期 (因为被 SIGSTOP 了很久)
    │       └── post xxx 个 WQE → HCA 400G 发出 (197μs)          ← burst 
    │       └── HCA 以 400G 线速发出 (burst, 设置时间~50μs)
    ├── busy_wait(burst+100μs)  C 层 μs 精确等待, 确保 burst 完成
    └── killpg(SIGSTOP)         一次 syscall 并行冻结所有 client (此时在 rate_limit 内部 pause 中)
11. sleep(sync_pause_ms) → Python 睡 ~2ms, perftest 已冻结, 无流量
12. 回到 8

结束阶段:
13. kill 所有 perftest 进程
14. MPI Finalize

三种突发模式对比

维度TrainingStoragesync_burst
burst 期间perftest 不停 post,无数量限制post 恰好 burst_size 个post 恰好 burst_size 个
burst 结束触发Python 醒来发 SIGSTOPperftest 发完自动停perftest 发完自动停,C 库随后 SIGSTOP
发送和结束方式串行,一个一个发无外部控制,perftest 内部计时器自行循环一次 syscall,内核并行投递
发送和结束速度15 个 client ≈ 750μs不适用(无 SIGSTOP/SIGCONT)15 个 client ≈ 10μs
burst 时长控制Python sleep(±1ms)burst_size(纳秒级)burst_size(纳秒级)
pause 触发SIGSTOP 冻结rate_limit 计时器等待C 库 SIGSTOP 冻结
pause 时长控制rank0 随机选(0-500ms)rate_limit 自动计算(μs 级)sync_pause_ms 固定(~2ms)
跨节点同步MPI Barrier(每轮)无(各流独立)MPI Barrier + C killpg(每轮)
下一轮如何开始Barrier → SIGCONTrate_limit 计时器到期Barrier → C 库 SIGCONT
Python 角色定时发信号旁观监控管理 pause 期间的同步
C 库角色burst 关键路径(SIGCONT→wait→SIGSTOP)
重叠情况重叠问题几乎不存在。burst 100ms,同步窗口 ~15μs,重叠 99.985ms ≈ 99.99%。ms 级 burst 下同步窗口可以忽略不存在重叠的概念。各流独立运行,没有同步机制,burst 时刻随机分布。偶尔碰巧 2-3 条流同时 burst,但不是设计保证重叠是核心关注点。μs 级 burst 下同步窗口占比大,burst 必须 > 同步窗口才有效
最小可靠 burst~1-2ms~2μs~20μs

测试场景

场景 1:sync_burst incast – UC

用 UC + sync_burst 让所有端口同步微妙级 burst,不受重传/DCQCN 降速影响,观察纯丢包行为。有效 buffer 计算(Alpha=50%)

理论数据:

  • 配置: msg_size=65536, 16 卡 incast, UC, sync_pause=2ms
  • 有效 buffer = 总 buffer × Alpha = 101MB × 50% = 50.5MB
  • 16卡incast净流入= 15 × 400G – 400g 出流量 = 5.6T (15入 – 1出排空)
  • 填满 50.5MB 需要 = 50.5MB × 8 / 5.6T = 75.6μs
  • 理论速度恒定下,1us 填充 0.7MB Buffer

测试中每轮 burst 都有 ~15μs 的 ramp-up 阶段(流逐个加入):

  • 实际 buffer 峰值 < 完美同步公式的计算值
  • Grafana 带宽也略低(ramp-up 期间不是满功率)
burst_sizeburst 时长buffer 净积累vs 50.5MBrate_limit (HW)内部 pause预期丢包Grafana/发送卡
30~39μs26.3MB52%112 Gbps101μs ✓无丢包~7 Gbps
45~59μs39.4MB78%120 Gbps138μs ✓无丢包~11 Gbps
58~76μs50.7MB100%168 Gbps105μs ✓拐点~14 Gbps
70~92μs61.3MB121%168 Gbps127μs ✓轻丢包~17 Gbps
100~131μs87.5MB173%200 Gbps131μs ✓中等丢包~24 Gbps
150~197μs131.3MB260%200 Gbps197μs ✓重丢包~34 Gbps
300~393μs262.5MB520%300 Gbps131μs ✓严重丢包~63 Gbps
# 无丢包基线(~26.3MB)
python3 run.py incast --burst-profile sync_burst --msg-size 65536 --burst-size 30 --transport UC --duration 300

# 拐点 (~39.4MB)
python3 run.py incast --burst-profile sync_burst --msg-size 65536 --burst-size 45 --transport UC --duration 300

# 轻丢包 (~50.7MB)
python3 run.py incast --burst-profile sync_burst --msg-size 65536 --burst-size 58 --transport UC --duration 300

# 中等丢包 (~50.7MB)
python3 run.py incast --burst-profile sync_burst --msg-size 65536 --burst-size 100 --transport UC --duration 300

# 严重丢包 (~50.7MB)
python3 run.py incast --burst-profile sync_burst --msg-size 65536 --burst-size 300 --transport UC --duration 300

场景 2:Training incast – RC

适合DCQCN 收敛测试,每端口 burst 期间净流入 = 14 × 400G = 5.6 Tbps,即使 1ms burst 也积累 700MB >> 50MB → 必然丢包。但在RC 下 DCQCN 和 RC 重传会逐渐降速,丢包从初始大量 → 收敛后减少。观察收敛时间和稳态丢包率

burst_msbuffer净积累预期丢包Grafana/端口
1700MB多 – > 少~4G
53.5GB多 – > 少~19G
5035GB多 – > 少~133G
10070GB多 – > 少~200G
python3 run.py incast --burst-ms 1 --pause-min 100 --pause-max 100 --duration 120
python3 run.py incast --burst-ms 5 --pause-min 100 --pause-max 100 --duration 120
python3 run.py incast --burst-ms 50 --pause-min 100 --pause-max 100 --duration 120
python3 run.py incast --burst-ms 100 --pause-min 100 --pause-max 100 --duration 120

场景 3:Storage incast – RC

15 流汇聚 1 端口,聚合 = rate_limit × 15

rate_limitburst_sizeburst 时长聚合带宽预期丢包Grafana/端口
1061~5μs150G~10G
2561~5μs375G偶尔(叠加超 400G)~25G
3061~5μs450G持续(超出口)~30G
python3 run.py incast --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 10 --duration 120
python3 run.py incast --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 25 --duration 120
python3 run.py incast --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 30 --duration 120

场景 4:Training all2all – RC

每端口:15 条入流各 ~26.7G = 400G ≈ 出口线速,约平衡

burst_mspause_msBuffer 压力预期丢包Grafana/端口
1000-500均250低(入≈出)轻微(burst 启停瞬间)~114G
100100轻微~200G
1000轻微~400G
python3 run.py all2all --burst-ms 100 --pause-min 0 --pause-max 500 --duration 120
python3 run.py all2all --burst-ms 100 --pause-min 100 --pause-max 100 --duration 120
python3 run.py all2all --pause-min 0 --pause-max 0 --duration 120

场景 5:Training allreduce – RC

每端口:2 条入流各 ~200G = 400G ≈ 出口线速,buffer 压力最低

python3 run.py allreduce --burst-ms 100 --pause-min 0 --pause-max 500 --duration 120
python3 run.py allreduce --pause-min 0 --pause-max 0 --duration 120

场景 6:Storage all2all – RC

每端口 15 条出流,聚合 = rate_limit × 15。Grafana 预期:~375 Gbps/卡。各流独立,burst 自然错开,丢包少于 training 模式。Grafana 显示几乎相同(都 ~375 Gbps),差异在交换机 queue 微观层面。

rate_limitburst_sizeburst 时长聚合带宽预期丢包Grafana/端口
1061~5μs150G~150G
2561~5μs375G级轻~375G
3061~5μs450G~400G
python3 run.py all2all --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 10 --duration 120
python3 run.py all2all --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 25 --duration 120
python3 run.py all2all --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 30 --duration 120

场景对比总结

场景单端口最大 buffer 压力丢包风险适合测试
sync_burst incast UC可精控 26-262MB可控OQ 水线精度
training incast RC极大 700MB-70GB必然重丢DCQCN 收敛
storage incast RC取决于 rate_limit可调存储热点 + DCQCN
training all2all RC低(入≈出)轻微训练业务端到端
storage all2all RC分散极轻存储正常业务
training allreduce RC最低极轻ring 通信

Messages size与场景的关系

场景msg-size原因
AI 训练65536 (64KB)NCCL 典型分块
存储随机 IO4096 (4KB)NVMe 标准块
存储顺序 IO65536-1048576大块 IO
OQ 水线测试65536大包更高效,单次 burst 数据量更大

扩展部署

只改 config.py

SERVERS = [
    ("tme92", "10.26.4.92", [("mlx5_0","192.168.100.2","0-20,64-84"), ("mlx5_1","172.16.0.2","32-52,96-116")]),
    # ...
]

新服务器准备

# OpenMPI 4.1.6
./configure --prefix=/opt/openmpi-4.1.6 --with-ucx=/usr --without-cuda --without-ucc --with-verbs
make -j$(nproc) && make install

# mpi4py 3.1.6
pip3 install --no-binary :all: mpi4py==3.1.6 -i https://pypi.tuna.tsinghua.edu.cn/simple

验证

python3 run.py show
python3 run.py deploy
python3 run.py all2all --duration 30
python3 run.py kill

大规模注意

16-18 卡时可能需要增加等待时间(perftest_burst.py 中 sleep(2)→5, sleep(4)→10)。mpirun 内置自动重试(最多 3 次),处理 DC 建连偶发超时。建议测试环境在同一局域网中。

常用命令速查

# 环境
export PATH=/opt/openmpi-4.1.6/bin:$PATH
export LD_LIBRARY_PATH=/opt/openmpi-4.1.6/lib
cd /root/rdma_burst

# 管理
python3 run.py show / deploy / kill

# 持续满载
python3 run.py all2all --pause-min 0 --pause-max 0 --duration 60

# 训练突发
python3 run.py all2all --duration 60
python3 run.py allreduce --duration 60
python3 run.py incast --duration 60

# 存储微突发
python3 run.py all2all --burst-profile storage --msg-size 4096 --burst-size 61 --rate-limit 25 --duration 60

# OQ 水线测试
python3 run.py incast --burst-profile sync_burst --msg-size 65536 --burst-size 58 --transport UC --duration 120

# 调试
python3 run.py all2all --duration 30 --log
ssh 10.26.4.93 "cat /tmp/perftest_r2_cli_to*.log"
本文出自 Frank's Blog

版权声明:


本文链接:MPI RDMA Burst 流量测试方案
版权声明:本文为原创文章,仅代表个人观点,版权归 Frank Zhao 所有,转载时请注明本文出处及文章链接
你可以留言,或者trackback 从你的网站

留言哦

blonde teen swallows load.xxx videos