我有一个Fortran90程序(Packmol),直到用静态内存分配实现。
我将代码更改为使用动态分配,以便在开头分配所有数组。在某些例子中,我的性能损失为400%。
然后,我验证了即使数组的大小与我使用静态分配时的大小相同,问题仍然存在。也就是说,如果我更改分配
喜欢的东西
double precision :: x(1000)
double precision :: x(1000)
这足以导致性能下降。当然,当需要动态分配的所有数组(大约30个)完成时。
有没有办法以更有效的方式分配数组以降低性能损失?或者有人有不同的建议吗?
非常感谢。
编辑:不知何故,问题解决了。动态版本现在只比静态版本略慢,这是预期的。我真的不知道是什么导致了之前的重大放缓。
答案 0 :(得分:2)
这种性能损失可能有很多原因:
1)静态数组始终在BSS上分配(参见Where are static variables stored (in C/C++)?),而“已分配”数组可以在堆上或堆栈上分配。堆栈上的分配比堆上快得多。一个好的编译器可以生成在堆栈上尽可能多地分配的代码。
2)您可能在循环中分配/解除分配语句。每次内存分配都需要一些时间。一个好的编译器可以避免在每次分配时物理分配一些内存,而是重新使用已经解除分配的空间。
3)编译器在编译时使用静态数组知道维度,因此它会进行一些额外的优化。
4)如果您有多维数组,则无法在编译时计算元素的地址。例如,A(5,6,7)
的地址为5 + 6*n1 + 7*n1*n2
,其中n1
和n2
是A
:A(n1,n2,n3)
的维度。对于静态数组,编译器可以优化此部分。此外,如果维度n1,n2,...
是2的幂,而不是进行整数乘法,编译器将生成比特移位快3倍的位移。
3号)是最有可能的。您可以为数组留下一些静态数组,这些数组在编译时知道合理的上限,并且相对较小(大致为1000个元素),并且在常规调用的内部例程中执行的数量非常少。工作
根据经验,只能静态分配小型阵列:大多数1D阵列,一些小型2D阵列和微型3D阵列。将所有其余部分转换为动态分配,因为它们可能无法放入堆栈中。
如果你有一些频繁的分配/解除分配,因为你在这样的循环中调用子程序:
do i=1,10000000
call work(a,b)
end do
subroutine work(a,b)
...
allocate (c)
...
deallocate (c)
end
如果c
始终具有相同的维度,则可以将其作为子例程的参数,或者作为在调用工作之前仅分配一个的全局变量:
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