我有一个 Fortran90 程序 (Packmol),直到它是通过静态内存分配实现的。
我将代码更改为使用动态分配,以便在开始时分配所有数组。在某些示例中,我的性能损失了 400%。
然后,我验证了即使数组的大小与我使用静态分配时的大小相同,问题仍然存在。也就是说,如果我将分配更改为
类似
That 的内容,则足以导致性能损失。当然,当所有需要动态分配的数组都这样做时,大约有 30 个。
double precision :: x(1000)
double precision, allocatable :: x(:)
allocate(x(1000))
有没有办法以更有效的方式分配数组以减少性能损失?或者有人有不同的建议?
非常感谢。
编辑:不知何故,问题得到了解决。动态版本现在只比静态版本慢一点,这是预期的。我真的不知道是什么导致了之前的重大放缓。
造成这种性能损失的原因可能有很多:
1) 静态数组始终在 BSS 上分配(请参阅静态变量存储在哪里(在 C/C++ 中)?),而“已分配”数组可以在堆或堆栈上分配。堆栈上的分配比堆上的分配快得多。一个好的编译器可以生成在堆栈上分配尽可能多的内存的代码。
2) 您可能在循环中有分配/解除分配语句。每次内存分配都会花费一些时间。一个好的编译器可以避免在每次分配时物理地分配一些内存,而是重新使用已释放的空间。
3)编译器在编译时就知道静态数组的维度,因此它会做一些额外的优化。
4)如果有多维数组,则无法在编译时计算元素的地址。例如, 的地址A(5,6,7)
是,5 + 6*n1 + 7*n1*n2
是:的维度。对于静态数组,编译器可以优化这部分。此外,如果维度是 2 的幂,编译器将生成位移位,而不是执行整数乘法,速度快 3 倍。n1
n2
A
A(n1,n2,n3)
n1,n2,...
3)是最有可能的。您可以为在编译时知道合理上限的数组保留一些静态数组,这些数组相对较小(大约<1000个元素),并且也在经常调用且执行少量工作的例程内。
根据经验,只有小型数组可以静态分配:大多数 1D 数组、一些小型 2D 数组和微小的 3D 数组。将所有其余的转换为动态分配,因为它们可能无法放入堆栈中。
如果您有一些频繁的分配/解除分配,因为您在循环中调用子例程,如下所示:
do i=1,10000000
call work(a,b)
end do
subroutine work(a,b)
...
allocate (c)
...
deallocate (c)
end
Run Code Online (Sandbox Code Playgroud)
如果c
始终具有相同的维度,您可以将其作为子例程的参数,或者作为全局变量,在调用 work 之前仅分配一个:
use module_where_c_is_defined
allocate (c)
do i=1,10000000
call work(a,b)
end do
deallocate(c)
subroutine work(a,b)
use module_where_c_is_defined
if (.not.allocated(c)) then
stop 'c is not allocated'
endif
...
end
Run Code Online (Sandbox Code Playgroud)