尝试在处理器之间传递MPI派生类型(并且失败)

时间:2016-02-08 19:02:03

标签: arrays fortran mpi fortran90 derived-types

我正在尝试将客户的Fortran代码与MPI并行化。 f是一个4字节实数的数组,其大小为f(dimx,dimy,dimz,dimf)。我需要各种过程来处理数组第一维的不同部分。 (我宁愿从最后开始,但这不取决于我。)所以我定义了一个类似的派生类型mpi_x_inteface

call mpi_type_vector(dimy*dimz*dimf, 1, dimx, MPI_REAL,  &
                     mpi_x_interface, mpi_err)
call mpi_type_commit(mpi_x_interface, mpi_err)

我的意图是单个mpi_x_interface将包含某些给定第一个索引“i”的'f'中的所有数据。也就是说,对于给定的i,它应该包含f(i,:,:,:)。 (注意,在游戏的这个阶段,所有过程都有f的完整副本。我打算最终在过程之间拆分f,除了我希望proc 0有一个完整的副本收集的目的。)

ptsinproc是一个数组,包含每个proc处理的“i”索引的数量。 x_slab_displs是每个proc的数组开头的位移。对于我正在测试的两个过程,它们是ptsinproc=(/61,60/)x_slab_displs=(/0,61/)myminpt是一个简单的整数,给出了每个proc中处理的最小索引。

所以现在我想将所有f收集到proc 0中并运行

    if (myrank == 0) then
      call mpi_gatherv(MPI_IN_PLACE, ptsinproc(myrank),
 +                     mpi_x_interface, f(1,1,1,1), ptsinproc,
 +                     x_slab_displs, mpi_x_interface, 0,
 +                     mpi_comm_world, mpi_err)
    else
      call mpi_gatherv(f(myminpt,1,1,1), ptsinproc(myrank),
 +                     mpi_x_interface, f(1,1,1,1), ptsinproc,
 +                     x_slab_displs, mpi_x_interface, 0,
 +                     mpi_comm_world, mpi_err)
    endif

我最多可以发送一个这样的“平板”。如果我尝试将整个60“slab”从proc 1发送到proc 0,我会因“无效的内存引用”而出现seg错误。顺便说一句,即使我发送单一的平板,数据也会在错误的地方结束。

我已经检查了所有明显的东西,比如maiking肯定myrank和ptsinproc以及x_slab_dislps是他们应该在所有过程中应该是什么。我已经研究了“大小”和“范围”之间的区别等等,但无济于事。我的智慧结束了。我只是看不出我做错了什么。有人可能还记得几个月前我问了一个类似(但不同!)的问题。我承认我只是没有得到它。请耐心等待。

1 个答案:

答案 0 :(得分:2)

首先,我只想说你遇到这么多问题的原因是因为你试图拆分第一个(最快的)轴。这根本不推荐,因为as {is packing-your mpi_x_interface需要大量非连续的内存访问。我们说的是性能上的巨大损失。

在MPI流程中分割最慢的轴是一个更好的策略。我强烈建议转置您的4D矩阵,以便x轴是最后一个,如果可以的话。

现在你的实际问题......

派生数据类型

正如您所推断的,一个问题是派生数据类型的大小和范围可能不正确。让我们稍微简化你的问题,这样我就可以画一幅画了。说dimy*dimz*dimf=3dimx=4。原样,您的数据类型mpi_x_interface描述了内存中的以下数据:

| X |   |   |   | X |   |   |   | X |   |   |   |

也就是说,每4 MPI_REAL,其中3个总共。{看到这就是你想要的,到目前为止一切都很好:变量的大小是正确的。但是,如果您尝试发送“下一个”mpi_x_interface,您会看到您的MPI实现将从内存中的下一个点开始(在您的情况下尚未分配),并抛出“无效的内存访问” “在你身边:

                                             tries to access and bombs
                                                 vvv
| X |   |   |   | X |   |   |   | X |   |   |   | Y |   |   |   | Y | ...

您需要告诉MPI作为数据类型的一部分,“下一个”mpi_x_interface仅在数组中启动1 real。这是通过调用MPI_Type_create_resized()重新定义派生数据类型的“范围”来实现的。在您的情况下,您需要写

integer :: mpi_x_interface, mpi_x_interface_resized
integer, parameter :: SIZEOF_REAL = 4 ! or whatever f actually is

call mpi_type_vector(dimy*dimz*dimf, 1, dimx, MPI_REAL,  &
                 mpi_x_interface, mpi_err)
call mpi_type_create_resized(mpi_x_interface, 0, 1*SIZEOF_REAL, &
                             mpi_x_interface_resized, mpi_err)
call mpi_type_commit(mpi_x_interface_resized, mpi_err)

然后,调用“下一个”3 mpi_x_interface_resized将导致:

| X | Y | Z | A | X | Y | Z | A | X | Y | Z | A |

正如所料。

<强> MPI_Gatherv

请注意,现在您已正确定义了数据类型的范围,使用数据类型的偏移调用mpi_gatherv现在应该按预期工作。

就我个人而言,我认为不需要为MPI_IN_PLACE尝试一些奇特的逻辑来进行集体操作。您只需在myminpt=1上设置myrank==0即可。然后你可以打电话给每个级别:

   call mpi_gatherv(f(myminpt,1,1,1), ptsinproc(myrank),
+                     mpi_x_interface_resized, f, ptsinproc,
+                     x_slab_displs, mpi_x_interface_resized, 0,
+                     mpi_comm_world, mpi_err)