使用堆内存时MPI_Accumulate段错误,栈内存正常?求技术解答
MPI RMA堆分配缓冲区段错误及结果不符问题
问题描述
在学习MPI RMA累积操作场景中,工作进程需将自身rank值累积到rank=0进程的数组中:
- 收发缓冲区在栈上分配时程序正常运行
- 使用
new或MPI_Alloc_mem堆分配时触发段错误 - 移除
MPI_Barrier后无段错误,但rank=0进程的rcv_buf输出值始终为-1,与预期结果不符 - 运行环境:OpenMPI 4.1 + gcc 12.3
原始代码
#include <cstdio> #include <cstddef> #include <mpi.h> int main (int argc, char *argv[]) { int my_rank, size; int count = 5; int *snd_buf; int *rcv_buf; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); MPI_Comm_size(MPI_COMM_WORLD, &size); int expected_value = 0.0; if (my_rank == 0) { for(int i=0; i<size; ++i) expected_value += i; } MPI_Alloc_mem(count * sizeof(int), MPI_INFO_NULL, &rcv_buf); MPI_Alloc_mem(count * sizeof(int), MPI_INFO_NULL, &snd_buf); memset(rcv_buf, -1, count * sizeof(int)); MPI_Win window; if (my_rank == 0) { MPI_Win_create(&rcv_buf, count * sizeof(int), 1, MPI_INFO_NULL, MPI_COMM_WORLD, &window); } else { MPI_Win_create(MPI_BOTTOM, 0, 1, MPI_INFO_NULL, MPI_COMM_WORLD, &window); } if (my_rank != 0) { for(int i=0; i<count; ++i) snd_buf[i] = my_rank; MPI_Win_lock(MPI_LOCK_EXCLUSIVE, 0, 0, window); MPI_Accumulate(&snd_buf, count, MPI_INT, 0, 0, count, MPI_INT, MPI_SUM, window); MPI_Win_unlock(0, window); } MPI_Barrier(MPI_COMM_WORLD); if (my_rank == 0) { printf("\n Expected result for all locations: %d\n", expected_value); printf(" Location Value\n"); for(int i=0; i<count; ++i) printf(" %5d %9d\n", i, rcv_buf[i]); printf("\n"); } MPI_Free_mem(rcv_buf); MPI_Free_mem(snd_buf); MPI_Win_free(&window); MPI_Finalize(); printf(" PE [%2d] done\n", my_rank); }
问题定位与修正
核心错误点
MPI_Win_create参数错误:
rank=0进程中,MPI_Win_create第一个参数需传入缓冲区起始地址,但代码错误传入&rcv_buf(指针变量自身的栈地址)。其他进程通过RMA访问该地址时,会越界访问栈内存,直接触发段错误。
修正:将&rcv_buf改为rcv_buf。MPI_Accumulate发送缓冲区参数错误:
MPI_Accumulate第一个参数需传入发送缓冲区起始地址,代码错误传入&snd_buf(指针变量的栈地址),导致发送无效数据,rank=0的rcv_buf始终为初始值-1。
修正:将&snd_buf改为snd_buf。变量初始化不规范:
expected_value为int类型,用浮点数0.0初始化属于不规范写法,修正为整数0。补充缺失头文件:
使用memset需包含<cstring>头文件,否则可能出现未定义行为。
修正后的代码
#include <cstdio> #include <cstddef> #include <mpi.h> #include <cstring> int main (int argc, char *argv[]) { int my_rank, size; int count = 5; int *snd_buf; int *rcv_buf; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); MPI_Comm_size(MPI_COMM_WORLD, &size); int expected_value = 0; if (my_rank == 0) { for(int i=0; i<size; ++i) expected_value += i; } MPI_Alloc_mem(count * sizeof(int), MPI_INFO_NULL, &rcv_buf); MPI_Alloc_mem(count * sizeof(int), MPI_INFO_NULL, &snd_buf); memset(rcv_buf, -1, count * sizeof(int)); MPI_Win window; if (my_rank == 0) { MPI_Win_create(rcv_buf, count * sizeof(int), sizeof(int), MPI_INFO_NULL, MPI_COMM_WORLD, &window); } else { MPI_Win_create(MPI_BOTTOM, 0, 1, MPI_INFO_NULL, MPI_COMM_WORLD, &window); } if (my_rank != 0) { for(int i=0; i<count; ++i) snd_buf[i] = my_rank; MPI_Win_lock(MPI_LOCK_EXCLUSIVE, 0, 0, window); MPI_Accumulate(snd_buf, count, MPI_INT, 0, 0, count, MPI_INT, MPI_SUM, window); MPI_Win_unlock(0, window); } // 用MPI_Win_fence替代Barrier,更贴合RMA同步语义 MPI_Win_fence(0, window); if (my_rank == 0) { printf("\n Expected result for all locations: %d\n", expected_value); printf(" Location Value\n"); for(int i=0; i<count; ++i) printf(" %5d %9d\n", i, rcv_buf[i]); printf("\n"); } MPI_Free_mem(rcv_buf); MPI_Free_mem(snd_buf); MPI_Win_free(&window); MPI_Finalize(); printf(" PE [%2d] done\n", my_rank); }
额外优化说明
- 将
MPI_Win_create的位移单位(第三个参数)从1改为sizeof(int),符合MPI规范(位移单位为字节数,对应int类型步长)。 - 推荐用
MPI_Win_fence替代MPI_Barrier,它专门用于RMA操作同步,语义更清晰,确保所有RMA操作完成后再访问数据。
内容的提问来源于stack exchange,提问作者user27293694
相关产品推荐
相关产品推荐

