分析我们的一个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
这些额外的mallocs和免费来自哪里?我如何设置它以便我分配这些数组一次?
答案 0 :(得分:5)
由于work
数组仅在bar
子例程中使用,因此可以向其添加save
属性,并在第一次调用子例程时分配它。如果work1
或work2
与之前的调用相比有所不同,则可以在这种情况下重新分配数组。
一旦不再需要子程序,这确实会留下重新分配的问题。如果你需要在程序的整个生命周期内调用它,那就没问题,因为操作系统应该在程序退出时释放内存。另一方面,如果您在初始化期间只需要它,即使不需要,内存也将保持分配状态。也许您可以在子例程中添加一个参数,告诉它释放work
数组,如果内存使用有问题。
答案 1 :(得分:0)
如果你可以在程序初始化时使用单个分配,那么没有理由将数组定义为可分配。把它放在一个共同点。
如果你只需要一个固定的大小,但是在运行之前你不知道这个大小,你将需要使用你的最终选项,在初始化时进行一次分配。但是,这会增加分配性能,这没有任何意义。我需要看到定义和分配代码来说明更多。
因为分配的是虚拟内存,"内存使用"这不是一个真正的问题,除非数组太大而影响可用的地址空间。