【问题标题】:Trouble receiving a subset of an array using MPI Datatypes使用 MPI 数据类型接收数组子集时遇到问题
【发布时间】:2015-11-05 18:50:33
【问题描述】:

我在发送和接收二维数组的列时遇到问题。

我有 2 个进程。第一个进程有一个二维数组,我想将它的一部分发送到第二个进程。所以说每个等级都有一个 9x9 数组,我希望等级 0 发送到等级 1 只是某些列:

例子:

-1--2--3-
-2--3--4-
-5--6--7- ...

我想发送“1,2,5,...”和“3,4,7,...”。

我已经编写了只发送第一列的代码,并且我已经阅读了this answer,并且我相信我已经为该列正确定义了一个 MPI_Type_vector:

MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);

这里的dime,9,是数组的大小;我正在发送 9 个 1 MPI_INT 块,每个块以 8 的步幅分隔 - 但即使只是发送这一列也会给我无效的结果。

我的代码如下:

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

#define dime 9

int main (int argc, char *argv[])
{
    int size,rank;
    const int ltag=2;

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);       // Get the number of processes
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);       // Get the rank of the process

    int table[dime][dime];
    for (int i=0; i<dime; i++)
        for (int j=0; j<dime; j++)
            table[i][j] = rank;

    int message[dime];

    MPI_Datatype LEFT_SIDE;
    MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);
    MPI_Type_commit(&LEFT_SIDE);

    if(rank==0) {
        MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
    } else if(rank==1){
        MPI_Status status;
        MPI_Recv(message, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
    }

    if(rank == 1 ){
        printf("Rank 1's received data: ");

        for(int i=0;i<dime;i++)
            printf("%6d ",*(message+i));

        printf("\n");
    }

    MPI_Finalize();
    return 0;

}

但是当我运行它并查看我收到的数据时,我得到的不是全零就是乱码:

$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99 
$ mpirun -np 2 datatype
Rank 1's received data:      0  32710 64550200      0 1828366128  32765 11780096      0      0 

数字每次都变化的地方。我做错了什么?

【问题讨论】:

  • This answer 详细讨论了从二维矩阵中选择列。
  • 是的,我看到了..但我无法解决问题..我的代码将我不想保留的数字更改为零。
  • 原始后笛卡尔拓扑中有很多不相关的东西,非阻塞发送/接收,随机数生成。我试图将这个问题剥离到它的本质,以便答案是有意义的。请注意,将问题精简到其基本要素对于找到解决方案至关重要。

标签: c mpi


【解决方案1】:

@Mort 的回答是正确的,并且是第一;我只是想用一些 ASCII 艺术图表来扩展它,试图把他的信息带回家。

