MPI单边通信重平衡函数正确性测试失败求助
我正在完成课程项目,其中的rebalance函数在正确性测试中失败(其余模块均通过)。初始数组与重平衡后数组校验和不匹配,说明存在数据重叠或损坏,怀疑是进程rank的起始/结束索引逻辑问题。代码如下:
void rebalance(const dist_sort_t *data, const dist_sort_size_t myDataCount, dist_sort_t **rebalancedData, dist_sort_size_t *rCount) { // Get number of processes int nProcs; MPI_Comm_size(MPI_COMM_WORLD, &nProcs); // Get rank of the process int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); dist_sort_size_t global_N; // Perform MPI all reduce to sum up all local_N's and get global_N MPI_Allreduce(&myDataCount, &global_N, 1, MPI_TYPE_DIST_SORT_SIZE_T, MPI_SUM, MPI_COMM_WORLD); uint64_t dataPerProc, limit; // Datacount per process dataPerProc = global_N / nProcs; // no of processes with extra data limit = global_N % nProcs; //Additional debugging specifically for handling of limit > 0 if (limit > 0) { std::cout << "Handling extra data - Rank: " << rank << ", Extra Data Limit: " << limit << std::endl; } // assign datasize for curr process //gives the first few ranks the extras dist_sort_size_t myCount = dataPerProc + (rank < limit ? 1 : 0); std::cout<< "myCount: " << myCount << std::endl; // allocate array for output dist_sort_t *balanced = (dist_sort_t *)malloc(myCount * sizeof(dist_sort_t)); int balanced_size = myCount * sizeof(dist_sort_t); std::cout<< "balanced_size: " << balanced_size << std::endl; // Global starting and ending index of this rank uint64_t myStartGlobal, myEnd; MPI_Exscan(&myDataCount, &myStartGlobal, 1, MPI_TYPE_DIST_SORT_SIZE_T, MPI_SUM, MPI_COMM_WORLD); if (rank == 0) { myStartGlobal = 0; } myEnd = myStartGlobal + myDataCount -1; MPI_Win win; // create window for one-way communication MPI_Win_create(balanced, myCount * sizeof(dist_sort_t), sizeof(dist_sort_t), MPI_INFO_NULL, MPI_COMM_WORLD, &win); MPI_Win_fence(MPI_MODE_NOPRECEDE, win); uint64_t next = myStartGlobal; while (next <= myEnd) { uint64_t dest; if (next < global_N) { dest = next / dataPerProc; // Calculate which process should receive the 'next' data item } else { dest = nProcs - 1; // Assign to the last process if 'next' is outside the range of available data items } uint64_t disp = next % dataPerProc; // offset in destination rank uint64_t size = std::min(dataPerProc - disp, myEnd - next + 1); // size to write to destination rank if (dest < nProcs) { // writing to destination rank MPI_Put(&data[next - myStartGlobal], size, dtype, dest, disp, size, dtype, win); } next += size; } MPI_Win_fence(MPI_MODE_NOSUCCEED, win); // assigning rebalanced data *rebalancedData = balanced; *rCount = myCount; }
问题排查与修复方案
1. 目标进程计算逻辑错误
当前代码用dest = next / dataPerProc划分目标进程,但未考虑前limit个进程多1个元素的情况,会导致数据分配错位。比如global_N=5, nProcs=2时,rank0应该接收3个元素,rank1接收2个,但当前计算会把第3个元素(索引2)送到rank1,导致数据分布错误。
修复代码:
uint64_t dest; if (next < global_N) { if (next < (dataPerProc + 1) * limit) { // 前limit个进程每个多1个元素,按dataPerProc+1划分 dest = next / (dataPerProc + 1); } else { // 剩余进程按dataPerProc划分 dest = limit + (next - (dataPerProc + 1) * limit) / dataPerProc; } } else { dest = nProcs - 1; }
2. 目标进程偏移量计算错误
同样,前limit个进程的偏移量应该基于dataPerProc+1计算,而非dataPerProc,否则会导致偏移超出数组范围或数据覆盖。
修复代码:
uint64_t blockSize = (dest < limit) ? (dataPerProc + 1) : dataPerProc; uint64_t disp = (dest < limit) ? (next % (dataPerProc + 1)) : ((next - (dataPerProc + 1) * limit) % dataPerProc);
3. 传输块大小计算错误
当前size计算未区分目标进程是否属于前limit组,导致传输块大小不符合目标进程的实际容量。
修复代码:
uint64_t size = std::min(blockSize - disp, myEnd - next + 1);
4. MPI数据类型一致性问题
代码中使用的dtype未定义,需确保其与dist_sort_t的MPI类型匹配;同时MPI_Exscan中使用的MPI_TYPE_DIST_SORT_SIZE_T需与uint64_t严格兼容,建议直接使用MPI_UINT64_T(如果dist_sort_size_t是uint64_t别名)避免类型不匹配导致的索引计算错误。
5. 边界值处理冗余
next < global_N判断冗余,因为myEnd = myStartGlobal + myDataCount -1,而global_N是所有myDataCount的总和,next不会超出global_N-1范围,可删除该分支判断。
修复后核心循环代码
uint64_t next = myStartGlobal; while (next <= myEnd) { uint64_t dest; if (next < (dataPerProc + 1) * limit) { dest = next / (dataPerProc + 1); } else { dest = limit + (next - (dataPerProc + 1) * limit) / dataPerProc; } uint64_t blockSize = (dest < limit) ? (dataPerProc + 1) : dataPerProc; uint64_t disp = (dest < limit) ? (next % (dataPerProc + 1)) : ((next - (dataPerProc + 1) * limit) % dataPerProc); uint64_t size = std::min(blockSize - disp, myEnd - next + 1); if (dest < nProcs) { MPI_Put(&data[next - myStartGlobal], size, YOUR_DIST_SORT_MPI_TYPE, dest, disp, size, YOUR_DIST_SORT_MPI_TYPE, win); } next += size; }
内容的提问来源于stack exchange,提问作者Joe_Early_MD

