MPI_Allreduce在动态数组结构中的数组上

时间:2015-03-21 15:19:41

标签: c arrays structure mpi

我在C中有一个动态的结构数组。说:

int n=100;
struct particle{
   double pos[3];   
   double force[3]; 
   double mass;
   int type;
};
struct particle *mypart;
mypart = (struct particle*) calloc(n,sizeof(struct particle));

在并行代码中,某些操作在mypart[i].force[j]完成,最后我需要在jsut这个数组(maypart[i].force)上执行MPI_Allreduce。通过考虑MPI_Type_create_struct以及其他data_type函数,我无法获得任何有效的解决方案,只需将结构内部的数组传递给其他核心。 有人有任何想法吗?

更新:关于代码的一些细节:这是一个分子动力学代码,其中每个粒子上的力是由于与其他粒子的相互作用。目标是在每个核心上分割力计算。可以同时在不同的核上计算第i个粒子上的力。强制循环后,应将此粒子上的力相加,使每个粒子具有单个力值(3个分量)。这是通过MPI_Allreduce + MPI_SUM函数完成的。我希望这可以澄清我将要做的事情。

1 个答案:

答案 0 :(得分:1)

你想要达到的目标并非不可能,但也不是微不足道的。首先,您必须声明表示整个结构类型的数据类型或仅包含力的数据类型。要构建后者,从适当位移的三个连续双打开始:

MPI_Datatype type_force;
int blen = 3;
MPI_Aint displ = offsetof(struct particle, force);
MPI_Type types = MPI_DOUBLE;

MPI_Type_create_struct(1, &blen, &displ, &types, &type_force);

必须调整新数据类型的大小以匹配C结构的范围,因此我们可以直接访问多个数组元素:

MPI_Datatype type_force_resized;
MPI_Aint lb, extent;

MPI_Type_get_extent(type_force, &lb, &extent);
extent = sizeof(struct particle);
MPI_Type_create_resized(type_force, lb, extent, &type_force_resized);
MPI_Type_commit(&type_force_resized);

全球(全部)减少现在几乎归结为:

struct particle *particles = calloc(n, sizeof(struct particle));
MPI_Allreduce(mypart, particles, n, type_force_resized,
              MPI_SUM, MPI_COMM_WORLD);

由于MPI_(All)Reduce不允许将不同的MPI数据类型用于源缓冲区和接收缓冲区,因此必须使用struct particle而不是double[n][3]的数组。结果将放在每个数组元素的forces[]字段中。

现在,问题是MPI_SUM不对派生数据类型进行操作。解决方案是声明自己的还原操作:

void force_sum(struct particle *in, struct particle *inout,
             int *len, MPI_Datatype *dptr)
{
   for (int i = 0; i < *len; i++)
   {
      inout[i].force[0] += in[i].force[0];
      inout[i].force[1] += in[i].force[1];
      inout[i].force[2] += in[i].force[2];
   }
}

MPI_Op force_sum_op;
MPI_Op_create(force_sum, 1, &force_sum_op);

通过上述所有准备工作,减少成为:

MPI_Allreduce(mypart, particles, n, type_force_resized,
              force_sum_op, MPI_COMM_WORLD);

如果您首先收集double forces[n][3]数组中的所有力,则更简单的变体就是。然后整个还原操作归结为:

double forces[n][3]; // Local forces
double total_forces[n][3]; // Total forces

... transfer mypart[i].force into forces[i] ...

MPI_Allreduce(forces, total_forces, 3*n, MPI_DOUBLE,
              MPI_SUM, MPI_COMM_WORLD);
// Done

但是这种方法需要额外的内存并需要内存复制操作。