MPI - mpi_type_get_extent 和 mpi_type_get_true_extent 之间的区别

MPI - Difference between mpi_type_get_extent and mpi_type_get_true_extent

我在理解 mpi_type_get_extentmpi_type_get_true_extent 之间的区别时遇到了一些问题。实际上,我使用的是前者,期望使用后者获得的结果,所以我检查了 MPI 3.1 Standard,在那里我找到了(在 4.1.8 True Extent of Datatypes)

However, the datatype extent cannot be used as an estimate of the amount of space that needs to be allocated, if the user has modified the extent

这让我觉得只要我没有修改数据类型的范围,我在使用这两个子例程时应该没有任何区别。

但我显然遗漏了一些东西。

声明了以下 MPI 派生数据类型,

sizes    = [10,10,10]
subsizes = [ 3, 3, 3]
starts   = [ 2, 2, 2]
CALL MPI_TYPE_CREATE_SUBARRAY(ndims, sizes, subsizes, starts, MPI_ORDER_FORTRAN, MPI_DOUBLE_PRECISION, newtype, ierr)

以下代码

call mpi_type_size(newtype, k, ierr)
call mpi_type_get_extent(newtype, lb, extent, ierr)                                                                                             
call mpi_type_get_true_extent(newtype, tlb, textent, ierr)
write(*,*) k/DBS, lb/DBS, extent/DBS, tlb/DBS, textent/DBS ! DBS is the size of double precision

产生输出(显然所有进程都相同)

27   0   1000   222   223

所以 mpi_type_size 表现得像我预期的那样,returning PRODUCT(subsizes)*DBS in k;另一方面,我希望 mpi_type_get_extentmpi_type_get_true_extent 只有后者 return 是什么(因为我根本没有修改 newtype),特别是 222 223,基本上是 starts(1) + starts(2)*sizes(1) + starts(3)*sizes(1)*sizes(2)1 + (subsizes - 1)*[1, sizes(1), sizes(1)*sizes(2)].

为什么 mpi_type_get_extent return 0PRODUCT(sizes)lbextent 中,而不考虑 subsizesstarts?

我没有发布 MWE,因为我完全没有错误(不是在编译时,也不是在 运行 时),我只是不明白上述两个例程的工作方式。我基本上希望有人能帮助我理解标准文档中那些子程序的描述,以及为什么获得那些我没想到的结果是正确的。

编辑 根据@GillesGouaillardet 的要求,我添加了一个 "minimal" 工作示例 运行 并且 至少有 4 个进程 (请 运行 它恰好有 4 个进程,所以我们有相同的输出),在这个问题的最后。最后几行可以取消注释(有意识地)以表明代表非连续内存位置的类型在与count > 1一起使用时可以正常工作,一旦它们被适当地调整大小 mpi_type_create_resized 的意思。注释掉这些行后,程序会为所有创建的类型(甚至是那些中间的、未提交的)打印 sizelbextenttrue_lbtrue_extent

 mpi_type_contiguous                    4                    0                    4                    0                    4
 mpi_type_vector                        4                    0                   13                    0                   13
 mpi_type_vector res                    4                    0                    1                    0                   13
 mpi_type_create_subarray               4                    0                   16                    0                   13
 mpi_type_create_subarray res           4                    0                    1                    0                   13

所有类型都表示 4 x 4 矩阵的一行或一列,因此可以预见它们的 size 总是 4;列类型的 extenttrue_extent 都等于 4 单位,因为它代表内存中的四个连续实数;用 mpi_type_vector 创建的类型有 extenttrue_extent 都等于 13 实数,如我所料(见漂亮的草图);如果我想将它与 count > 1 一起使用,我必须调整它的大小,更改它的 extent(并且 true_extent 保持不变);现在困难的部分来了:

mpi_type_create_subarray 创建的类型 16 as extent 是什么?老实说,我希望例程 return 已经调整大小的类型,准备与 count > 1 一起使用( 具有 [=58= 的类型], extent = 1, true_extent = 13), 但似乎不是:令我惊讶的是, extent16,这是全局数组的大小!

问题是:为什么?为什么用 mpi_type_create_subarray 创建的类型的 extentarray_of_sizes 参数的元素的乘积?

program subarray
use mpi
implicit none
integer :: i, j, k, ierr, myid, npro, rs, mycol, myrowugly, myrow_vec, myrow_sub
integer(kind = mpi_address_kind) :: lb, extent, tlb, textent
real, dimension(:,:), allocatable :: mat
call mpi_init(ierr)
call mpi_comm_rank(mpi_comm_world, myid, ierr)
call mpi_comm_size(mpi_comm_world, npro, ierr)
allocate(mat(npro,npro))
mat = myid*1.0
call mpi_type_size(mpi_real, rs, ierr)

call mpi_type_contiguous(npro, mpi_real, mycol, ierr)
call mpi_type_commit(mycol, ierr)
call mpi_type_size(mycol, k, ierr)
call mpi_type_get_extent(mycol, lb, extent, ierr)
call mpi_type_get_true_extent(mycol, tlb, textent, ierr)
if (myid == 0) print *, 'mpi_type_contiguous         ', k/rs, lb/rs, extent/rs, tlb/rs, textent/rs

call mpi_type_vector(npro, 1, npro, mpi_real, myrowugly, ierr)
call mpi_type_size(myrowugly, k, ierr)
call mpi_type_get_extent(myrowugly, lb, extent, ierr)
call mpi_type_get_true_extent(myrowugly, tlb, textent, ierr)
if (myid == 0) print *, 'mpi_type_vector             ', k/rs, lb/rs, extent/rs, tlb/rs, textent/rs
call mpi_type_create_resized(myrowugly, int(0, mpi_address_kind)*rs, int(1, mpi_address_kind)*rs, myrow_vec, ierr)
call mpi_type_commit(myrow_vec, ierr)
call mpi_type_size(myrow_vec, k, ierr)
call mpi_type_get_extent(myrow_vec, lb, extent, ierr)
call mpi_type_get_true_extent(myrow_vec, tlb, textent, ierr)
if (myid == 0) print *, 'mpi_type_vector res         ', k/rs, lb/rs, extent/rs, tlb/rs, textent/rs

call mpi_type_create_subarray(2, [npro, npro], [1, npro], [0, 0], mpi_order_fortran, mpi_real, myrowugly, ierr)
call mpi_type_size(myrowugly, k, ierr)
call mpi_type_get_extent(myrowugly, lb, extent, ierr)
call mpi_type_get_true_extent(myrowugly, tlb, textent, ierr)
if (myid == 0) print *, 'mpi_type_create_subarray    ', k/rs, lb/rs, extent/rs, tlb/rs, textent/rs

call mpi_type_create_resized(myrowugly, int(0, mpi_address_kind)*rs, int(1, mpi_address_kind)*rs, myrow_sub, ierr)
call mpi_type_commit(myrow_sub, ierr)
call mpi_type_size(myrow_sub, k, ierr)
call mpi_type_get_extent(myrow_sub, lb, extent, ierr)
call mpi_type_get_true_extent(myrow_sub, tlb, textent, ierr)
if (myid == 0) print *, 'mpi_type_create_subarray res', k/rs, lb/rs, extent/rs, tlb/rs, textent/rs

!if (myid == 0) call mpi_send(mat(1,1), 2, mycol, 1, 666, mpi_comm_world, ierr)
!if (myid == 0) call mpi_recv(mat(1,3), 2, mycol, 1, 666, mpi_comm_world, mpi_status_ignore, ierr)
!if (myid == 1) call mpi_recv(mat(1,1), 2, mycol, 0, 666, mpi_comm_world, mpi_status_ignore, ierr)
!if (myid == 1) call mpi_send(mat(1,3), 2, mycol, 0, 666, mpi_comm_world, ierr)
!if (myid == 0) call mpi_send(mat(1,1), 2, myrow_vec, 1, 666, mpi_comm_world, ierr)
!if (myid == 0) call mpi_recv(mat(3,1), 2, myrow_vec, 1, 666, mpi_comm_world, mpi_status_ignore, ierr)
!if (myid == 1) call mpi_recv(mat(1,1), 2, myrow_vec, 0, 666, mpi_comm_world, mpi_status_ignore, ierr)
!if (myid == 1) call mpi_send(mat(3,1), 2, myrow_vec, 0, 666, mpi_comm_world, ierr)
!if (myid == 0) call mpi_send(mat(1,1), 2, myrow_sub, 1, 666, mpi_comm_world, ierr)
!if (myid == 0) call mpi_recv(mat(3,1), 2, myrow_sub, 1, 666, mpi_comm_world, mpi_status_ignore, ierr)
!if (myid == 1) call mpi_recv(mat(1,1), 2, myrow_sub, 0, 666, mpi_comm_world, mpi_status_ignore, ierr)
!if (myid == 1) call mpi_send(mat(3,1), 2, myrow_sub, 0, 666, mpi_comm_world, ierr)
!do i = 0, npro
!if (myid == i) then
!print *, ""
!print *, myid
!do j = 1, npro
!print *, mat(j,:)
!end do
!end if
!call mpi_barrier(mpi_comm_world, ierr)
!end do

call mpi_finalize(ierr)
end program subarray

MPI_Type_create_subarray() 创建一个派生数据类型,根据定义,其范围是所有大小的乘积。

定义在 MPI 3.1 标准第 96 页。

MPI_Type_create_subarray() 通常用于 MPI-IO,所以这个范围的定义在那里是有意义的。

在这种非常具体的情况下,这可能不是您想要的,但请考虑一个 4x4 数组的 2x2 子数组。您希望达到什么程度?