在 MPI C++ 中传递大型二维数组
Posted
技术标签:
【中文标题】在 MPI C++ 中传递大型二维数组【英文标题】:Passing large 2d dimentional array in MPI C++ 【发布时间】:2021-12-15 17:32:02 【问题描述】:我的任务是使用 MPI 加速程序。 假设我在输入上有一个大的二维数组(1000x1000 或更大)。我有一个工作的顺序程序,可以将二维数组分成块(例如 10x10)并计算每个卡盘的两倍结果。 (所以我们有一个函数,它的参数是 10x10 的二维数组,结果是一个双精度数)。
我的第一个加快速度的想法:
-
创建大小为 N*N 的一维数组(例如 10x10 = 100)并将数组发送到另一个进程
double* buffer = new double[dataPortionSize];
//copy some data to buffer
MPI_Send(buffer, dataPortionSize, MPI_DOUBLE, currentProcess, 1, MPI_COMM_WORLD);
-
在另一个进程中接收,计算结果,发回结果
double* buf = new double[dataPortionSize];
MPI_Recv(buf, dataPortionSize, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD, status);
double result = function->calc(buf);
MPI_Send(&result, 1, MPI_DOUBLE, 0, 3, MPI_COMM_WORLD);
这个程序比顺序版本慢得多。看起来 MPI 需要很长时间才能将数组传递给另一个进程。
我的第二个想法:
-
将整个二维输入数组传递给所有进程
// data is protected field in base class, it is injected during runtime
MPI_Send(&(data[0][0]), dataSize * dataSize, MPI_DOUBLE, currentProcess, 1, MPI_COMM_WORLD);
-
并像这样接收数据
double **arrayAlloc( int size )
double **result; result = new double [ size ];
for ( int i = 0; i < size; i++ )
result[ i ] = new double[ size ];
return result;
double **data = arrayAlloc(dataSize);
MPI_Recv(&data[0][0], dataSize * dataSize, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD, status);
不幸的是,我在执行过程中遇到了一堆错误:
这些崩溃是非常随机的。程序成功结束发生了2次
我的第三个想法:
将内存地址传递给所有进程,但我发现了这个:
MPI processes cannot read each others' memory, and virtual addressing makes one process' pointer completely meaningless to another.
有人知道如何加快速度吗?我知道提高速度的关键是以有效的方式将数组/数组传递给进程,但我不知道如何做到这一点。
【问题讨论】:
关于崩溃,data
是什么?如何声明/定义和初始化?
接收时我正在使用函数分配内存:``` double *arrayAlloc( int size ) double **result;结果 = 新的双倍 [大小]; for ( int i = 0; i data,我将其注入,您可以假设data
定义明确。我忘了补充。这些崩溃是非常随机的。程序成功结束发生了 2 次。
请edit您的问题包含代码。
问题是你实际上没有一个“2D”数组,你只有一个指针数组。数据不是连续的,因为它是一个适当的“2D”数组。
1.不,double**
不是二维数组。 2. 在“输入”上创建一个大数组是违反 MPI 精神的。它创造了记忆和时间的瓶颈。对于一个好的 MPI 程序,您将首先并行创建矩阵。 3. 当然,您的并行程序更慢:您已将网络操作引入到顺序代码中。只有摊销引入的开销才会更快。
【参考方案1】:
这里有多个问题。我会尝试按任意顺序浏览它们。
-
正如其他人所解释的,您的第二次尝试失败了,因为 MPI 期望您使用单个连续数组,而不是指针数组。所以你想分配像
matrix = new double[rows * cols]
这样的东西,然后以&matrix[row * cols]
访问单个行或以matrix[row * cols + col]
访问单个值
这将是一个您可以使用 MPI 发送、接收、分散和收集的数据结构。一般来说,它也会更快。
您认为 MPI 需要时间来传输数据是正确的。即使是最好的情况,它也是一个 memcpy 的成本。通常明显更多。如果您的程序在传输数据之前做的工作太少,它就不会更快。
您的第一次尝试可能失败了,因为第一个进程在等待结果时没有做任何有用的事情。您没有在代码示例中包含接收操作。但是,如果你写了这样的东西:
for(int block = 0; block < nblocks; ++block)
generate_data(buf);
MPI_Send(buf, ...);
MPI_Recv(buf, ...);
那么你不能期望加速,因为在等待结果时进程没有做任何有用的事情。您可以通过双缓冲来避免这种情况。让第一个进程在接收操作等待结果之前生成下一个数据块。像这样的:
generate_data(0, input); /* 0-th block */
MPI_Send(input, ...);
for(int block = 1; block < nblocks; ++block)
generate_data(block, input); /* 1st up to nth block */
MPI_Recv(output, ...); /* 0-th up to n-1-th block */
MPI_Send(input, ...);
MPI_Recv(output, ...); /* n-th block */
现在两个过程中的计算可以重叠。
您不应该使用MPI_Send
和MPI_Recv
开头! MPI 专为MPI_Scatter
和MPI_Gather
等集体操作而设计。您应该做的是为 N 个进程生成 N 个块,MPI_Scatter
它们在所有进程中。然后让每个进程计算它们的结果。然后MPI_Gather
他们回到根进程。
更好的是,如果可能的话,让每个流程独立工作。当然,这取决于您的数据,但如果您可以相互独立地生成和处理数据块,请不要进行任何通信。让他们都单独工作。像这样的:
int rank, worldsize;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &worldsize);
for(int block = rank; block < nblocks; block += worldsize)
process_data(block);
【讨论】:
以上是关于在 MPI C++ 中传递大型二维数组的主要内容,如果未能解决你的问题,请参考以下文章