MPI分布式均衡生成随机矩阵并写入文件的问题排查
并行生成4×4随机矩阵的MPI文件写入问题
我尝试用MPI并行均衡生成4×4随机矩阵:给定一维数组维度N(本例为16)和进程数NP,每个进程生成N/NP个元素(一维向量形式,无需对齐行维度),再将各自生成的部分写入文件。用8个进程运行代码后,各进程生成元素符合预期,但进程异常退出,文件读取到大量垃圾数据。
运行代码
#include <mpi.h> #include <stdio.h> #include <stdlib.h> int size, rank; int distribute_elems_count(int elems_no) { int base_elems = elems_no / size; int spare_elems = elems_no % size; return rank < spare_elems ? ++base_elems : base_elems; } int main(int argc, char **argv) { MPI_Init(&argc, &argv); MPI_Comm comm; MPI_Comm_dup(MPI_COMM_WORLD, &comm); MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank); int max = 30, min = -30; int rows = 4, cols = 4; // generating by columns int elements_count = distribute_elems_count(rows * cols); double *part = (double *) malloc(elements_count * sizeof(double)); // random value generation for (int i = 0; i < elements_count; i++) { part[i] = rand() % (max + 1 - min) + min; printf("[proc. %d] generated: %f\n", rank, part[i]); } int rank_norm = rank / cols; char *filename = "filename"; MPI_Datatype filetype; MPI_File file; MPI_Status status; int gsizes[1], distribs[1], dargs[1], psizes[1]; gsizes[0] = rows * cols; // size of the main matrix (1d array) distribs[0] = MPI_DISTRIBUTE_BLOCK; // block distribution dargs[0] = elements_count; psizes[0] = size; // all processes can work MPI_Type_create_darray(size, rank_norm, 1, gsizes, distribs, dargs, psizes, MPI_ORDER_C, MPI_DOUBLE, &filetype); MPI_Type_commit(&filetype); MPI_File_open(comm, filename, MPI_MODE_CREATE | MPI_MODE_RDWR, MPI_INFO_NULL, &file); MPI_File_set_view(file, 0, MPI_DOUBLE, filetype, "native", MPI_INFO_NULL); MPI_File_write_all(file, part,size, MPI_DOUBLE, &status); MPI_Barrier(comm); MPI_File_close(&file); printf("proc. %d still alive at the end\n", rank); return 0; }
编译运行命令
$ rm -f filename && mpicc -Wall test2.c && mpirun -np 8 --oversubscribe ./a.out
问题现象
- 各进程输出显示生成了预期数量的元素,但运行时出现进程异常退出提示
- 读取输出文件得到大量垃圾数据,而非预期的矩阵:
-1 24 -1 24 -1 24 -1 24 -1 24 -1 24 -1 24 -1 24
怀疑MPI_Type_create_darray参数设置错误,希望先解决均等分配场景(N能被NP整除,如本例16/8=2)的问题,再适配任意进程数(支持进程生成不同数量元素)的情况。
解决方案
1. 修正均等分配场景的问题
代码存在三处核心错误:
rank_norm参数错误:MPI_Type_create_darray的第二个参数是进程在网格中的坐标,1维分布下直接用全局rank即可,rank / cols会导致多个进程使用相同坐标,引发冲突。MPI_File_write_all计数错误:写入元素数应为当前进程生成的elements_count,而非size,否则会越界访问数组,导致内存错误和垃圾数据。dargs参数逻辑偏差:均等分配时,dargs[0]应设为全局总元素数除以进程数的结果,确保和实际生成的元素数一致。
修正后的核心代码片段:
// 删除错误的rank_norm计算 // int rank_norm = rank / cols; // 修正darray参数 gsizes[0] = rows * cols; distribs[0] = MPI_DISTRIBUTE_BLOCK; dargs[0] = gsizes[0] / psizes[0]; // 均等分配的块大小 psizes[0] = size; MPI_Type_create_darray(size, rank, 1, gsizes, distribs, dargs, psizes, MPI_ORDER_C, MPI_DOUBLE, &filetype); // 修正写入计数为elements_count MPI_File_write_all(file, part, elements_count, MPI_DOUBLE, &status);
2. 适配任意进程数(非均等分配)
当N不能被NP整除时,MPI_DISTRIBUTE_BLOCK会自动将剩余元素分配给前几个进程,只需将dargs设为基础块大小(即N/NP的整数部分),MPI会自动计算每个进程的实际块大小。distribute_elems_count函数的逻辑正确,可保留用于计算每个进程的生成元素数。
修正后的完整代码:
#include <mpi.h> #include <stdio.h> #include <stdlib.h> int size, rank; int distribute_elems_count(int elems_no) { int base_elems = elems_no / size; int spare_elems = elems_no % size; return rank < spare_elems ? base_elems + 1 : base_elems; } int main(int argc, char **argv) { MPI_Init(&argc, &argv); MPI_Comm comm = MPI_COMM_WORLD; MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank); int max = 30, min = -30; int rows = 4, cols = 4; int total_elements = rows * cols; int elements_count = distribute_elems_count(total_elements); double *part = (double *) malloc(elements_count * sizeof(double)); // 每个进程用不同种子生成随机序列 srand(rank + MPI_Wtime()); for (int i = 0; i < elements_count; i++) { part[i] = rand() % (max + 1 - min) + min; printf("[proc. %d] generated: %f\n", rank, part[i]); } char *filename = "filename"; MPI_Datatype filetype; MPI_File file; MPI_Status status; int gsizes[1], distribs[1], dargs[1], psizes[1]; gsizes[0] = total_elements; distribs[0] = MPI_DISTRIBUTE_BLOCK; dargs[0] = total_elements / size; // 基础块大小,MPI自动处理剩余元素 psizes[0] = size; MPI_Type_create_darray(size, rank, 1, gsizes, distribs, dargs, psizes, MPI_ORDER_C, MPI_DOUBLE, &filetype); MPI_Type_commit(&filetype); MPI_File_open(comm, filename, MPI_MODE_CREATE | MPI_MODE_RDWR, MPI_INFO_NULL, &file); MPI_File_set_view(file, 0, MPI_DOUBLE, filetype, "native", MPI_INFO_NULL); MPI_File_write_all(file, part, elements_count, MPI_DOUBLE, &status); MPI_File_close(&file); printf("proc. %d finished successfully\n", rank); free(part); MPI_Finalize(); return 0; }
额外优化点
- 添加
MPI_Finalize()确保MPI资源正确释放 - 为每个进程设置不同随机种子,避免生成重复序列
- 释放
malloc分配的内存,避免内存泄漏 - 去掉不必要的
MPI_Comm_dup,直接使用MPI_COMM_WORLD
内容的提问来源于stack exchange,提问作者G. Ianni
相关产品推荐
相关产品推荐

