mpi_gather 不 return 具有 Fortran 派生数据类型的整个向量
mpi_gather doesn't return entire vector with fortran derived datatype
我 运行 遇到一个问题,其中 mpi_gather 只有 returns 我试图传递的矢量的一小部分。请注意,我是 运行 这与 np 1,但它也发生在 np 2 和 np 3 上。NAT = 3(nat = 原子数),并且有 194 个唯一对。
为了做到这一点,我在 fortran 中有两种派生数据类型:
type dtlrdh_lut
sequence
integer p
integer q
integer ind
real(dp), dimension(3, 3) :: TLR
real(dp), dimension(3, 3, 3, 3) :: dTLRdh
end type dtlrdh_lut
在我的子程序中,我定义了我的变量:
type(dtlrdh_lut), dimension(:), allocatable :: my_dtlrdh, collected_dtlrdh
integer :: dh_dtype, dr_dtype, dh_types(5), dr_types(6), dh_blocks(5), dr_blocks(6)
INTEGER(kind=MPI_ADDRESS_KIND) :: dh_offsets(5), dr_offsets(6)
integer :: numtasks, rank, ierr, dh_displs(nproc_image), dr_displs(nproc_image)
integer :: n, status(mpi_status_size)
我用另一种方法拆分了进程之间的工作,然后计算查找 table 需要计算的元素数量,并在这个特定节点上分配本地查找 tables所以:
my_num_pairs = 0
do i = 1, num_pairs, 1
if(unique_pairs(i)%cpu.eq.me_image) then
my_num_pairs = my_num_pairs + 1
end if
end do
if(.not.allocated(my_dtlrdh)) allocate(my_dtlrdh(my_num_pairs))
我还分配了查找 table 并将其归零,所有内容都将与以下代码组合回去:
如果(me_image.eq.root_image)那么
如果(。not.allocated(collected_dtlrdh))分配(collected_dtlrdh(num_pairs))
do i=1,my_num_pairs,1
collected_dtlrdh(i)%p = 0
collected_dtlrdh(i)%q = 0
collected_dtlrdh(i)%ind = 0
collected_dtlrdh(i)%TLR = 0.0_DP
collected_dtlrdh(i)%dTLRdh = 0.0_DP
end do
end if
然后我填写查询 table,但我将跳过该代码。它很长而且不相关。完成此操作后,就可以启动 MPI 进程以将所有内容收集回根进程。
call mpi_get_address(my_dtlrdh(1)%p, dh_offsets(1), ierr)
call mpi_get_address(my_dtlrdh(1)%q, dh_offsets(2), ierr)
call mpi_get_address(my_dtlrdh(1)%ind, dh_offsets(3), ierr)
call mpi_get_address(my_dtlrdh(1)%TLR(1,1), dh_offsets(4), ierr)
call mpi_get_address(my_dtlrdh(1)%dTLRdh(1,1,1,1), dh_offsets(5), ierr)
do i = 2, size(dh_offsets)
dh_offsets(i) = dh_offsets(i) - dh_offsets(1)
end do
dh_offsets(1) = 0
dh_types = (/MPI_INTEGER, MPI_INTEGER, MPI_INTEGER, MPI_DOUBLE_PRECISION, MPI_DOUBLE_PRECISION/)
dh_blocks = (/1, 1, 1, 3*3, 3*3*3*3/)
call mpi_type_struct(5, dh_blocks, dh_offsets, dh_types, dh_dtype, ierr)
call mpi_type_commit(dh_dtype, ierr)
然后我通过以下方式召集:
call mpi_gather(my_dtlrdh, my_num_pairs, dh_dtype, &
collected_dtlrdh, my_num_pairs, dh_dtype, &
root_image, intra_image_comm, ierr)
收集后,我可以打印出所有内容:
do i = 1, num_pairs, 1
write(stdout, *) my_dtlrdh(i)%p, collected_dtlrdh(i)%p, my_dtlrdh(i)%q, collected_dtlrdh(i)%q
end do
这是我看到非常奇怪的信息的地方。打印出来的前几个元素看起来不错:
1 1 3 3
1 1 6 6
1 1 9 9
但是我的向量的尾端看起来像我只发送 174 个元素而不是完整的 194 个元素:
17 0 24 0
18 0 19 0
18 0 20 0
18 0 21 0
18 0 22 0
鉴于有 194 对,而 num_pairs 和 my_num_pairs 都等于 194,我很困惑。我继续前进,开始绝望地玩耍,发现如果我使用这个 gather 调用而不是上面的调用,我会得到完整的向量:
num_pairs = 2*num_pairs+40
call mpi_gather(my_dtlrdh, num_pairs, dh_dtype, &
collected_dtlrdh, num_pairs, dh_dtype, &
root_image, intra_image_comm, ierr)
这是我通过猜测和检查发现的。虽然这可能适用于这个小型测试系统,但它看起来不像是可扩展的解决方案。我完全不知所措......有什么想法吗?如果你们需要我提供更多信息,请告诉我。
MPI_TYPE_STRUCT
已 弃用 取而代之的是 MPI_TYPE_CREATE_STRUCT
。后者在概念上采用与前者相同的参数,但偏移数组的类型为 INTEGER(KIND=MPI_ADDRESS_KIND)
,即 MPI_GET_ADDRESS
.
返回的类型
在使用 MPI 数据类型数组时,您还应该考虑对齐问题,因为尽管有 SEQUENCE 属性,您的派生类型可能会被编译器在末尾填充一些字节。因此,根据 MPI_GET_ADDRESS() 应用于 my_dtlrdh(1) 和 my_dtlrdh(2) 的输出之间的差异调整 dh_dtype 的大小是一个好主意,方法是MPI_TYPE_CREATE_RESIZED() 子例程。
第 41 页 lecture on datatypes 对此进行了解释
但这可能不足以解释您的问题。
我 运行 遇到一个问题,其中 mpi_gather 只有 returns 我试图传递的矢量的一小部分。请注意,我是 运行 这与 np 1,但它也发生在 np 2 和 np 3 上。NAT = 3(nat = 原子数),并且有 194 个唯一对。
为了做到这一点,我在 fortran 中有两种派生数据类型:
type dtlrdh_lut
sequence
integer p
integer q
integer ind
real(dp), dimension(3, 3) :: TLR
real(dp), dimension(3, 3, 3, 3) :: dTLRdh
end type dtlrdh_lut
在我的子程序中,我定义了我的变量:
type(dtlrdh_lut), dimension(:), allocatable :: my_dtlrdh, collected_dtlrdh
integer :: dh_dtype, dr_dtype, dh_types(5), dr_types(6), dh_blocks(5), dr_blocks(6)
INTEGER(kind=MPI_ADDRESS_KIND) :: dh_offsets(5), dr_offsets(6)
integer :: numtasks, rank, ierr, dh_displs(nproc_image), dr_displs(nproc_image)
integer :: n, status(mpi_status_size)
我用另一种方法拆分了进程之间的工作,然后计算查找 table 需要计算的元素数量,并在这个特定节点上分配本地查找 tables所以:
my_num_pairs = 0
do i = 1, num_pairs, 1
if(unique_pairs(i)%cpu.eq.me_image) then
my_num_pairs = my_num_pairs + 1
end if
end do
if(.not.allocated(my_dtlrdh)) allocate(my_dtlrdh(my_num_pairs))
我还分配了查找 table 并将其归零,所有内容都将与以下代码组合回去: 如果(me_image.eq.root_image)那么 如果(。not.allocated(collected_dtlrdh))分配(collected_dtlrdh(num_pairs))
do i=1,my_num_pairs,1
collected_dtlrdh(i)%p = 0
collected_dtlrdh(i)%q = 0
collected_dtlrdh(i)%ind = 0
collected_dtlrdh(i)%TLR = 0.0_DP
collected_dtlrdh(i)%dTLRdh = 0.0_DP
end do
end if
然后我填写查询 table,但我将跳过该代码。它很长而且不相关。完成此操作后,就可以启动 MPI 进程以将所有内容收集回根进程。
call mpi_get_address(my_dtlrdh(1)%p, dh_offsets(1), ierr)
call mpi_get_address(my_dtlrdh(1)%q, dh_offsets(2), ierr)
call mpi_get_address(my_dtlrdh(1)%ind, dh_offsets(3), ierr)
call mpi_get_address(my_dtlrdh(1)%TLR(1,1), dh_offsets(4), ierr)
call mpi_get_address(my_dtlrdh(1)%dTLRdh(1,1,1,1), dh_offsets(5), ierr)
do i = 2, size(dh_offsets)
dh_offsets(i) = dh_offsets(i) - dh_offsets(1)
end do
dh_offsets(1) = 0
dh_types = (/MPI_INTEGER, MPI_INTEGER, MPI_INTEGER, MPI_DOUBLE_PRECISION, MPI_DOUBLE_PRECISION/)
dh_blocks = (/1, 1, 1, 3*3, 3*3*3*3/)
call mpi_type_struct(5, dh_blocks, dh_offsets, dh_types, dh_dtype, ierr)
call mpi_type_commit(dh_dtype, ierr)
然后我通过以下方式召集:
call mpi_gather(my_dtlrdh, my_num_pairs, dh_dtype, &
collected_dtlrdh, my_num_pairs, dh_dtype, &
root_image, intra_image_comm, ierr)
收集后,我可以打印出所有内容:
do i = 1, num_pairs, 1
write(stdout, *) my_dtlrdh(i)%p, collected_dtlrdh(i)%p, my_dtlrdh(i)%q, collected_dtlrdh(i)%q
end do
这是我看到非常奇怪的信息的地方。打印出来的前几个元素看起来不错:
1 1 3 3
1 1 6 6
1 1 9 9
但是我的向量的尾端看起来像我只发送 174 个元素而不是完整的 194 个元素:
17 0 24 0
18 0 19 0
18 0 20 0
18 0 21 0
18 0 22 0
鉴于有 194 对,而 num_pairs 和 my_num_pairs 都等于 194,我很困惑。我继续前进,开始绝望地玩耍,发现如果我使用这个 gather 调用而不是上面的调用,我会得到完整的向量:
num_pairs = 2*num_pairs+40
call mpi_gather(my_dtlrdh, num_pairs, dh_dtype, &
collected_dtlrdh, num_pairs, dh_dtype, &
root_image, intra_image_comm, ierr)
这是我通过猜测和检查发现的。虽然这可能适用于这个小型测试系统,但它看起来不像是可扩展的解决方案。我完全不知所措......有什么想法吗?如果你们需要我提供更多信息,请告诉我。
MPI_TYPE_STRUCT
已 弃用 取而代之的是 MPI_TYPE_CREATE_STRUCT
。后者在概念上采用与前者相同的参数,但偏移数组的类型为 INTEGER(KIND=MPI_ADDRESS_KIND)
,即 MPI_GET_ADDRESS
.
在使用 MPI 数据类型数组时,您还应该考虑对齐问题,因为尽管有 SEQUENCE 属性,您的派生类型可能会被编译器在末尾填充一些字节。因此,根据 MPI_GET_ADDRESS() 应用于 my_dtlrdh(1) 和 my_dtlrdh(2) 的输出之间的差异调整 dh_dtype 的大小是一个好主意,方法是MPI_TYPE_CREATE_RESIZED() 子例程。
第 41 页 lecture on datatypes 对此进行了解释
但这可能不足以解释您的问题。