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

如何利用MPI将两个4x8子矩阵按行交错合并为8x8矩阵?

MPI合并交错排列的子矩阵

问题场景

有一个8x8的float*矩阵,计算后拆分为两个4x8子矩阵:

  • 进程0持有行:
    0 0 0 0 0 0 0 0
    1 1 1 1 1 1 1 1
    4 4 4 4 4 4 4 4
    5 5 5 5 5 5 5 5
    
  • 进程1持有行:
    2 2 2 2 2 2 2 2
    3 3 3 3 3 3 3 3
    6 6 6 6 6 6 6 6
    7 7 7 7 7 7 7 7
    

需要将两个子矩阵合并为完整矩阵,按行交错排列为:

0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5
6 6 6 6 6 6 6 6
7 7 7 7 7 7 7 7

错误尝试代码

用户尝试使用MPI_Vector和MPI_Gatherv但失败,代码如下:

MPI_Datatype dt;
int gsizes[2], distribs[2], dargs[2], psizes[2];

// original 4x8 submatrices
gsizes[0] = 4;
gsizes[1] = 8;

distribs[0] = MPI_DISTRIBUTE_CYCLIC;
distribs[1] = MPI_DISTRIBUTE_CYCLIC;

// I want to interleave 2x8 blocks
dargs[0] = 2;
dargs[1] = 8;

psizes[0] = 2; 
psizes[1] = 1; // I just want 1 proc to have all the row

MPI_Type_vector(2, 2 * 8, 1, MPI_FLOAT, &dt);
MPI_Type_create_resized(dt, 0, 2*8*sizeof(float), &dt);
MPI_Type_commit(&dt);

int displs[2] = {0, 0};
int recva[2] = {2, 2}; // I want to receive two 2*8 blocks from each process

// src and result are two properly-allocated float*
MPI_Gatherv(src, 4 * 8, MPI_FLOAT,
            result, recva, displs, dt, 0, group_comm);

执行结果仅收到64个浮点数中的17个,且仅来自进程1,顺序混乱。

问题分析

  1. 自定义类型错误:MPI_Type_vector(2, 2*8, 1, MPI_FLOAT, &dt)的参数不符合内存布局需求,该定义会尝试从连续内存中取2个长度为16的块,步长为1,与子矩阵的实际存储逻辑不匹配。
  2. 位移参数错误:displs[2] = {0, 0}导致两个进程的数据都写入接收缓冲区的起始位置,相互覆盖。
  3. 接收计数逻辑错误:recva的设置未对应正确的块位置和数量。

可行解决方案

方案一:自定义MPI块类型 + MPI_Gatherv

通过定义2行8列的连续块类型,在根进程指定每个进程数据的接收位移,实现正确合并:

MPI_Datatype block_2x8;
// 定义2行8列的连续块:每个块包含2行,每行8个float,块内连续
MPI_Type_contiguous(2 * 8, MPI_FLOAT, &block_2x8);
MPI_Type_commit(&block_2x8);

int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

if (rank == 0) {
    // 每个进程发送2个block_2x8类型的数据
    int recvcounts[2] = {2, 2};
    // 位移单位为自定义类型大小:
    // 进程0的两个块对应目标矩阵的0-1行、4-5行,位移为0、2
    // 进程1的两个块对应目标矩阵的2-3行、6-7行,位移为1、3
    int displs_int[2] = {0, 1};

    MPI_Gatherv(src, 2, block_2x8,
                result, recvcounts, displs_int, block_2x8,
                0, MPI_COMM_WORLD);
} else {
    // 非根进程发送2个block_2x8类型的数据
    MPI_Gatherv(src, 2, block_2x8,
                NULL, NULL, NULL, MPI_DATATYPE_NULL,
                0, MPI_COMM_WORLD);
}

MPI_Type_free(&block_2x8);

方案二:直接拆分发送(直观易调试)

如果自定义类型容易出错,可直接将每个进程的4行拆分为两组2行,分别发送到根进程的对应位置:

int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

if (rank == 0) {
    // 复制自身数据到目标缓冲区对应位置
    memcpy(result, src, 2*8*sizeof(float));
    memcpy(result + 4*8, src + 2*8, 2*8*sizeof(float));

    // 接收进程1的两组数据
    MPI_Recv(result + 2*8, 2*8, MPI_FLOAT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    MPI_Recv(result + 6*8, 2*8, MPI_FLOAT, 1, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
} else if (rank == 1) {
    // 发送两组数据到根进程对应位置
    MPI_Send(src, 2*8, MPI_FLOAT, 0, 0, MPI_COMM_WORLD);
    MPI_Send(src + 2*8, 2*8, MPI_FLOAT, 0, 1, MPI_COMM_WORLD);
}

注意事项

  • 确保src和result缓冲区已正确分配:每个进程的src分配4*8*sizeof(float),根进程的result分配8*8*sizeof(float)。
  • 使用自定义MPI类型后,需调用MPI_Type_free释放类型资源。
  • 方案二更适合小规模矩阵,调试维护简单;方案一更适合扩展到多进程或大矩阵场景。

内容的提问来源于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.29 10:52:33