我刚刚学习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
奇怪的队伍是什么?似乎与我的分散有关?另外,为什么sendcount
和recvcount
相同?起初我想,因为我将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
我很想知道,为什么我需要区分根进程和子进程?好像在这种情况下,源/ root也会得到副本?另一件事是其他进程也会分散吗?可能不是,但为什么呢?我认为所有进程都将运行此代码,因为它不是典型的,如果我在MPI程序中看到的那样?
if (rank == xxx) {
更新
我注意到运行,发送和接收缓冲区的长度必须相同......并且数据应该声明为:
int data[5][5] = { {0}, {5}, {10}, {3}, {4} };
请注意,列被声明为长度为5但我只初始化了1个值?这里到底发生了什么?这段代码是否正确?假设我只希望每个进程只接收1个值。
答案 0 :(得分: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
- recvcount
对和sendtype
- recvtype
对通常会相同。它们可以不同的一个例子是当一方在任何一方使用用户定义的数据类型时:
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
中收到的元素,如果收到的邮件长度与完全相同,则会引发错误。)
可能你现在知道打印出来的奇怪排名是由堆栈损坏引起的,因为num
只能包含1 int
但int
收到5 MPI_Scatter
我很想知道,为什么我需要区分根进程和子进程?好像在这种情况下,源/ root也会得到副本?另一件事是其他进程也会分散吗?可能不是,但为什么呢?我认为所有进程都将运行此代码,因为它不是典型的,如果我在MPI程序中看到的那样?
在某些操作(如Scatter和Gather)中,有必要区分通信器中的根和其他进程(它们不是root的子进程,因为它们可以位于单独的计算机中),因为这些是集体通信(组通信)但有一个源/目的地。因此,单个源/目标(奇数输出)称为根。所有进程都必须知道源/目标(根进程)才能正确设置发送和接收。
在Scatter的情况下,根进程也会收到一段数据(来自其自身),如果是Gather,还会在最终结果中包含其数据。除非使用“就地”操作,否则根进程没有例外。这也适用于所有集体沟通职能。
还有一些无根的全局通信操作,如MPI_Allgather
,其中一个不提供根级别。相反,所有排名都会收到正在收集的数据。
通信器中的所有进程都将运行该函数(尝试在通信器中排除一个进程,您将遇到死锁)。您可以想象盲目地运行相同代码的不同计算机上的进程。然而,由于它们中的每一个可能属于不同的通信器组并且具有不同的等级,因此该功能将以不同的方式运行。每个进程都知道它是否是通信器的成员,并且每个进程都知道自己的级别,并且可以与根进程的级别(如果有的话)进行比较,因此他们可以建立通信或相应地执行额外的操作。