Fortran 90中使用标准数组和派生类型时的内存使用差异

4
我注意到关于派生数据类型的内存使用存在奇怪的行为。以下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双精度数字)。我有两种不同的方法:

  1. 一个多维标准数组“simple_array(egmax,niter,...,)”
  2. 一个嵌套的派生数据结构“fancy_array”,如我所提供的代码中定义的那样。

我使用以下编译代码:

    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需要跟踪它,因此导致内存消耗增加。如果您有任何意见或类似的观察结果,请不吝赐教。

感谢您的帮助。

Sebastian

1个回答

6
(可分配组件是Fortran 2003功能。)Fortran处理器(包括Intel Fortran)实现可分配数组对象的典型方法是使用描述符 - 包含信息的数据结构,例如数组数据在内存中的位置以及每个维度的边界和步幅,等等
对于x64平台上的Intel Fortran,在一维可分配数组的情况下,该描述符需要72字节。 在您的派生类型情况下,您大约有4200万个这样的数组 - 对于每个创建的lev_3组件都需要一个,再加上更少量的父分配组件。 72乘以4200万约为3 GB。 与底层内存分配器相关的进一步开销可能存在。
在同一平台上,五阶数组的描述符需要168字节,并且仅有一个内存分配。
两种方法的数据存储要求大致相同。
请注意,这两种方法所提供的功能显著不同(因此开销也不同)- 在派生类型的情况下,您可以更改每个 lev_3组件的分配状态、边界和范围。在单个数组的情况下,您没有那么大的灵活性-如果分配了该数组,则必须是矩形的。
(在Fortran 90中,组件声明中的维数需要是常量表达式(在编译时固定)。不使用描述符,两种方法的内存要求将收敛。)

谢谢你的回答!我认为这解决了问题。当然,你是对的,可分配组件在派生类型中是2003年的功能。即使它使用了2003年的元素,我仍然认为我的代码是Fortran90的某种形式。 - user2014624
嗯,如果我能够进行乘法运算,那就可以解决问题了。可分配对象的数量与实际相差一个数量级。 - IanH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接