You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:25:09