2012-06-26 62 views
8

剖析我们的fortran代码之一,有两个子程序占用了大部分计算时间(22.1%和17.2%)。在每个程序中,约5%的时间花在分配和释放内存上。这些程序看起来像Fortran:动态数组与自动数组避免内存分配

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次,我的基准点,所以我想摆脱分配和释放的。将这些更改为自动数组会将分析器输出更改为。

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 

在哪里这些额外mallocs,并释放来自哪里?我怎么设置这个,所以我分配这些数组一次?

+1

Fortran中的堆数组通过隐式'malloc' /'free'调用在每个函数调用上分配和释放。它们在这方面与'ALLOCATABLE'数组没有区别。 –

+1

我不希望这种情况发生在ifort上。我始终使用第二种方法,即使用多次预分配的缓冲区而不释放和重新分配。 – bdforbes

+0

当您事先分配一个配置文件时,配置文件不可能变得更糟......您是否找出原因? – Lupocci

回答

4

由于work数组仅用于bar子例程中,因此您可以将save属性添加到它并在第一次调用子例程时进行分配。如果work1work2与先前的调用相比有所不同,则可以在此情况下重新分配阵列。

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

0

如果你可以在程序初始化时获得一个分配,那么没有理由将数组定义为可分配。把它放在一个共同的。

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

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