我有一个Fortran90程序(Packmol),直到使用静态内存分配来实现。
我将代码更改为使用动态分配,以使所有数组都在开始时分配。在某些示例中,我的性能损失为400%。
然后,我验证了即使数组的大小与使用静态分配时的大小相同,问题仍然存在。也就是说,如果我从double precision :: x(1000)
像double precision, allocatable :: x(:)
allocate(x(1000))
这足以造成性能损失。当然,对所有需要动态分配的数组(大约30个)执行此操作时。
有没有一种方法可以以更有效的方式分配阵列以减少性能损失?还是有人有不同的建议?
非常感谢你。
编辑:以某种方式解决了问题。现在,动态版本仅比预期的静态版本慢一点。我真的不知道是什么原因导致了之前的主要放缓。
最佳答案
这种性能下降可能有很多原因:
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
始终具有相同的尺寸,则可以将其作为子例程的参数,或作为在调用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
关于arrays - 使用可分配数组的性能损失,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35901756/