我有一个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,其中n1n2A的尺寸: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/

10-11 22:13
查看更多