Parallel processing 在Fortran 90中使用MPI_Send/Recv处理多维数组块

Parallel processing 在Fortran 90中使用MPI_Send/Recv处理多维数组块,parallel-processing,mpi,fortran90,Parallel Processing,Mpi,Fortran90,我必须在FORTRAN 90中发送和接收(MPI)多维数组块。线路 MPI_Send(x(2:5,6:8,1),12,MPI_Real,....) 根据Gropp、Lusk和Skjellum的《使用MPI…》一书,不应该使用。最好的方法是什么?我必须创建一个临时数组并发送它,还是使用MPI\u Type\u create\u Subarray或类似的东西?编辑:正如赫里斯托·伊利耶夫指出的那样MPI\u send总是阻塞,但可能会选择异步发送数据。发件人: 在可以使用发送缓冲区之前,MPI_发

我必须在FORTRAN 90中发送和接收(MPI)多维数组块。线路

MPI_Send(x(2:5,6:8,1),12,MPI_Real,....)

根据Gropp、Lusk和Skjellum的《使用MPI…》一书,不应该使用。最好的方法是什么?我必须创建一个临时数组并发送它,还是使用
MPI\u Type\u create\u Subarray
或类似的东西?

编辑:正如赫里斯托·伊利耶夫指出的那样
MPI\u send
总是阻塞,但可能会选择异步发送数据。发件人:

在可以使用发送缓冲区之前,MPI_发送不会返回

非阻塞通信(如
MPI\u Send
)在涉及非连续数组时可能会对Fortran造成问题。然后,编译器为伪变量创建一个临时数组,并将其传递给子例程。一旦子例程完成,编译器就可以自由地释放该副本的内存

只要使用阻塞通信(
MPI\u Send
),就可以了,因为这样子例程返回时消息就已经发送了。但是,对于非阻塞通信(
MPI_Isend
),临时数组是发送缓冲区,子例程在发送之前返回

因此,MPI可能会从不再保存有效数据的内存位置发送数据

因此,要么您自己创建一个副本(以便发送缓冲区在内存中是连续的),要么您创建一个子数组(即告诉MPI您要发送的元素在内存中的地址)。还有更多的选择,比如
MPI\u-Pack
,但我没有使用它们的经验

哪条路更快?那要看情况了:

  • 关于MPI库的实际实现
  • 论数据及其分布
  • 在编译器上
  • 在你的硬件上

有关详细说明和更多选项,请参阅

不在
MPI\u SEND
中使用数组节的原因是编译器必须使用一些MPI实现创建临时副本。这是因为Fortran只能正确地将数组部分传递给具有显式接口的子例程,并且在所有其他情况下,通常在调用子例程的堆栈上,必须生成临时“展平”副本。不幸的是,在F2008的TR 29113扩展之前,Fortran无法声明采用变量类型参数的子例程,MPI实现通常采用语言黑客,例如,
MPI_Send
完全用C实现,并且依赖Fortran始终将数据作为指针传递

一些MPI库通过为
MPI\u SEND
生成大量重载来解决此问题:

  • 取一个
    整数的一种
  • 采用
    整数的一维数组的一种
  • 采用
    整数
  • 等等
然后对
字符
逻辑
双精度
等重复相同的操作。这仍然是一个难题,因为它不包括传递用户定义类型的情况。此外,它使C实现变得非常复杂,因为它现在必须理解Fortran数组描述符,这些描述符非常特定于编译器

幸运的是,时代在变化。Fortran 2008的TR 29113扩展包括两个新功能:

  • 假定类型参数:
    type(*)
  • 假定维度参数:
    维度(..)
两者的组合,即
类型(*)、维度(..)和意图(IN)::buf
,描述了一个既可以是不同类型又可以有任何维度的参数。mpi-3中新的
mpi_f08
接口已经利用了这一点

非阻塞调用在Fortran中提出了更大的问题,超出了Alexander Vogt所描述的范围。原因是Fortran没有禁止编译器优化的概念(即Fortran中没有
volatile
关键字)。以下代码可能无法按预期运行:

INTEGER :: data

data = 10
CALL MPI_IRECV(data, 1, MPI_INTEGER, 0, 0, MPI_COMM_WORLD, req, ierr)
! data is not used here
! ...
CALL MPI_WAIT(req, MPI_STATUS_IGNORE, ierr)
! data is used here
人们可能期望在调用
MPI_WAIT
数据之后,数据将包含从秩0接收到的值,但情况可能并非如此。原因是编译器无法知道在
MPI\u IRECV
返回后,
data
可能会异步更改,因此将其值保留在寄存器中。这就是为什么在Fortran中,非阻塞MPI调用通常被认为是危险的

TR 29113还通过
ASYNCHRONOUS
属性解决了第二个问题。如果查看
mpi\u IRECV
mpi\u f08
定义,其
buf
参数声明为:

TYPE(*), DIMENSION(..), INTENT(OUT), ASYNCHRONOUS :: buf

即使
buf
是一个标量参数,即没有创建临时副本,符合TR 29113的编译器也不会对缓冲区参数进行寄存器优化。

这本书是否给出了使用它的理由?是不是说这不好?还是说有更好的替代方案?根据这本书(主要参考书之一),目前的MPI 2实现不一定遵循FORTRAN 90的数组语法和形式。不过,它确实显示了一些方法,但没有提到任何关于MPI\u Type\u Create\u子阵列的内容,我认为这更安全
MPI_Send
可能并不总是同步的,但它总是阻塞,并且在返回后,它不再访问数据参数。您的解释仅适用于诸如
MPI\u Isend
之类的调用。实际上,Fortran 2003确实有
volatile
,但是异步在这里应该更好。