READ/WRITE 操作和本地内存操作的 RDMA 内存语义
RDMA memory semantics for READ/WRITE operations and local memory operations
我看到 RDMA 读取的意外结果让我怀疑我对 RDMA 读写语义的理解。
我正在尝试以类似于 L5 的方式在 RMDA 中实现消息传递,但是 运行 遇到了看起来像内存撕裂的问题。但这不应该发生。
我有一个比 L5 更复杂的结构:
struct Header {
std::atomic<uint8_t> mailbox = 1;
std::atomic<uint32_t> length;
char data[128];
};
在写入方面,我执行 RDMA 读取,直到我在邮箱中看到值 1。然后我执行长度 + 数据的 RDMA 写入,将邮箱设置为 0,并使用第二个 RDMA 写入发送邮箱。
在阅读方面,我检查邮箱 == 0,读取数据,并将长度设置为 0,将邮箱设置为 1。
当我进行 RDMA 读取时,我偶尔会看到长度 <> 0 以及邮箱值 0。由于 RDMA 操作应该按顺序发生,我不明白这是怎么发生的。
一个可能的解释是,如果您针对整个 struct Header
执行 RDMA 读取,则无法保证目标 RDMA 适配器将从内存中读取的顺序以满足该读取。特别是因为你的结构没有与缓存行大小对齐(我猜你在 x86 上,缓存行是 64 字节),所以 mailbox
和 length
可能在不同的缓存行中。
我仍然不太明白为什么看到 length
!= 0 和 mailbox
== 0 会令人惊讶 - 阅读方不就是这样吗?根本没有处理邮箱?根据您所写的内容,从写入端进行两次 RDMA 写入后,结构的最终状态正好是 length
!= 0, mailbox
== 0.
在任何情况下,由于如上所述,RDMA 适配器完全可以自由读取 RDMA 以任何顺序读取的内存,因此 RDMA 可以读取 return 新旧混合的任何内容数据,无论 CPU 更新字段的顺序如何。例如,如果您有:
- RDMA 读取进入目标
struct Header
- RDMA 适配器使用
length
字段读取缓存行
- CPU 更新
length
到 0 然后 mailbox
到 1
- RDMA 适配器使用
mailbox
字段读取缓存行
然后 READ 读取将获取 length
!= 0,mailbox
== 1。这是因为 RDMA 读取操作不参与任何内存屏障或其他排序,即使您将您的结构成员声明为原子的。
我看到 RDMA 读取的意外结果让我怀疑我对 RDMA 读写语义的理解。
我正在尝试以类似于 L5 的方式在 RMDA 中实现消息传递,但是 运行 遇到了看起来像内存撕裂的问题。但这不应该发生。
我有一个比 L5 更复杂的结构:
struct Header {
std::atomic<uint8_t> mailbox = 1;
std::atomic<uint32_t> length;
char data[128];
};
在写入方面,我执行 RDMA 读取,直到我在邮箱中看到值 1。然后我执行长度 + 数据的 RDMA 写入,将邮箱设置为 0,并使用第二个 RDMA 写入发送邮箱。 在阅读方面,我检查邮箱 == 0,读取数据,并将长度设置为 0,将邮箱设置为 1。
当我进行 RDMA 读取时,我偶尔会看到长度 <> 0 以及邮箱值 0。由于 RDMA 操作应该按顺序发生,我不明白这是怎么发生的。
一个可能的解释是,如果您针对整个 struct Header
执行 RDMA 读取,则无法保证目标 RDMA 适配器将从内存中读取的顺序以满足该读取。特别是因为你的结构没有与缓存行大小对齐(我猜你在 x86 上,缓存行是 64 字节),所以 mailbox
和 length
可能在不同的缓存行中。
我仍然不太明白为什么看到 length
!= 0 和 mailbox
== 0 会令人惊讶 - 阅读方不就是这样吗?根本没有处理邮箱?根据您所写的内容,从写入端进行两次 RDMA 写入后,结构的最终状态正好是 length
!= 0, mailbox
== 0.
在任何情况下,由于如上所述,RDMA 适配器完全可以自由读取 RDMA 以任何顺序读取的内存,因此 RDMA 可以读取 return 新旧混合的任何内容数据,无论 CPU 更新字段的顺序如何。例如,如果您有:
- RDMA 读取进入目标
struct Header
- RDMA 适配器使用
length
字段读取缓存行 - CPU 更新
length
到 0 然后mailbox
到 1 - RDMA 适配器使用
mailbox
字段读取缓存行
然后 READ 读取将获取 length
!= 0,mailbox
== 1。这是因为 RDMA 读取操作不参与任何内存屏障或其他排序,即使您将您的结构成员声明为原子的。