Sto*_*eak 9 c arrays mpi multidimensional-array mpich
我试图理解这个MPI_Reduce_scatter
功能,但似乎我的推论总是错误的:(
文档说(链接):
MPI_Reduce_scatter首先对count = S(i)的向量进行逐元素减少,在sendbuf,count和datatype定义的发送缓冲区中recvcounts [i]元素.接下来,结果的结果向量被分成n个不相交的段,其中n是组中的进程数.段i包含recvcounts [i]元素.第i个段被发送到进程i并存储在由recvbuf,recvcounts [i]和datatype定义的接收缓冲区中.
我有以下(非常简单)的C程序,我希望获得第一个recvcounts [i]元素的最大值,但似乎我做错了...
#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
#define NUM_PE 5
#define NUM_ELEM 3
char *print(int arr[], int n);
int main(int argc, char *argv[]) {
int rank, size, i, n;
int sendbuf[5][3] = {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
{ 10, 11, 12 },
{ 13, 14, 15 }
};
int recvbuf[15] = {0};
int recvcounts[5] = {
3, 3, 3, 3, 3
};
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
n = sizeof(sendbuf[rank]) / sizeof(int);
printf("sendbuf (thread %d): %s\n", rank, print(sendbuf[rank], n));
MPI_Reduce_scatter(sendbuf, recvbuf, recvcounts, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
n = sizeof(recvbuf) / sizeof(int);
printf("recvbuf (thread %d): %s\n", rank, print(recvbuf, n)); // <--- I receive the same output as with sendbuf :(
MPI_Finalize();
return 0;
}
char *print(int arr[], int n) { } // it returns a string formatted as the following output
Run Code Online (Sandbox Code Playgroud)
我的程序输出与recvbuf和sendbuf相同.我希望recvbuf包含最大值:
$ mpicc 03_reduce_scatter.c
$ mpirun -n 5 ./a.out
sendbuf (thread 4): [ 13, 14, 15 ]
sendbuf (thread 3): [ 10, 11, 12 ]
sendbuf (thread 2): [ 7, 8, 9 ]
sendbuf (thread 0): [ 1, 2, 3 ]
sendbuf (thread 1): [ 4, 5, 6 ]
recvbuf (thread 1): [ 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
recvbuf (thread 2): [ 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
recvbuf (thread 0): [ 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
recvbuf (thread 3): [ 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
recvbuf (thread 4): [ 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
Run Code Online (Sandbox Code Playgroud)
谢谢!
Jon*_*rsi 21
是的,Reduce_scatter的文档很简洁,并没有被广泛使用,所以没有很多例子.这个OCW麻省理工学院讲座的前几张幻灯片有一个很好的图表,并建议一个用例.
通常情况下,关键是阅读MPI文档并特别注意对实施者的建议:
"MPI_REDUCE_SCATTER例程在功能上等同于:MPI_REDUCE集合操作,其计数等于recvcounts [i]的总和,后跟MPI_SCATTERV,sendcounts等于recvcounts."
那么让我们来看看你的例子:这条线,
MPI_Reduce_scatter(sendbuf, recvbuf, recvcounts, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
Run Code Online (Sandbox Code Playgroud)
将相当于:
int totcounts = 15; // = sum of {3, 3, 3, 3, 3}
MPI_Reduce({1,2,3...15}, tmpbuffer, totcounts, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD);
MPI_Scatterv(tmpbuffer, recvcounts, [displacements corresponding to recvcounts],
MPI_INT, rcvbuffer, 3, MPI_INT, 0, MPI_COMM_WORLD);
Run Code Online (Sandbox Code Playgroud)
所以每个人都会发送相同的数字{1 ... 15},并且每列都会相互最大化,导致{max(1,1 ... 1),max( 2,2 ... 2)... max(15,15 ... 15)} = {1,2,... 15}.
然后这些将被分散到处理器,一次3个,导致{1,2,3},{4,5,6},{7,8,9} ......
所以,这就是确实发生,我们如何得到你有什么要发生的情况发生?我知道你希望每一行得到最大化,并且每个处理器得到"它"对应的行 - 最大值.例如,假设数据如下所示:
Proc 0: 1 5 9 13
Proc 1: 2 6 10 14
Proc 2: 3 7 11 15
Proc 3: 4 8 12 16
Run Code Online (Sandbox Code Playgroud)
并且我们希望结束Proc 0(比如说)具有所有第0个数据的最大值,proc 1具有所有第1个的最大值,等等,所以我们最终得到
Proc 0: 4
Proc 1: 8
Proc 2: 12
Proc 3: 16
Run Code Online (Sandbox Code Playgroud)
那么让我们看看如何做到这一点.首先,每个人都将拥有一个值,因此所有的计数都是1.其次,每个流程都必须发送单独的数据.所以我们会看到这样的东西:
#include <stdio.h>
#include <stdlib.h>
#include "mpi.h"
int main(int argc, char *argv[]) {
int rank, size, i, n;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int sendbuf[size];
int recvbuf;
for (int i=0; i<size; i++)
sendbuf[i] = 1 + rank + size*i;
printf("Proc %d: ", rank);
for (int i=0; i<size; i++) printf("%d ", sendbuf[i]);
printf("\n");
int recvcounts[size];
for (int i=0; i<size; i++)
recvcounts[i] = 1;
MPI_Reduce_scatter(sendbuf, &recvbuf, recvcounts, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
printf("Proc %d: %d\n", rank, recvbuf);
MPI_Finalize();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
运行给出(输出重新排序以便清晰):
Proc 0: 1 5 9 13
Proc 1: 2 6 10 14
Proc 2: 3 7 11 15
Proc 3: 4 8 12 16
Proc 0: 4
Proc 1: 8
Proc 2: 12
Proc 3: 16
Run Code Online (Sandbox Code Playgroud)