2

我只是在学习 OpenMPI。尝试了一个简单的MPI_Scatter例子:

#include <mpi.h>

using namespace std;

int main() {
    int numProcs, rank;

    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    int* data;
    int num;

    data = new int[5];
    data[0] = 0;
    data[1] = 1;
    data[2] = 2;
    data[3] = 3;
    data[4] = 4;
    MPI_Scatter(data, 5, MPI_INT, &num, 5, MPI_INT, 0, MPI_COMM_WORLD);
    cout << rank << " recieved " << num << endl; 

    MPI_Finalize();
    return 0;
}

但它并没有按预期工作......

我期待类似的东西

0 received 0
1 received 1 
2 received 2 ... 

但我得到的是

32609 received 
1761637486 received 
1 received 
33 received 
1601007716 received 

奇怪的排名是怎么回事?似乎和我的分散有关?另外,为什么sendcountrecvcount一样?起初我想既然我将 5 个元素分散到 5 个处理器,每个处理器会得到 1 个?所以我应该使用:

MPI_Scatter(data, 5, MPI_INT, &num, 1, MPI_INT, 0, MPI_COMM_WORLD);

但这给出了一个错误:

[JM:2861] *** An error occurred in MPI_Scatter
[JM:2861] *** on communicator MPI_COMM_WORLD
[JM:2861] *** MPI_ERR_TRUNCATE: message truncated
[JM:2861] *** MPI_ERRORS_ARE_FATAL: your MPI job will now abort

我想知道,为什么我需要区分根进程和子进程?似乎在这种情况下,源/根也会得到一个副本?另一件事是其他进程也会分散运行吗?可能不是,但为什么呢?我认为所有进程都会运行此代码,因为如果我在 MPI 程序中看到它不是典型的?

if (rank == xxx) {

更新

我注意到运行,发送和接收缓冲区的长度必须相同......并且数据应该声明为:

int data[5][5] = { {0}, {5}, {10}, {3}, {4} };

注意列被声明为长度 5 但我只初始化了 1 个值?这里实际发生了什么?这段代码正确吗?假设我只希望每个进程只接收 1 个值。

4

1 回答 1

5

sendcount是要发送到每个进程的元素数,而不是发送缓冲区中的元素数。MPI_Scatter只会sendcount从根进程的发送缓冲区中获取 * [通信器中的进程数] 元素,并将其分散到通信器中的所有进程。

因此,要将 1 个元素发送到通信器中的每个进程(假设有 5 个进程),请将sendcount和设置recvcount为 1。

MPI_Scatter(data, 1, MPI_INT, &num, 1, MPI_INT, 0, MPI_COMM_WORLD);

可能的数据类型对存在限制,它们与点对点操作相同。的类型映射recvtype应该与 的类型映射兼容sendtype,即它们应该具有相同的基础基本数据类型列表。此外,接收缓冲区应该足够大以容纳接收到的消息(它可能更大,但不能更小)。在最简单的情况下,发送端和接收端的数据类型是相同的。所以sendcount- recvcountpair 和sendtype- recvtypepair 通常最终是一样的。他们可以不同的一个例子是当一个人在任一侧使用用户定义的数据类型时:

MPI_Datatype vec5int;

MPI_Type_contiguous(5, MPI_INT, &vec5int);
MPI_Type_commit(&vec5int);

MPI_Scatter(data, 5, MPI_INT, local_data, 1, vec5int, 0, MPI_COMM_WORLD);

这是因为发送者构造了 5 个类型元素的消息,MPI_INT而每个接收者将消息解释为 5 元素整数向量的单个实例。

(请注意,您指定要接收的最大元素数量,MPI_Recv实际接收的数量可能会更少,可以通过 获得MPI_Get_count。相反,您提供要接收的预期元素数量,recvcount因此MPI_Scatter如果出现错误,则会引发错误收到的消息长度与承诺的不完全相同。)

可能您现在知道打印出的奇怪排名是由堆栈损坏引起的,因为numcan 仅包含 1int但 5int被接收到MPI_Scatter.

我想知道,为什么我需要区分根进程和子进程?似乎在这种情况下,源/根也会得到一个副本?另一件事是其他进程也会分散运行吗?可能不是,但为什么呢?我认为所有进程都会运行此代码,因为如果我在 MPI 程序中看到它不是典型的?

在 Scatter 和 Gather 等一些操作中,有必要区分 root 和通信器中的其他进程(它们不是 root 的子进程,因为它们可以在单独的计算机中),因为这些是集体通信(组通信),但具有单一来源/目的地。因此,单个源/目标(奇数输出)称为根。所有进程都必须知道源/目标(根进程)才能正确设置发送和接收。

根进程,在 Scatter 的情况下,也将接收一条数据(来自它自己),在 Gather 的情况下,也将其数据包含在最终结果中。根进程也不例外,除非使用“就地”操作。这也适用于所有集体通信功能。

还有一些无根的全局通信操作,例如MPI_Allgather,其中不提供根等级。相反,所有等级都会收到正在收集的数据。

通信器中的所有进程都将运行该功能(尝试排除通信器中的一个进程,您将获得死锁)。您可以想象不同计算机上的进程盲目地运行相同的代码。但是,由于它们中的每一个都可能属于不同的通信器组并且具有不同的等级,因此该功能将不同地运行。每个进程都知道它是否是通信器的成员,每个进程都知道自己的等级,并且可以与根进程的等级(如果有的话)进行比较,因此它们可以建立通信或相应地执行额外的操作。

于 2012-11-05T03:10:42.940 回答