MPI 数据类型描述了数据在内存中的布局方式。让我们看一下您的二维数组,以获得较小的 dime(比如 4)和相应的 MPI_Type_vector:

 MPI_Type_vector(count=dime, blocksize=1, stride=dime-1, type=MPI_INT ...
                      = 4             =1        = 3

 data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
 Vector:  X  -  -  X  -  -  X  -  -  X -  -

请注意,MPI 类型中的步幅是类型的开始之间的距离,而不是它们之间的间隙大小;所以你实际上想要stride=dime,而不是dime-1。这很容易解决,但不是实际问题:

 MPI_Type_vector(count=dime, blocksize=1, stride=dime, type=MPI_INT ...
                      = 4             =1        = 4

 data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
 Vector:  X  -  -  -  X  -  -  -  X  -  -  -  X -  -  - 

好的,到目前为止一切顺利,我们正在选择正确的元素。但我们没有正确接收它们;代码尝试将数据接收到大小为一角钱的数组中,使用相同的布局

int message[dime];
MPI_Recv(message, 1, LEFT_SIDE, 0, ...

message = { 0, 1, 2, 3 };
Vector:     X  -  -  -  X  -  -  -  X  -  -  -  X -  -  - 

向量很好地超出了消息的范围,这 (a) 在消息中留下了未初始化的数据,这是乱码的来源,并且 (b) 可能会导致超出数组范围的分段错误。

至关重要的是,这些 MPI_Type_vectors 之一描述了二维矩阵中所需数据的布局,但 描述了相同数据的布局,因为它被接收到紧凑的一维数组中。

这里有两种选择。要么将数据接收到 message 数组中,就像 dime x MPI_INT 一样:

// ....
} else if(rank==1){
    MPI_Status status;
    MPI_Recv(message, dime, MPI_INT, 0, ltag, MPI_COMM_WORLD, &status);
}

//...

$ mpirun -np 2 datatype
Rank 1's received data:      0      0      0      0      0      0      0      0      0 

或者直接将数据直接接收到 Rank 1 的二维矩阵中,覆盖相应的列:

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

#define dime 9

int main (int argc, char *argv[])
{
    int size,rank;
    const int ltag=2;

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD, &size);       // Get the number of processes
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);       // Get the rank of the process

    int table[dime][dime];
    for (int i=0; i<dime; i++)
        for (int j=0; j<dime; j++)
            table[i][j] = rank;

    MPI_Datatype LEFT_SIDE;
    MPI_Type_vector(dime,1,dime,MPI_INT,&LEFT_SIDE);
    MPI_Type_commit(&LEFT_SIDE);

    if(rank==0) {
        MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
    } else if(rank==1){
        MPI_Status status;
        MPI_Recv(table, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
    }

    if(rank == 1 ){
        printf("Rank 1's new array:\n");

        for(int i=0;i<dime;i++) {
            for(int j=0;j<dime;j++) 
                printf("%6d ",table[i][j]);
            printf("\n");
        }

        printf("\n");
    }

    MPI_Type_free(&LEFT_SIDE);
    MPI_Finalize();
    return 0;

}

跑步给予

$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99 
$ mpirun -np 2 datatype
Rank 1's new array:
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 
     0      1      1      1      1      1      1      1      1 

(修正 MPI_Type_vector 后)

关于如何将其扩展到多列的剩余部分可能最好留给另一个问题。

【讨论】:

  • 感谢您如此公平,乔纳森。
  • @mort - 嘿,你先说对了,我只是想添加更多单词。感谢您太客气地指出我的代码更新版本仍然缺少 MPI_Type_free()。
【解决方案2】:

我不太确定您的问题到底是什么(请在您的问题中明确说明,您会得到更好的答案!另请参阅How do I ask good questions。),但您的代码有几个问题。

  • 您需要使用MPI_Type_vector(dime,1,dime,MPI_INT,&amp;LEFT_SIDE);,因为您要发送矩阵的每个一角钱元素。在 C 中,二维数组简单地存储为标准数组,元素 [i][j] 存储在索引 [i*dime+j] 处。您想发送索引为 0、dime、2*dime、3*dime、...的元素...

  • 如果您使用 LEFT_SIDE 数据类型接收数据,MPI 将存储您的数据项,其中包含一角元素的间隙 - 类似于发送者。但是,您的接收缓冲区message 是一个简单的数组。您需要接收这样的数据:MPI_Recv(message, dime, MPI_INT, 0, LTAG, newcomm,&amp;status);。此操作将接收一角整数并将它们放入您的 message 数组中。

编辑:我更新了我的答案以匹配显着变化的问题。

【讨论】:

  • 问题是消息表写的是 1,0,3 而不是 1,2,5.. 将第一行中的数字 2 替换为 0
  • 这是在你采纳了我的建议之后?你能用一个突出你的问题的完整例子来更新你的问题吗?另外,如果它对您有帮助,请点赞我的回答,如果它解决了您的问题,则除外。
  • 我更新我的帖子...如果你看到我的消息只有表格的第一行并用 0 替换数字。
猜你喜欢
  • 2022-01-21
  • 2018-05-02
  • 2019-01-20
  • 2018-09-10
  • 1970-01-01
  • 2021-03-20
  • 1970-01-01
  • 2012-06-17
  • 2015-03-08
相关资源
最近更新 更多