浅谈 RDMA 的 Selective Signaling
文章目录
本文通过一个连续提交 RDMA WRITE 的例子,浅谈 Selective Signaling(选择性完成通知)。
为便于理解,下面只讨论同一个 RC QP 的发送队列,假设所有请求都成功提交并正常完成。本文不展开错误恢复和资源管理等工程细节。
What
先把最容易混淆的一点说清楚:
1 | 一个 RDMA 操作已经完成 |
RDMA 中,应用提交的工作请求称为 WR(Work Request)。网卡可以向 CQ(Completion Queue,完成队列) 写入 CQE(Completion Queue Entry,完成记录),应用通过读取这些记录获知完成情况。
Selective signaling 的意思是:
不要求每个 WR 成功完成后都产生 CQE,而是只让选定的 WR 产生成功完成记录。
可以简单理解为:
1 | signaled WR: |
所以:
unsignaled 不是“不执行”,而是“成功完成时不单独报告”。
这里的“通知”指本地的完成记录,不是远端应用程序的回复。
Why
完成通知本身也有开销。
网卡需要写入 CQE,CPU 需要读取和处理 CQE。这些动作也会消耗 PCIe 带宽和处理资源。
假设应用连续提交 8 个 WR:
1 | 每个 WR 都报告完成: |
第二种方式依然执行全部 8 个操作,但减少了生成和处理完成记录的工作。
因此,Selective signaling 的目的可以概括为:
减少完成通知的开销,而不是减少 RDMA 操作本身。
基础 Example
假设向同一个 QP 的 SQ(Send Queue,发送队列)依次提交 8 个 RDMA WRITE:
1 | WR1 → WR2 → WR3 → WR4 → WR5 → WR6 → WR7 → WR8 |
全部 signaled
每个 WR 成功完成后,都产生自己的 CQE:
1 | WR1(S) → CQE1 |
这里 S 表示 signaled。
应用最终需要处理 8 条完成记录。
使用 selective signaling
只将 WR4、WR8 设置为 signaled,其余设置为 unsignaled:
1 | 同一个 QP 的发送队列: |
这里 U 表示 unsignaled。
在本文假设的正常完成情况下,CQ 中只会出现:
1 | CQE4、CQE8 |
前面没有 CQE 的 WR,如何确认完成?
关键在于:
对本例同一个发送队列中按序提交的 RDMA WRITE,后一个 WR 的成功完成,可以作为前面这些 WR 已完成的确认依据。
因此,应用读取到 WR4 的成功完成记录后,就可以确认:
1 | WR1、WR2、WR3、WR4 都已经完成 |
读取到 WR8 的成功完成记录后,则可以继续确认:
1 | WR5、WR6、WR7、WR8 也已经完成 |
可以把 WR4、WR8 理解为两个“完成进度标记”:
1 | 收到 CQE4:完成进度已经到达 WR4 |
需要区分:CQE4 仍然只对应 WR4,并不是其中打包了四份完成结果。
应用之所以能够确认前面的请求也已完成,是因为同一发送队列上的完成顺序提供了依据,而不是因为网卡补发了前面三个请求的 CQE。
How
只看两个设置。
创建 QP 时,将 sq_sig_all 设置为 0,允许应用逐个 WR 选择是否请求成功完成记录:
1 | qp_init_attr.sq_sig_all = 0; |
然后,为需要报告完成的 WR 设置 IBV_SEND_SIGNALED。下面仅展示与 signaling 有关的设置:
1 | wr[0].send_flags = 0; // WR1:unsignaled |
这样,前面三个 WR 成功完成时不单独报告,第四个 WR 成功完成时产生 CQE。
“每 4 个通知一次”只是本文的示例,并不是固定要求。
一个简单的取舍
通知变少,也意味着应用可能更晚才知道前面的请求已经完成。
例如 WR1 已经完成,但应用要等到读到 CQE4,才通过这个完成记录确认它已经完成。
因此:
1 | 通知更频繁: |
注意,更晚得知完成,不等于操作本身更晚完成。
记忆图
1 | 同一个 QP 的发送队列 |
可以简单记成:
1 | 操作:每个都执行 |
一句话概括:
每个操作都要完成,但不必每个操作都单独报告完成。
参考资料: