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_extent
和 mpi_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_extent
和 mpi_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 0
和 PRODUCT(sizes)
在 lb
和 extent
中,而不考虑 subsizes
和 starts
?
我没有发布 MWE,因为我完全没有错误(不是在编译时,也不是在 运行 时),我只是不明白上述两个例程的工作方式。我基本上希望有人能帮助我理解标准文档中那些子程序的描述,以及为什么获得那些我没想到的结果是正确的。
编辑
根据@GillesGouaillardet 的要求,我添加了一个 "minimal" 工作示例 运行 并且 至少有 4 个进程 (请 运行 它恰好有 4 个进程,所以我们有相同的输出),在这个问题的最后。最后几行可以取消注释(有意识地)以表明代表非连续内存位置的类型在与count > 1
、一起使用时可以正常工作,一旦它们被适当地调整大小 mpi_type_create_resized
的意思。注释掉这些行后,程序会为所有创建的类型(甚至是那些中间的、未提交的)打印 size
、lb
、extent
、true_lb
、true_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
;列类型的 extent
和 true_extent
都等于 4
单位,因为它代表内存中的四个连续实数;用 mpi_type_vector
创建的类型有 extent
和 true_extent
都等于 13
实数,如我所料(见漂亮的草图);如果我想将它与 count > 1
一起使用,我必须调整它的大小,更改它的 extent
(并且 true_extent
保持不变);现在困难的部分来了:
用 mpi_type_create_subarray
创建的类型 16
as extent
是什么?老实说,我希望例程 return 已经调整大小的类型,准备与 count > 1
一起使用( 即 具有 [=58= 的类型], extent = 1
, true_extent = 13
), 但似乎不是:令我惊讶的是, extent
是 16
,这是全局数组的大小!
问题是:为什么?为什么用 mpi_type_create_subarray
创建的类型的 extent
是 array_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 子数组。您希望达到什么程度?
我在理解 mpi_type_get_extent
和 mpi_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_extent
和 mpi_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 0
和 PRODUCT(sizes)
在 lb
和 extent
中,而不考虑 subsizes
和 starts
?
我没有发布 MWE,因为我完全没有错误(不是在编译时,也不是在 运行 时),我只是不明白上述两个例程的工作方式。我基本上希望有人能帮助我理解标准文档中那些子程序的描述,以及为什么获得那些我没想到的结果是正确的。
编辑
根据@GillesGouaillardet 的要求,我添加了一个 "minimal" 工作示例 运行 并且 至少有 4 个进程 (请 运行 它恰好有 4 个进程,所以我们有相同的输出),在这个问题的最后。最后几行可以取消注释(有意识地)以表明代表非连续内存位置的类型在与count > 1
、一起使用时可以正常工作,一旦它们被适当地调整大小 mpi_type_create_resized
的意思。注释掉这些行后,程序会为所有创建的类型(甚至是那些中间的、未提交的)打印 size
、lb
、extent
、true_lb
、true_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
;列类型的 extent
和 true_extent
都等于 4
单位,因为它代表内存中的四个连续实数;用 mpi_type_vector
创建的类型有 extent
和 true_extent
都等于 13
实数,如我所料(见漂亮的草图);如果我想将它与 count > 1
一起使用,我必须调整它的大小,更改它的 extent
(并且 true_extent
保持不变);现在困难的部分来了:
用 mpi_type_create_subarray
创建的类型 16
as extent
是什么?老实说,我希望例程 return 已经调整大小的类型,准备与 count > 1
一起使用( 即 具有 [=58= 的类型], extent = 1
, true_extent = 13
), 但似乎不是:令我惊讶的是, extent
是 16
,这是全局数组的大小!
问题是:为什么?为什么用 mpi_type_create_subarray
创建的类型的 extent
是 array_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 子数组。您希望达到什么程度?