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

MPI矩阵列向分解实现异常求助:自定义接收数据类型错误排查

问题描述

我有一个N=7、M=8的N×M矩阵,以行优先存储为一维数组:

[0,  1,  2,  3,  4,  5,  6,  7,
 8,  9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55]

需要按列分配给3个进程,预期结果:

  • 进程0:
[ 0,  1,  2,
  8,  9, 10,
 16, 17, 18,
 24, 25, 26,
 32, 33, 34,
 40, 41, 42,
 48, 49, 50]
  • 进程1:
[ 3,  4,  5,
 11, 12, 13,
 19, 20, 21,
 27, 28, 29,
 35, 36, 37,
 43, 44, 45,
 51, 52, 53]
  • 进程2:
[ 6,  7,
 14, 15,
 22, 23,
 30, 31,
 38, 39,
 46, 47,
 54, 55]

每个进程通过以下代码计算本地参数:

int nloc = N;
int mloc = rank < M%nproc ? (M/nproc)+1 : M/nproc;
int *recv_vec = (int *)malloc(nloc*mloc*sizeof(int));

注:rank为进程编号(3个进程对应0、1、2)。

使用MPI_Scatterv实现分配,创建了自定义发送数据类型col_type:

MPI_Datatype col, col_type;
MPI_Type_vector(nloc, 1, M, MPI_INT, &col);
MPI_Type_commit(&col);
//调整新创建的向量类型,使MPI_Scatterv知晓后续元素的正确偏移量
MPI_Type_create_resized(col, 0, sizeof(int), &col_type);
MPI_Type_commit(&col_type);

初始调用MPI_Scatterv时,接收的数据是按列存储的一维数组,不符合行优先的预期格式。于是创建了自定义接收数据类型recv_row_type:

MPI_Datatype recv_row, recv_row_type;
MPI_Type_vector(mloc,1,nloc,MPI_INT, &recv_row);
MPI_Type_commit(&recv_row);
MPI_Type_create_resized(recv_row, 0, sizeof(int), &recv_row_type);
MPI_Type_commit(&recv_row_type);

修改后的MPI_Scatterv调用:

MPI_Scatterv(matrix, sendcounts, displs, col_type, recv_vec, nloc, recv_row_type, 0, MPI_COMM_WORLD);

但接收的数据顺序完全混乱,无法定位recv_row_type的问题,寻求技术帮助。


问题分析

问题出在接收数据类型的逻辑和MPI_Scatterv参数匹配上:

  1. 接收类型步长设置错误:recv_row_type中MPI_Type_vector(mloc,1,nloc,MPI_INT, &recv_row)的步长参数错误。本地存储是nloc*mloc的一维数组,行优先存储下每行有mloc个元素,步长应该对应每行的元素数,而非nloc。
  2. 参数匹配逻辑混乱:发送端每个col_type对应一列(nloc个元素),但接收端的count参数和自定义类型的对应关系不匹配,导致数据映射错位。

解决方案

推荐采用先接收列数据,再本地转置为行优先格式的方案,逻辑直观且不易出错:

  1. 保留发送端的col_type,确保能正确选取矩阵的每一列。
  2. 用临时数组接收按列存储的数据,再通过转置操作将其转换为行优先格式。

修改后的完整代码
#define N 7
#define M 8

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

int main(int argc, char *argv[]){
    int matrix[N*M];
    int menum, nproc, i, j;
    
    MPI_Init(&argc, &argv);
    
    MPI_Comm_rank(MPI_COMM_WORLD, &menum);
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);
    
    // 初始化矩阵
    if (menum == 0) {
        for (i = 0; i < N; i++) {
            for (j = 0; j < M; j++) {
                matrix[(i*M)+j] = i * M + j;
            }
        }
    }
    
    // 计算本地参数并分配内存
    int nloc = N;
    int mloc = menum < M%nproc ? (M/nproc)+1 : M/nproc;
    int *recv_vec = (int *)malloc(nloc*mloc*sizeof(int));
    int *temp_vec = (int *)malloc(nloc*mloc*sizeof(int)); // 临时存储列数据
    
    // 创建发送用的列类型
    MPI_Datatype col, col_type;
    MPI_Type_vector(nloc, 1, M, MPI_INT, &col);
    MPI_Type_commit(&col);
    MPI_Type_create_resized(col, 0, sizeof(int), &col_type);
    MPI_Type_commit(&col_type);
    
    // 分发数据到各进程
    int sendcounts[]={3,3,2};
    int displs[]={0,3,6};
    MPI_Scatterv(matrix, sendcounts, displs, col_type, temp_vec, mloc*nloc, MPI_INT, 0, MPI_COMM_WORLD);
    
    // 将列数据转置为行优先格式
    for(i=0; i<nloc; i++){
        for(j=0; j<mloc; j++){
            recv_vec[i*mloc + j] = temp_vec[j*nloc + i];
        }
    }
    
    free(temp_vec);
    
    // 打印接收结果
    printf("进程 %d: \n", menum);
    for(i=0; i<nloc; i++){
        for(j=0; j<mloc; j++){
            printf("%3d ", recv_vec[i*mloc + j]);
        }
        printf("\n");
    }
    printf("\n");
    
    // 释放资源并终止MPI环境
    free(recv_vec);
    MPI_Type_free(&col);
    MPI_Type_free(&col_type);
    MPI_Finalize();

    return 0;
}

代码解释
  1. 临时数组接收列数据:temp_vec中存储的顺序是:第0列所有元素、第1列所有元素……第mloc-1列所有元素。
  2. 转置操作:通过双重循环将列存储的temp_vec转换为行优先的recv_vec,核心逻辑为recv_vec[i*mloc + j] = temp_vec[j*nloc + i],其中i是行号,j是列号。
  3. 简化逻辑:删除了错误的接收自定义类型,避免了MPI类型匹配的复杂问题,同时保证结果符合预期。

内容的提问来源于stack exchange,提问作者Amato.g

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 11:12:04