在 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] 这样的东西,然后以&amp;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_SendMPI_Recv 开头! MPI 专为MPI_ScatterMPI_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++ 中传递大型二维数组的主要内容,如果未能解决你的问题,请参考以下文章

使用 MPI 发送二维数组

MPI_Bcast 动态二维数组

将二维数组从 C# 传递到 C++

如何将二维数组从 C# 传递到 C++?

使用 MPI_Gather 在 Fortran 中发送二维数组

使用 MPI 在 C 中发送二维数组块