我正在尝试将客户的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是他们应该在所有过程中应该是什么。我已经研究了“大小”和“范围”之间的区别等等,但无济于事。我的智慧结束了。我只是看不出我做错了什么。有人可能还记得几个月前我问了一个类似(但不同!)的问题。我承认我只是没有得到它。请耐心等待。
答案 0 :(得分:2)
首先,我只想说你遇到这么多问题的原因是因为你试图拆分第一个(最快的)轴。这根本不推荐,因为as {is packing-your mpi_x_interface
需要大量非连续的内存访问。我们说的是性能上的巨大损失。
在MPI流程中分割最慢的轴是一个更好的策略。我强烈建议转置您的4D矩阵,以便x
轴是最后一个,如果可以的话。
现在你的实际问题......
派生数据类型
正如您所推断的,一个问题是派生数据类型的大小和范围可能不正确。让我们稍微简化你的问题,这样我就可以画一幅画了。说dimy*dimz*dimf=3
和dimx=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)