2016-09-29 50 views
1

有一个动态分配的结构:与动态分配的成员的动态分配结构MPI的数据类型

TYPE Struct  
    INTEGER :: N 
    REAL*8 :: A 
    REAL*8,ALLOCATABLE :: B(:) 
END TYPE Struct 

,它有一个动态分配的memeber:B(:)

当我尝试使用MPI_TYPE_CREATE_STRUCT为这种Struct创建派生数据类型,碰巧不同的CPU会创建不一致的派生数据类型。 这是因为在不同的CPU上,Struct%B(:)可能位于相对于第一个构件StructN的不同内存位置。

然后MPI_SEND(结构,...)是不会得逞的......

那么,怎么会这样的问题可以解决,如果我真的想用MPI的数据类型来发送这个结构?

或者这样的派生数据类型是被禁止的?

+0

你写这整个问题的方式很奇怪,你没有提到你使用的语言Fortran。好吧,我为你加了标签。 –

+0

你见过http://stackoverflow.com/questions/2258759/passing-variable-length-structures-between-mpi-processes和http://stackoverflow.com/questions/4273253/variable-sized-message-in- mpi? –

+0

对不起国旗...我刚才看到这些链接,他们没有解决我遇到的问题,我的意思是: 使用REAL * 8 :: B(2)而不是allocatable一个是好的,没有问题! 问题显示当我使用REAL * 8,ALLOCATABLE :: B(:) @JohnZwinck –

回答

3

通过创建使用MPI_TYPE_CREATE_STRUCT的结构化数据类型发送结构的一个元件,简单地proceed as usual。根据程序堆和程序堆相互之间的位置关系,B(1)相对于N的偏移量最终可能是一个巨大的正数或负数,但这在大多数Unix平台上都没有问题,数字应该在INTEGER(KIND=MPI_ADDRESS_KIND)的范围内。

重要:结构的不同实例将最有可能B相对的不同偏移N,因此只能用于发送这是用于在施工过程中获得的偏移量的特定记录的MPI数据类型数据类型。

当我尝试使用MPI_TYPE_CREATE_STRUCT为这样的Struct创建派生数据类型时,碰巧不同的CPU会创建不一致的派生数据类型。这是因为在不同的CPU上,Struct%B(:)可能位于与第一个元素Struct%N不同的内存位置。

这是一个非问题。通信操作两边的MPI数据类型只能是一致的,这意味着它们应该由相同序列中相同的基本数据类型组成。 每个元素的偏移量是无关紧要的。换句话说,只要发送方和接收方在调用MPI_TYPE_CREATE_STRUCT时指定了相同类型和数量的数据元素,程序就会正常工作。

要发送多个元素,事情会变得有点复杂。有两种解决方案:

使用MPI_PACK来串行化发送端数据和MPI_UNPACK以在接收端对其进行反序列化。由于打包和解包需要额外的缓冲空间,这使得程序的内存需求增加了一倍。

创建为每个记录单独的MPI结构数据类型,然后创建一个结合了所有记录的数据类型结构。下面是如何发送两个这样的结构的阵列,一个在B 10元和一个20一个例子:

TYPE(Struct) :: Structs(2) 

ALLOCATE(Structs(1)%B(10)) 
ALLOCATE(Structs(2)%B(20)) 

! (1) Create a separate structure datatype for each record 
DO i=1,2 
    CALL MPI_GET_ADDRESS(Structs(i)%N, POS_(1), IError) 
    CALL MPI_GET_ADDRESS(Structs(i)%A, POS_(2), IError) 
    CALL MPI_GET_ADDRESS(Structs(i)%B(1), POS_(3), IError) 
    Offsets = POS_ - POS_(1) 

    Types(1) = MPI_INTEGER 
    Types(2) = MPI_REAL8 
    Types(3) = MPI_REAL8 

    Blocks(1) = 1 
    Blocks(2) = 1 
    Blocks(3) = i * 10 

    CALL MPI_TYPE_CREATE_STRUCT(3, Blocks, Offsets, Types, Elem_Type(i), IError) 
END DO 

! (2) Create a structure of structures that describes the whole array 
CALL MPI_GET_ADDRESS(Structs(1)%N, POS_(1), IError) 
CALL MPI_GET_ADDRESS(Structs(2)%N, POS_(2), IError) 
Offsets = POS_ - POS_(1) 

Types(1) = Elem_Type(1) 
Types(2) = Elem_Type(2) 

Blocks(1) = 1 
Blocks(2) = 1 

CALL MPI_TYPE_CREATE_STRUCT(2, Blocks, Offsets, Types, TwoElem_Type, IError) 
CALL MPI_TYPE_COMMIT(TwoElem_Type, IError) 

! (2.1) Free the intermediate datatypes 
DO i=1,2 
    CALL MPI_TYPE_FREE(Elem_Type(i), IError) 
END DO 

! (3) Send the array 
CALL MPI_SEND(Structs(1)%N, 1, TwoElem_Type, ...) 

需要注意的是,虽然构建MPI数据类型是一种相对廉价的操作,你不应该使用上述程序发送,例如1000000个结构化类型的实例。此外,MPI数据类型描述符位于库管理的内存中,并且不再需要的数据类型的及时释放非常重要。

+0

我明白了! 我真的从中学到了很多! 〜^ _ ^〜 –

+0

如果阵列中的结构数量变化很大,该怎么办?每次我们需要定义一个新的数据类型,例如'TwoElem_Type','MillionElem_Type'等等。 – Shibli

+0

是的,不幸的是,MPI被设计为主要用于“线性”类型,也就是说,类型被按顺序存储在内存中,模式。数据存储在内存中并通过指针引用的分支类型不直接受支持,而且必须破解它们。尽管如此,对于非常大的数据对象,构建数据类型比通过网络发送数据要便宜得多,所以它不应该是一个大问题。 –