【问题标题】:Fortran: dynamic arrays vs. automatic array Avoiding Memory AllocationFortran:动态数组与自动数组避免内存分配
【发布时间】:2012-06-28 03:57:59
【问题描述】:

分析我们的一个 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

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

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
IMPLICIT NONE
...
REAL, DIMENSION(size1,size2) :: work
...
END SUBROUTINE bar
...
END MODULE foo

将生成的配置文件更改为

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

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

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

work_array.f

MODULE work_array
IMPLICIT NONE

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

END MODULE work_array

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

MODULE foo

CONTAINS

SUBROUTINE bar( ... )
...
USE work_array
IMPLICIT NONE
...
END SUBROUTINE bar
...
END MODULE foo

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

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

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

【问题讨论】:

  • Fortran 中的堆数组通过隐式 malloc/free 调用在每个函数调用中分配和释放。在这方面它们与ALLOCATABLE 数组没有什么不同。
  • 我不希望 ifort 会发生这种情况。我一直使用您的第二种方法,即有一个预先分配的缓冲区,我多次使用它而无需解除分配和重新分配。
  • 如果您事先分配一个配置文件,配置文件不可能变得更糟...您找出原因了吗?

标签: fortran


【解决方案1】:

由于work数组只在bar子程序内部使用,你可以给它添加save属性,并在第一次调用子程序时分配它。如果work1work2 与之前的调用相比不同,那么您可以在这种情况下重新分配数组。

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

【讨论】:

    【解决方案2】:

    如果您可以在程序初始化时进行一次分配,那么就没有理由将数组定义为可分配的。把它放在一个共同的地方。

    如果您只需要一个固定大小,但直到运行时才知道该大小,您将需要使用最终选项,即在初始化时进行一次分配。但是,这增加了对分配性能的影响是没有意义的。我需要查看定义和分配代码才能说更多。

    由于分配的是虚拟内存,“内存使用”并不是真正的问题,除非数组太大以至于影响可用地址空间。

    【讨论】:

      猜你喜欢
      • 2021-04-24
      • 2021-04-01
      • 1970-01-01
      • 2011-09-16
      • 2020-08-07
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 2021-04-02
      相关资源
      最近更新 更多