Fortran:动态数组与自动数组避免内存分配

use*_*069 9 fortran

分析我们的一个fortran代码,有两个子程序占用了大部分计算时间(22.1%和17.2%).在每个例程中,大约5%的时间用于分配和释放内存.这些例程看起来像

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
IMPLICIT NONE
...
REAL, ALLOCATABLE, DIMENSION(:,:) :: work
...
ALLOCATE (work(size1,size2))
...
DEALLOCATE (work)
END SUBROUTINE bar
...
END MODULE foo
Run Code Online (Sandbox Code Playgroud)

这些子程序在我的基准测试中被调用大约4000-5000次,所以我想摆脱ALLOCATE和DEALLOCATE.将这些更改为自动数组将更改为分析器输出.

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
IMPLICIT NONE
...
REAL, DIMENSION(size1,size2) :: work
...
END SUBROUTINE bar
...
END MODULE foo
Run Code Online (Sandbox Code Playgroud)

将生成的配置文件更改为

Running Time        Symbol Name
20955.0ms  17.0%    __totzsp_mod_MOD_totzsps
    7.0ms   0.0%        malloc
    5.0ms   0.0%        free
    2.0ms   0.0%        user_trap

16192.0ms  13.2%    __tomnsp_mod_MOD_tomnsps
   20.0ms   0.0%        free
    3.0ms   0.0%        malloc
    1.0ms   0.0%        szone_size_try_large
Run Code Online (Sandbox Code Playgroud)

我看起来像gfortran在堆栈上分配这些而不是那堆,但我担心当这些数组变得太大时会发生什么.

我采取的第二种方法是分配和释放这些数组一次.

work_array.f

MODULE work_array
IMPLICIT NONE

REAL(rprec), ALLOCATABLE, DIMENSION(:,:) :: work

END MODULE work_array
Run Code Online (Sandbox Code Playgroud)

我在代码的不同部分分配了一次.现在我的子程序看起来像

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
USE work_array
IMPLICIT NONE
...
END SUBROUTINE bar
...
END MODULE foo
Run Code Online (Sandbox Code Playgroud)

但是,当我运行代码时,配置文件变得更糟.

Running Time        Symbol Name
30584.0ms  21.6%    __totzsp_mod_MOD_totzsps
 3494.0ms   2.4%        free
 3143.0ms   2.2%        malloc
   27.0ms   0.0%        DYLD-STUB$$malloc_zone_malloc
   19.0ms   0.0%        szone_free_definite_size
    6.0ms   0.0%        malloc_zone_malloc

24325.0ms  17.1%    __tomnsp_mod_MOD_tomnsps
 2937.0ms   2.0%        free
 2456.0ms   1.7%        malloc
   23.0ms   0.0%        DYLD-STUB$$malloc_zone_malloc
    3.0ms   0.0%        szone_free_definite_size
Run Code Online (Sandbox Code Playgroud)

这些额外的mallocs和免费来自哪里?如何设置它以便我分配这些数组一次?

paf*_*fcu 5

由于work数组仅在bar子例程中使用,因此您可以向其添加save属性并在第一次调用子例程时分配它.如果与先前的调用相比work1work2不同,则可以在这种情况下重新分配数组.

一旦不再需要子程序,这确实留下了解除分配的问题.如果你需要在程序的整个生命周期内调用它,那就没问题,因为操作系统应该在程序退出时释放内存.另一方面,如果您在初始化期间只需要它,即使不需要,内存也将保持分配状态.也许你可以在子程序中添加一个参数,告诉它解除分配work数组,如果内存使用有问题的话.