C MPI通信大型二维阵列

C MPI通信大型二维阵列,c,arrays,multidimensional-array,mpi,C,Arrays,Multidimensional Array,Mpi,关于我关于将2维数组复制到1维数组的问题,我想知道是否有更好的方法来完成整个业务,我试图实现这一点。因此,我希望MPI将两个大的二维数组(作为数组数组的数组动态分配,但在编译时大小已知)从主机传送到客户端,反之亦然。数据应该分散到客户机中/从客户机中按列收集 我现在正在做的是将二维数组(实际上是三维数组的子数组)映射到静态分配的一维数组中,发送这些一维数组。通过MPI重新构建阵列,然后在接收器处重新构建二维阵列。然而,我觉得应该有比这更有效的东西 非常感谢 如果数组维度在编译时已知,例如使用静态

关于我关于将2维数组复制到1维数组的问题,我想知道是否有更好的方法来完成整个业务,我试图实现这一点。因此,我希望MPI将两个大的二维数组(作为数组数组的数组动态分配,但在编译时大小已知)从主机传送到客户端,反之亦然。数据应该分散到客户机中/从客户机中按列收集

我现在正在做的是将二维数组(实际上是三维数组的子数组)映射到静态分配的一维数组中,发送这些一维数组。通过MPI重新构建阵列,然后在接收器处重新构建二维阵列。然而,我觉得应该有比这更有效的东西


非常感谢

如果数组维度在编译时已知,例如使用静态维度,那么它就像分配一个线性数组,然后使用与多维数组形状对应的类型强制转换指针一样简单。例如,对于3x5整数数组,即

int array[3][5];
您可以改为使用:

int (*array)[5] = malloc(3*5*sizeof(int));
int(*)[5]
类型是指向5个元素的整数数组的指针
array[0]
是第一个这样的5元素数组,
array[0][3]
是第一个数组的第四个元素

你也可以用三维数组来做

int array[3][4][5];
变成:

int (*array)[4][5] = malloc(3*4*5*sizeof(int));
对于只有在运行时才知道大小的数组,通常使用不同的技术。我们分配一大块内存,然后在其中构建一个指针树。所以不是

int **array = malloc(3*sizeof(int *));
for (i = 0; i < 5; i++)
   array[i] = malloc(5*sizeof(int));

不需要分配两个单独的动态存储器,指针数组的位置可以与数据存储器一起分配,然后是单个
空闲(数组)
将处理所有内容。

C99支持动态多维数组:使用它们可以避免复制数据。例如,以下代码使用
gcc-std=c99编译:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  int m = atoi(argv[1]);
  int n = atoi(argv[2]);
  int p = atoi(argv[3]);

  // if defined in stack... actually I'm not sure it's defined in stack
  // in this way - shouldn't its size be known at compile time?
  // float a[m][n][p];
  // in heap:
  float (*a)[n][p] = malloc(m*n*p*sizeof(float));
  for (int i=0; i<m; ++i) {
    for (int j=0; j<n; ++j) {
      for (int k=0; k<p; ++k) {
        a[i][j][k] = 100.*i + 10.*j + k;
      }
    }
  }
  for (int i=0; i<m; ++i) {
    for (int j=0; j<n; ++j) {
      for (int k=0; k<p; ++k) {
        if (k>0) printf(",");
        printf("%7.2f", a[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }
  free(a);
}
在这两种情况下,阵列位置在内存中都是连续的,可以通过单个MPI通信发送:

MPI_Send(&a[0][0][0], m*n*p, MPI_FLOAT, ...       (c99)
MPI_Send(&ARR(a,0,0,0), m*n*p, MPI_FLOAT, ...     (c89)
或者按照您的要求,发送第i个子阵列:

MPI_Send(&a[i][0][0], n*p, MPI_FLOAT, ...         (c99)
MPI_Send(&ARR(a,i,0,0), n*p, MPI_FLOAT, ...       (c89)

为什么要使用数组的数组?如果它们只是为了“语法糖”,现在就把它们扔掉。你在优化,并行化-这是第一步:没有副本,更好地使用缓存,有可能使用线性代数库…@Sigismondo我不能同意你的观点,如果你使用数组作为已知维度的表,你更愿意查找foo[3][4]还是*(foo+3*width+4)?不使用二维阵列的优点是什么?第二,如果询问者正在使用MPI,他们需要使用副本,MPI通常在进程之间没有共享内存,它们可能在不同的机器上,无法在它们之间双重映射内存。@Vality。C的问题是,当使用数组数组时,foo[3][4]表示*(*(foo+3)+4),当使用二维静态数组时,表示*(foo+3*宽度+4)。第二个更好,是所有优化线性代数库(ATLAS,libgoto)所做的,是支持多维数组的语言(Fortran)所做的。C唯一的问题是它不支持多维动态数组。但是数组的数组与2D数组不同。@Sigismondo感谢您的回答,看看它是如何工作的真的很有趣。我很想知道,在静态编译时维度的情况下,是否有任何常见的编译器能够显著地优化这一点。不过,我确实明白你的观点,明确将迫使它使用最有效的方式,谢谢。@Vality。其实我错了。。。C89不支持动态多维数组。但是C99确实支持多维动态数组——我也该停止使用[i*n+j]了——下面添加一个答案。谢谢你让我找这个:)
#include <stdio.h>
#include <stdlib.h>

#define ARR(A,i,j,k) ((A).a[(i)*A.p*A.n + (j)*A.p + (k)]) 

struct Arr3d {
  float *a;
  int m;
  int n;
  int p;
};

int main(int argc, char **argv) {
  struct Arr3d a;
  int m,n,p;
  int i,j,k;

  m = a.m = atoi(argv[1]);
  n = a.n = atoi(argv[2]);
  p = a.p = atoi(argv[3]);
  a.a = malloc(m*n*p*sizeof(float));
  for (i=0; i<m; ++i) {
    for (j=0; j<n; ++j) {
      for (k=0; k<p; ++k) {
        ARR(a,i,j,k) = 100.*i + 10.*j + k;
      }
    }
  }
  for (i=0; i<m; ++i) {
    for (j=0; j<n; ++j) {
      for (k=0; k<p; ++k) {
        if (k>0) printf(",");
        printf("%7.2f", ARR(a,i,j,k));
      }
      printf("\n");
    }
    printf("\n");
  }
  free(a.a);
}
MPI_Send(&a[0][0][0], m*n*p, MPI_FLOAT, ...       (c99)
MPI_Send(&ARR(a,0,0,0), m*n*p, MPI_FLOAT, ...     (c89)
MPI_Send(&a[i][0][0], n*p, MPI_FLOAT, ...         (c99)
MPI_Send(&ARR(a,i,0,0), n*p, MPI_FLOAT, ...       (c89)