1

我正在编写一个 Fortran 程序。该程序实现了一些数值方法。程序速度非常重要。我决定摆脱动态数组(是否加快程序速度?),并面临以下问题。我有 3d 数组(NXxNYxNZ = MAX 元素),我知道 MAX,但我不知道 NX/NY/NZ 比率。它可以是这样的:1x1xNZ 或像这样的 2xNYx1 等。我看到的解决方案 - 使用指针。简化的 2D 案例:

program ptrtest
   parameter ( MAX = 50 )    ! I know this number on compile time.
   integer :: NX,NY          ! I will find this numbers on run time.
   real, target :: a(MAX)    ! Static Array
   real, pointer :: b(:,:)
   a = 5
   read(*,*) NX, NY          ! I get NX, NY (NX*NY == MAX)
   b (1:NX, 1:NY) => a       ! I can use b(:,:) <- this is my goal.
end program ptrtest

这个例子有效,但我担心这样的更新会减慢我使用 5d 数组的实际程序。是否可以?

4

1 回答 1

3

你说程序速度很重要,所以老式的 Fortran 会给你最好的:

  • 分配一个大小为一维的数组MAX
  • pass 是子程序以及NX,NYNZ. 这将避免指针间接导致性能损失(与 C 不同,Fortran 标准假定子例程数组参数不重叠,请参阅这篇文章)。

例如:


program noptrtest
   implicit none
   integer, parameter :: MAX = 50     ! I know this number on compile time.
   integer :: NX,NY          ! I will find this numbers on run time.
   real    :: a(MAX)         ! Static Array

   read(*,*) NX, NY          ! I get NX, NY (NX*NY == MAX)

   if (NX*NY /= MAX) then
     stop 'NX*NY /= MAX'
   endif
   call run(a,NX,NY)
end program noptrtest


subroutine run(b,NX,NY)
  integer :: NX,NY
  real    :: b(NX,NY)
  integer :: i,j

  do j=1,NY
    do i=1,NX
      ! use b(i,j) here
    enddo
  enddo
end

如果性能真的很重要,这里还有一些其他有用的技巧:

  • 告诉你的编译器在 32 字节边界上对齐数组(使用 ifort,使用!DIR$ ATTRIBUTES ALIGN : 32指令
  • 对二维数组进行物理尺寸标注,使前导尺寸的大小是 32 字节的倍数。对于 real*4,您需要 8 个元素的倍数。
  • !DIR$ VECTOR ALIGNED使用指令告诉编译器每一列都正确对齐

例如:

program noptrtest
   implicit none
   integer, parameter :: real_kind = 4 
   integer, parameter :: MAX = 50               ! I know this number on compile time.
   integer, parameter :: MAXDIM = MAX*(32/real_kind)    ! Max possible dimension required
   integer            :: NX,NY, NX_MOD         ! I will find this numbers on run time.
   real(real_kind)    :: a(MAXDIM)             ! Static Array

   !DIR$ ATTRIBUTES ALIGN : 32 :: a

   read(*,*) NX, NY          ! I get NX, NY (NX*NY == MAX)

   if (NX*NY /= MAX) then
     stop 'NX*NY /= MAX'
   endif

   if (mod(NX,real_kind) == 0) then
     NX_MOD = NX
   else
     NX_MOD = ((NX/real_kind)+1)*real_kind
   endif

   call run(a,NX_MOD,NX,NY)
end program noptrtest


subroutine run(b,NX_MOD,NX,NY)
  integer :: NX_MOD,NX,NY
  real    :: b(NX_MOD,NY)
  integer :: i,j

  do j=1,NY
    !DIR$ VECTOR ALIGNED
    do i=1,NX
    ! use b(i,j) here
    enddo
  enddo
end

编辑

这种老式的 Fortran技巧避免了指针别名。

关于指针别名的参考:

于 2014-12-18T09:47:53.383 回答