我观察到有关派生数据类型的内存使用情况的奇怪行为。以下fortran90代码演示了这个问题。
module prec
implicit none
integer, parameter :: d_t = selected_real_kind(15,307)
end module
module typdef
use prec
implicit none
type level_2
real(kind=d_t), allocatable :: lev_3(:)
end type
type level_1
type(level_2),allocatable :: lev_2(:,:)
end type
type array
type(level_1),allocatable :: lev_1(:,:)
end type
end module
program mem_test
use prec
use typdef
implicit none
integer :: n,i,j,k,l,m,egmax,niter,ncells,namom,nsmom
real(kind=d_t),allocatable :: simple_array(:,:,:,:,:)
type(array) :: fancy_array
real(kind=d_t) :: it
egmax=7
niter=2
ncells=3000000
namom=1
nsmom=1
!
!
!
allocate( simple_array(egmax,niter,ncells,namom,nsmom) )
!
!
!
allocate( fancy_array%lev_1(egmax,niter))
do i=1,niter
do j=1,egmax
allocate( fancy_array%lev_1(j,i)%lev_2(ncells,namom) )
end do
end do
do i=1,niter
do j=1,egmax
do k=1,namom
do l=1,ncells
allocate( fancy_array%lev_1(j,i)%lev_2(l,k)%lev_3(nsmom) )
end do
end do
end do
end do
!
do n=1,100000
it=0.0_d_T
do i=1,100000
it=it+1.0_d_t
end do
end do
!
!
deallocate(simple_array)
deallocate(fancy_array%lev_1)
end program
我想将数据存储在多维数组中(例如,max * niter * ncell * namom * nsmom双精度数)。我以两种不同的方式做到了这一点:
我使用
编译了代码 ifort -g -o test.exe file.f90
我在valgrind中运行它并比较了simple_array和fancy_array的内存消耗。 simple_array按预期使用大约300Mb,而fancy_array使用3Gb(10倍),即使它存储 相同数量的实数。因此,它也应该只消耗300Mb。
运行一个更简单的测试用例,其中派生类型只有一个深度,例如
type level_1
real(kind=d_t),allocatable :: subarray(:)
end type
type array
type(level_1),allocatable :: lev_1(:)
end type
正好消耗我期望的内存量。它不消耗10倍 记忆。有没有人观察到类似的行为或有任何线索为什么会发生这种情况?我唯一的想法是所描述的行为的原因是fancy_array分配了非连续的内存,并且fortran需要跟踪它,因此内存消耗的增加。我将不胜感激任何意见或类似意见。
感谢您的帮助。
塞巴斯蒂安
答案 0 :(得分:6)
(可分配组件是Fortran 2003功能。)
Fortran处理器(包括英特尔Fortran)实现可分配数组对象的典型方法是使用描述符 - 一种数据结构,其中包含内存中数组数据的位置以及每个维度的边界和跨度等信息数组amongst other things。
对于x64平台上的英特尔Fortran,描述符占用一维可分配数组的72个字节。在派生类型的情况下,您有大约4200万个这样的数组 - 一个用于您存在的每个lev_3
组件,加上父可分配组件的数量要小得多。 72字节乘以4200万,大约3 GB。可能存在与底层内存分配器相关的进一步开销。
在同一平台上,排名为5的数组的描述符占用168个字节,并且只有一个内存分配
这两种方法的数据存储要求大致相同。
请注意,这两种方法提供的功能明显不同(因此开销的差异) - 在派生类型的情况下,您可以更改每个 {{1}的分配状态,范围和范围} 零件。在单个数组的情况下,您没有任何接近灵活性的任何地方 - 如果分配该数组必须是矩形的。
(在Fortran 90中,组件在声明中的维度需要是常量表达式(在编译时固定)。不使用描述符,两种方法的内存需求会收敛。)