一、tx-depth 是什么?
1.1 一句话定义
tx-depth 是 RDMA QP 发送队列(Send Queue)的最大深度 —— 一次能挂多少个"待发送请求"。
1.2 形象比喻
快递站的待派件堆积区:
- 区域大小 = tx-depth
- 包裹 = 待发送的 RDMA write 请求
- 快递员 = 网卡 DMA 引擎(异步派件)
- 派件完成短信 = CQE(Completion Queue Entry)
对应关系:
| 快递站 | RDMA QP |
|---|---|
| 区域大小 | tx-depth |
| 堆包裹 | post_send() |
| 派件员 | 网卡 DMA 引擎 |
| 派件短信 | CQE(完成通知) |
| 收短信清空区域 | poll CQ |
1.3 工作流程
CPU 网卡(DMA)
│ │
│ post_send(req1) │
├─────────────────────────────────►│
│ post_send(req2) │ ← 同时在飞 (in-flight)
├─────────────────────────────────►│
│ post_send(req3) │ ← SQ 还有位置
├─────────────────────────────────►│
│ ... │
│ │ DMA 到对端
│ ├──────────►
│ ... 直到 tx-depth 个坑占满 │
│ │
│ ←──── CQE(req1 完成) ──────────│ 通知 CPU 释放坑
│ post_send(req4) │ CPU 立刻补新坑
├─────────────────────────────────►│
│ │
关键点:
post_send是 非阻塞的,CPU 不等 DMA 完成,立刻返回- CQE 是 异步的,DMA 完成后网卡中断通知 CPU
- CPU 看到 CQE 后才能复用那个 SQ 坑
1.4 为什么需要 tx-depth?
如果没有 tx-depth(= 1):
- CPU 每次只能发 1 条 → 必须等 CQE 才能发下一条
- 等待时间 = 网络往返延迟(μs 级)→ CPU 闲置
- 带宽利用率 < 50%
有了 tx-depth(= 128):
- CPU 连续发 128 条不等 CQE
- 网卡 DMA 流水线处理 → 全程不空转
- 带宽利用率 100%
这就是流水线(pipelining) 的威力 —— CPU 和 DMA 解耦。
二、4 组实验:tx-depth 怎么影响 RDMA 性能
实验环境同上文:i3-6320 + 2 ×cx8 + 200GbE RoCE v2
2.1 实验 A:QP=4 msg=1MB,tx=64(太小)
ib_write_bw -d mlx5_1 -x 2 -s 1048576 -D 10 -q 4 -t 64 \
--report_gbits --cpu_util <server_ip>
结果:
#bytes #iterations BW peak[Gb/sec] BW average[Gb/sec] MsgRate[Mpps] CPU_Util[%]
1048576 35515 0.00 50.97 0.006076 0.00
BW = 50.97 Gb/s(跟 tx=128 几乎一样)
为什么 tx=64 跟 tx=128 一样?
大包(1MB)+ 50 Gb/s 带宽 → 每包耗时 = 1MB / 50Gb/s = 160 μs。
- tx=64 表示最多 64 个包在飞 → 总飞行时间 = 64 × 160 μs = 10.24 ms
- 10 秒测试窗口内能发 64 × 10000/10.24 ≈ 62,500 包
- 实际 iterations = 35,515(远小于上限)→ SQ 还没满
结论:1MB 大包 + 50 Gb/s 下,tx=64 已经够用,再大也用不上。
2.2 实验 B:QP=4 msg=1MB,tx=128(perftest 默认)
ib_write_bw -d mlx5_1 -x 2 -s 1048576 -D 10 -q 4 -t 128 ...
结果:
1048576 36691 0.00 50.68 0.006042 0.00
BW = 50.68 Gb/s
对比 Exp A(tx=64):
| tx | BW | 差异 |
|---|---|---|
| 64 | 50.97 | baseline |
| 128 | 50.68 | -0.6% |
0.6% 差异在测试误差范围内 → 大包 + tx=128 没优势也没劣势。
2.3 实验 C:QP=4 msg=1MB,tx=512(偏大)
ib_write_bw -d mlx5_1 -x 2 -s 1048576 -D 10 -q 4 -t 512 ...
结果:
1048576 36118 0.00 50.85 0.006062 0.00
BW = 50.85 Gb/s(跟 tx=64/128 几乎一样)
为什么 tx=512 没提速?
i3-6320 L1-dcache = 32 KB。每个 SQ 坑(约 64 bytes)× 512 = 32 KB → 正好打爆 L1-dcache。
但因为是大包场景,CPU 访问 SQ 的频率低(160 μs 才访问一次),cache miss 的延迟被网络延迟掩盖了。
2.4 实验 D:QP=1 msg=4KB,tx=128 vs 512 vs 2048(关键对比)
4KB 小包——这次差异显形:
for tx in 64 128 512 1024 2048; do
ib_write_bw -d mlx5_1 -x 2 -s 4096 -D 15 -q 1 -t $tx ...
done
结果:
| tx-depth | BW (Gb/s) | L1_dc_miss | 评价 |
|---|---|---|---|
| 64 | 50.97 | N/A | 太小 |
| 128 | 77.80 ⭐ | 45.4M | 甜蜜点 |
| 512 | 72.93 | 55.8M | 大了掉 6.3% |
| 2048 | 73.87 | 55.2M | 大了掉 5.0% |
🐕 关键发现:4KB 小包场景下,tx=128 是甜蜜点,超过 128 反而慢 5-6%!
为什么小包 + tx 大反而慢?
- 4KB @ 78 Gb/s = 每包 410 ns
- tx=128 → 总飞行时间 = 128 × 410 ns = 52 μs(足够 CPU 在这期间干别的)
- tx=2048 → SQ 占用 = 2048 × 64B = 128 KB → 完全打爆 L1-dcache(32 KB)
每次 post_send() 都要查 SQ → L1 miss → 访问 L2/L3 → 延迟从 1ns 涨到 12ns。
小包 410 ns/包,3% 延迟放大 = 1% 带宽损失(与实测吻合)。
三、4 组实验汇总
3.1 总表
| # | msg_size | tx | QP | BW (Gb/s) | 现象 |
|---|---|---|---|---|---|
| A | 1MB | 64 | 4 | 50.97 | 大包,SQ 没用满 |
| B | 1MB | 128 | 4 | 50.68 | 默认,没区别 |
| C | 1MB | 512 | 4 | 50.85 | SQ 装满 L1,但访问频率低 |
| D | 4KB | 128 | 1 | 77.80 | 小包甜蜜点 |
| D’ | 4KB | 512 | 1 | 72.93 | 小包,SQ 打爆 L1,开始掉速 |
| D’’ | 4KB | 2048 | 1 | 73.87 | 同上,更严重 |
3.2 ASCII 图(tx-depth vs BW,4KB 场景)
BW (Gb/s)
80 ┤
78 ┤ ● ← tx=128 甜蜜点 (77.80)
76 ┤
74 ┤ ● ● ← tx=512/2048 (-5%)
72 ┤
70 ┤
68 ┤
66 ┤
64 ┤
62 ┤
60 ┤
58 ┤
56 ┤
54 ┤
52 ┤ ● ← tx=64 也低(CPU 在等 CQE)
50 ┤
└─┬──┬──┬──┬──┬──┬─► tx-depth
64 128 256 512 1024 2048
3.3 现象归因
| msg_size | 甜蜜点 | 原因 |
|---|---|---|
| 大包(1MB) | tx=64~512 都行 | 每包耗时 160 μs,CPU 访问 SQ 频率低 |
| 小包(4KB) | tx=128 | 每包耗时 410 ns,CPU 频繁访问 SQ,cache miss 拖慢 |
| 超小包(256B) | tx=128 还是更稳 | perftest 默认值就是经验最优 |
四、tx-depth 选择决策树
你的应用场景是什么?
│
├── 大文件传输(msg ≥ 64KB)
│ │
│ └── tx=64 就够
│ perftest 默认 128 也行,几乎没差别
│
├── 中等消息(msg 4KB ~ 64KB) ← NCCL AllReduce 典型场景
│ │
│ └── tx=128(perftest 默认)就是最优
│ 调大反伤 L1 cache
│
├── 小消息控制平面(msg ≤ 1KB)
│ │
│ └── tx=128 也行
│ 如果 batch 大(> 1000)可以试 tx=64 省 cache
│
└── 延迟极敏感(金融交易、风控)
│
└── tx=32 或更小
减少 SQ 占用,CPU 访问更快
但带宽利用率会下降 20-30%
五、生产环境 RDMA 应用的最佳实践
5.1 NCCL AllReduce(最常见 RDMA 应用)
| 参数 | 推荐值 | 原因 |
|---|---|---|
| QP 数 | NCCL 自动(通常 = NIC 数 × 通道数) | 跟硬件拓扑强相关 |
| tx-depth | NCCL 内部默认(一般 32-128) | 已经过调优 |
| 改 tx-depth | 不建议 | NCCL 已经按 GPU + NIC 配比调好 |
5.2 自写 RDMA 应用
struct ibv_qp_init_attr attr = {
.cap = {
.max_send_wr = 128, // ← tx-depth(perftest 默认)
.max_recv_wr = 128,
.max_send_sge = 1,
.max_recv_sge = 1,
},
.qp_type = IBV_QPT_RC,
.sq_sig_all = 0,
};
// 创建 QP 后可以动态调整(在某些驱动支持)
// 但通常创建后固定
5.3 监控 tx-depth 是否够用
# 看 SQ 占用
cat /sys/class/infiniband/mlx5_0/ports/1/counters/sq_watermark
# 看 completion queue 积压
perf stat -e cs_unc_arfs_cycles_ok # CX8 专用
六、tx-depth vs 相关概念
| 概念 | 全称 | 关系 |
|---|---|---|
| tx-depth | TX queue depth | SQ 容量(发送方) |
| rx-depth | RX queue depth | RQ 容量(接收方) |
| CQ moderation | Completion Queue moderation | 多少个 CQE 触发一次中断(降低中断开销) |
| inline size | Inline data size | 小包走 SQ 寄存器,省 PCIe DMA |
| MTU | Maximum Transmission Unit | 物理包大小(与 tx-depth 无关) |
七、一句话总结
tx-depth 是发送队列深度 —— 让 CPU 和 DMA 解耦的"流水线窗口"。太小浪费带宽,太大打爆 L1 cache。perftest 默认的 128 在大多数场景下是甜蜜点,生产环境(NCCL)已经调好,不要瞎改。
八、附录:测试环境 & 复现
测试机:i3-6320 + 2 × cx8(200GbE RoCE v2)
OFED:26.01
工具:ib_write_bw + perf stat
复现命令:
# Exp D:小包 + tx 扫描
for tx in 64 128 512 1024 2048; do
ib_write_bw -d mlx5_0 -x 2 -s 4096 -D 15 -q 1 -t $tx --report_gbits &
sleep 2
perf stat -e cache-misses,cache-references,L1-dcache-load-misses \
ib_write_bw -d mlx5_1 -x 2 -s 4096 -D 15 -q 1 -t $tx \
--report_gbits <server_ip>
done
482

被折叠的 条评论
为什么被折叠?



