只读场景下MPI RDMA高效使用及代码实现相关问题问询
问题背景与代码实现
我当前正处理一个适合用MPI单边通信高效实现的场景,但难以找到同类示例,不确定是否做出了最优选择。
每个MPI进程都拥有一个超大向量,需要以不可预测的方式访问其他进程的向量——无法提前知晓消息的频率、大小及数据需求的时机。优势在于这些向量在通信所在的整个函数调用期间保持恒定,无需关注读写同步。
具体来说,每个进程需要完成以下步骤:
- 与所有其他进程进行初始同步,在向量上创建MPI_Win窗口
- 交替执行对其他所有进程的远程读取和数据处理,按需重复多次
- 与所有其他进程进行最终同步,关闭窗口
我目前采用的代码结构如下:
//Initial sync MPI_Win window; MPI_Win_create(... , &window); MPI_Win_fence(0, window); //reading and processing data MPI_Win_lock_all(MPI_MODE_NOCHECK, window); while (keep_on) { //loop over every process excepted world_rank for (int offset = 1; offset < world_size; ++offset) { int target = (world_rank + offset)%world_size; MPI_Get(... , window); } MPI_Win_flush_local_all(window); //process data and update keep_on [...] } MPI_Win_unlock_all(window) //final sync MPI_Win_fence(0, window); MPI_Win_free(&window);
我有以下几点疑问:
- 我仅为调用flush才使用lock/unlock函数,不清楚这些调用的意义,是否有更优方案?
- 尝试用MPI_Rget/MPI_Wait替代lock-get-flush-unlock但无法成功,可能缺少额外调用,报错信息如下:
An error occurred in MPI_Rget reported by process [4181227537,28] on win ucx window 3 MPI_ERR_RMA_SYNC: error executing rma sync
- MPI_MODE_NOCHECK在此场景下是否有用?我困惑的是未指定MPI_NO_PUT来告知MPI我需要只读窗口访问权限。
针对疑问的解答
疑问1:lock/unlock的意义与更优方案
MPI单边通信(RMA)依赖访问epoch概念,lock/unlock或fence的作用就是界定合法的访问时间段,确保远程操作的语义正确性。你用MPI_Win_lock_all开启了全局访问epoch,意味着可以对所有目标进程发起RMA操作;MPI_Win_flush_local_all则是确保本地发起的RMA请求已完成本地缓冲处理并发送,让后续处理能拿到有效数据。
结合你数据全程只读、无修改的场景,完全可以简化流程:
- 初始阶段用
MPI_Win_fence(0, window)建立全局同步epoch后,直接在这个epoch内发起MPI_Get操作,不需要额外lock/unlock。因为MPI_Win_fence本身就创建了所有进程参与的同步上下文,只读场景下无冲突风险。 - 每次循环后用
MPI_Win_flush_all(而非flush_local_all)等待所有远程读取完成,确保处理逻辑能使用完整的远程数据。
调整后的核心代码示例:
//Initial sync MPI_Win window; MPI_Win_create(... , &window); MPI_Win_fence(0, window); // 进入全局只读同步epoch while (keep_on) { for (int offset = 1; offset < world_size; ++offset) { int target = (world_rank + offset)%world_size; MPI_Get(... , window); } MPI_Win_flush_all(window); // 等待所有Get操作完成 // process data and update keep_on [...] } //final sync MPI_Win_fence(0, window); // 退出全局同步epoch MPI_Win_free(&window);
这种方式省去了lock/unlock的开销,更贴合你的场景需求。
疑问2:MPI_Rget/MPI_Wait失败的原因
MPI_Rget作为非阻塞RMA操作,必须在合法的访问epoch内执行。你报错的核心原因是未提前开启同步上下文:
- 非阻塞RMA操作(Rget/Rput等)不能直接调用,必须先通过lock/unlock或fence创建访问epoch,否则MPI会因缺乏同步上下文抛出
MPI_ERR_RMA_SYNC错误。 - 正确用法是:先通过
MPI_Win_lock_all或MPI_Win_fence进入epoch,发起MPI_Rget后用MPI_Wait/MPI_Waitall等待请求完成。
示例代码:
MPI_Win_fence(0, window); MPI_Request reqs[world_size-1]; int req_idx = 0; while (keep_on) { req_idx = 0; for (int offset = 1; offset < world_size; ++offset) { int target = (world_rank + offset)%world_size; MPI_Rget(... , &reqs[req_idx++], window); } MPI_Waitall(req_idx, reqs, MPI_STATUSES_IGNORE); // 等待所有非阻塞读取完成 // process data and update keep_on [...] } MPI_Win_fence(0, window);
疑问3:MPI_MODE_NOCHECK与MPI_NO_PUT的作用
- MPI_MODE_NOCHECK:在
MPI_Win_lock_all时使用,告诉MPI无需检查其他进程是否持有冲突锁。你的场景中所有进程都是只读访问,不会有写操作引发的冲突,因此这个参数有用——可以跳过锁冲突检查,减少额外开销。 - MPI_NO_PUT:这是
MPI_Win_create的窗口属性,用来标记窗口为只读(禁止远程PUT操作)。你的场景确实应该添加这个参数:一方面MPI可以针对只读场景做优化(比如跳过写同步逻辑、使用更高效的内存映射);另一方面能在误发起写操作时触发错误,避免数据损坏。
修改后的MPI_Win_create调用示例:
// 显式设置只读属性 MPI_Info info; MPI_Info_create(&info); MPI_Info_set(info, "no_put", "true"); MPI_Win_create(..., info, MPI_COMM_WORLD, &window); MPI_Info_free(&info);
不同MPI实现对no_put的支持略有差异,但显式设置能明确告知MPI你的只读需求,提升性能或安全性。
内容的提问来源于stack exchange,提问作者Antoine Motte
相关产品推荐
相关产品推荐

