Performance 可分配阵列性能

Performance 可分配阵列性能,performance,memory-management,fortran,mpi,fortran-common-block,Performance,Memory Management,Fortran,Mpi,Fortran Common Block,有一个mpi版本的程序,它使用公共块来存储通过代码到处使用的数组。不幸的是,并没有办法以公共块大小声明数组,只有在运行时才能知道它的大小。因此,作为一种解决方法,我决定将该阵列移动到模块中,模块中接受可分配阵列。也就是说,公共块中的所有数组都消失了,而使用了ALLOCATE。所以,这是我在程序中唯一改变的东西。不幸的是,程序的性能非常糟糕(与公共块实现相比)。至于mpi设置,每个计算节点上都有一个mpi进程,每个mpi进程都有一个线程。 我在这里发现了这个问题,但不想(不明白:)如何将它应用到我

有一个mpi版本的程序,它使用公共块来存储通过代码到处使用的数组。不幸的是,并没有办法以公共块大小声明数组,只有在运行时才能知道它的大小。因此,作为一种解决方法,我决定将该阵列移动到模块中,模块中接受可分配阵列。也就是说,公共块中的所有数组都消失了,而使用了ALLOCATE。所以,这是我在程序中唯一改变的东西。不幸的是,程序的性能非常糟糕(与公共块实现相比)。至于mpi设置,每个计算节点上都有一个mpi进程,每个mpi进程都有一个线程。 我在这里发现了这个问题,但不想(不明白:)如何将它应用到我的案例中(每个进程都有一个线程)。谢谢你的帮助

下面是一个简单的示例,说明了我所说的内容(下面是一个伪代码):

“源文件”:

正如您可能看到的,ZEROSET()没有并行或MPI内容。文件1.INC,文件2,文件\u N.INC是数组\u 1、数组\u 2。。。数组在公共块中定义。诸如此类

REAL ARRAY_1
COMMON /ARRAY_1/ ARRAY_1(NX, NY, NZ)
其中,NX、NY、NZ是借助参数指令描述的定义良好的参数。 当我使用模块时,我只是销毁了所有公共块,所以FILE_I.INC看起来像

REAL, ALLOCATABLE:: ARRAY_I(:,:,:)
然后将上面的“INCLUDE‘FILE_I.INC’”语句改为“USE FILE_I”。实际上,当执行并行程序时,一个特定的进程不需要整个(NX,NY,NZ)域,所以我计算参数,然后分配数组_I(仅一次!)

子例程ZEROSET()使用公共块执行0.18秒,使用模块执行0.36秒(当运行时计算阵列的尺寸时)。因此,性能恶化了两倍


我希望现在一切都清楚了。非常感谢您的帮助。

在模块中使用可分配数组通常会影响性能,因为编译器不知道编译时的大小。使用以下代码的许多编译器将获得更好的性能:

   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   call Z(A,N)
   end subroutine

   subroutine Z(A,N)
   Integer N
   real A(N,N)
   do stuff here
   end
   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   do stuff here
   end subroutine
然后这个代码:

   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   call Z(A,N)
   end subroutine

   subroutine Z(A,N)
   Integer N
   real A(N,N)
   do stuff here
   end
   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   do stuff here
   end subroutine
编译器将知道数组是NxN,do循环超过N,并且能够利用这一事实(大多数代码在数组上是这样工作的)。此外,在“do stuff here”中调用任何子例程之后,编译器必须假设数组“A”可能已经改变了大小或移动了内存中的位置,然后重新检查。这就扼杀了优化

这会让你恢复大部分的表现


公共块也位于内存中的特定位置,这也允许进行优化。

对于使用数组的fortran性能,我可以想到以下原因:

  • 堆栈上的数组与堆上的数组相比,但我怀疑这可能会对性能产生巨大影响
  • 将数组传递给子例程,因为传递数组的最佳方式取决于数组,请参见上的本页

  • 实际上,我想,这里的问题是,结合堆栈与堆内存,确实是基于编译器优化的。根据您使用的编译器,它可能会执行一些更有效的内存消隐,对于固定内存块,它甚至不需要检查它在子例程中的范围和位置。因此,在固定大小的阵列中,几乎不会涉及任何开销。 这个程序经常被调用吗?或者你为什么关心这个0.18秒?
    如果它确实相关,那么最好的选择是完全去掉0设置,例如,分离第一个迭代循环并将其用于初始化,这样您就不必引入额外的内存访问,只需使用0进行初始化。但是它会复制一些代码…

    这与并行执行有什么关系?在串行执行中是否尚未看到性能下降?你多久分配一次?伪参数是否具有allocatable属性?您的问题不太可能与mpi相关。你在做共享内存还是分布式内存?您可能遇到了内存瓶颈,但如果一切都做对了,情况就不是这样了,因为代码的公共块版本可以正常工作。请包括如何分配和分配的示例代码。如果您只分配和取消分配一次阵列(分别是程序的开始和结束),您不应该看到性能下降。性能“糟糕”不是很定量,慢了多少?就像上面说的,请先在串行运行时验证你的问题。伙计们,我很确定这个问题与并行编程或MPI无关。这就是为什么我的问题的标题不包含MPI相关的单词。我只是想准确地描述一下项目的环境。这里有一个简单的例子,说明了我所说的(很抱歉,没有提前提供)P.S.找不到如何在评论中显示一段代码,所以我在第一篇博文中描述了这个例子链接断了,所以我提交了一个编辑,我相信是同一篇文章。如果我错了,请把它修好。