具有动态分配成员的动态分配结构的 MPI 派生数据类型
MPI derived datatype for dynamically allocated structs with dynamically allocated member
有一个动态分配的结构:
TYPE Struct
INTEGER :: N
REAL*8 :: A
REAL*8,ALLOCATABLE :: B(:)
END TYPE Struct
它有一个动态分配的成员:B(:)
当我尝试使用 MPI_TYPE_CREATE_STRUCT 为此类结构创建派生数据类型时,不同的 CPU 会创建不一致的派生数据类型。
这是因为在不同的 CPU 上,Struct%B(:) 可能位于相对于第一个成员 Struct%N 的不同内存位置。
那么MPI_SEND(Struct,...)不会成功...
那么,如果我真的想使用 MPI 派生数据类型发送这个结构,如何解决这个问题?
或者这种派生数据类型是被禁止的?
要发送该结构的 一个 元素,只需 通过使用 MPI_TYPE_CREATE_STRUCT
创建结构化数据类型即可。根据程序的堆和堆栈相对于彼此的位置,B(1)
相对于 N
的偏移量最终可能会是一个巨大的正数或负数,但这不是大多数 Unix 平台上的问题,数字应在 INTEGER(KIND=MPI_ADDRESS_KIND)
.
范围内
重要:结构的单独实例很可能相对于N
有不同的B
偏移量,因此只能使用MPI数据类型用于发送在构造数据类型期间用于获取偏移量的特定记录。
When I try to use MPI_TYPE_CREATE_STRUCT to create a derived datatype for such Struct, it happens that differen CPUs create inconsistent derived datatypes. This is because Struct%B(:) might be located in different memory locations relative to the first memeber Struct%N, on different CPUs.
这不是问题。通信操作双方的 MPI 数据类型只能是一致的,这意味着它们应该由相同顺序的相同基本数据类型组成。 每个元素的偏移量是无关的。换句话说,只要发送方和接收方在MPI_TYPE_CREATE_STRUCT
调用中指定相同类型和数量的数据元素,程序将正常运行。
要发送多个元素,事情会变得有点复杂。有两种解决方案:
使用MPI_PACK
在发送方序列化数据,MPI_UNPACK
在接收方反序列化数据。由于打包和解包需要额外的缓冲区 space,这会使程序的内存需求加倍。
或
为每个记录创建一个单独的 MPI 结构数据类型,然后创建一个组合所有记录的结构数据类型。下面是一个示例,说明如何发送两个这样的结构的数组,一个在 B
中有 10 个元素,一个在 B
中有 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 数据类型描述符存在于由库管理的内存中,及时释放不再需要的数据类型也很重要。
有一个动态分配的结构:
TYPE Struct
INTEGER :: N
REAL*8 :: A
REAL*8,ALLOCATABLE :: B(:)
END TYPE Struct
它有一个动态分配的成员:B(:)
当我尝试使用 MPI_TYPE_CREATE_STRUCT 为此类结构创建派生数据类型时,不同的 CPU 会创建不一致的派生数据类型。 这是因为在不同的 CPU 上,Struct%B(:) 可能位于相对于第一个成员 Struct%N 的不同内存位置。
那么MPI_SEND(Struct,...)不会成功...
那么,如果我真的想使用 MPI 派生数据类型发送这个结构,如何解决这个问题?
或者这种派生数据类型是被禁止的?
要发送该结构的 一个 元素,只需 MPI_TYPE_CREATE_STRUCT
创建结构化数据类型即可。根据程序的堆和堆栈相对于彼此的位置,B(1)
相对于 N
的偏移量最终可能会是一个巨大的正数或负数,但这不是大多数 Unix 平台上的问题,数字应在 INTEGER(KIND=MPI_ADDRESS_KIND)
.
重要:结构的单独实例很可能相对于N
有不同的B
偏移量,因此只能使用MPI数据类型用于发送在构造数据类型期间用于获取偏移量的特定记录。
When I try to use MPI_TYPE_CREATE_STRUCT to create a derived datatype for such Struct, it happens that differen CPUs create inconsistent derived datatypes. This is because Struct%B(:) might be located in different memory locations relative to the first memeber Struct%N, on different CPUs.
这不是问题。通信操作双方的 MPI 数据类型只能是一致的,这意味着它们应该由相同顺序的相同基本数据类型组成。 每个元素的偏移量是无关的。换句话说,只要发送方和接收方在MPI_TYPE_CREATE_STRUCT
调用中指定相同类型和数量的数据元素,程序将正常运行。
要发送多个元素,事情会变得有点复杂。有两种解决方案:
使用MPI_PACK
在发送方序列化数据,MPI_UNPACK
在接收方反序列化数据。由于打包和解包需要额外的缓冲区 space,这会使程序的内存需求加倍。
或
为每个记录创建一个单独的 MPI 结构数据类型,然后创建一个组合所有记录的结构数据类型。下面是一个示例,说明如何发送两个这样的结构的数组,一个在 B
中有 10 个元素,一个在 B
中有 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 数据类型描述符存在于由库管理的内存中,及时释放不再需要的数据类型也很重要。