将嵌套循环替换为n维

时间:2016-08-07 10:02:55

标签: iterator fortran

我有一个嵌套循环,应该迭代一个N-dim立方体(直线,方形,立方体......)

到目前为止,我使用嵌套循环执行此操作,例如。 对于2D:

do i = 1,Ni
   do j = 1,Nj
      ! do stuff with f(i,j)
   enddo
enddo

或3D:

do i = 1,Ni
   do j = 1,Nj
      do k = 1,Nk
      ! do stuff with f(i,j,k)
      enddo
   enddo
enddo

我想用一个适用于每个可能的N维案例的构造替换这些嵌套循环。我怎么能在Fortran中这样做?在C ++中,可以使用迭代器。在Fortran中有这样的东西吗?

我正在考虑写一个类,它的工作类似于机械的n维计数器(即如果我们每个维数计算3个):

0,0,0,0,0
1,0,0,0,0
2,0,0,0,0
0,1,0,0,0
1,1,0,0,0

与嵌套for循环相比,这会有点快吗?你们有更好的想法吗?

2 个答案:

答案 0 :(得分:1)

这可能是最简单的......

另外,您会注意到数组的索引顺序是相反的。 Fortran在左边最快,所以它与C相反。

当你调用它时,它通过检查模块的接口,使用哪一个与你自动发送给它的数组维度相匹配。

MODULE Stuff
IMPLICIT NONE
PUBLIC MY$Work
INTERFACE MY$Work
  MODULE PROCEDURE MY$Work1D, MY$Work2D, MY$Work3D
END INTERFACE
PRIVATE 

CONTAINS   

!~~~~~~~~~~
SUBROUTINE MY$Work1d(Array, F_of_Array)
REAL, DIMENSION(:), INTENT(IN   ) :: Array
REAL, DIMENSION(:), INTENT(INOUT) :: F_of_Array
INTEGER                           :: I

!DIR$ DO SIMD
do i = LBOUND(Array,1) ,UBOUND(Array,1)
  ! do stuff with f(i)
enddo

RETURN
END SUBROUTINE MY$Work1d

!~~~~~~~~~~
SUBROUTINE MY$Work2d(Array, F_of_Array)
REAL, DIMENSION(:,:), INTENT(IN   ) :: Array
REAL, DIMENSION(:,:), INTENT(INOUT) :: F_of_Array
INTEGER                             :: I,J

!DEC$ UNROLL_AND_JAM
 do j = LBOUND(Array,2) ,UBOUND(Array,2)
   do i = LBOUND(Array,1) ,UBOUND(Array,1)
      ! do stuff with f(i,j)
   enddo
enddo

RETURN
END SUBROUTINE MY$Work2d

!~~~~~~~~~~
SUBROUTINE MY$Work3d(Array, F_of_Array)
REAL, DIMENSION(:,:,:), INTENT(IN   ) :: Array
REAL, DIMENSION(:,:,:), INTENT(INOUT) :: F_of_Array
INTEGER                               :: I,J,K

!DEC$ UNROLL_AND_JAM
do k = LBOUND(Array,3) , UBOUND(Array,3)
   do j = LBOUND(Array,2) ,UBOUND(Array,2)
      do i = LBOUND(Array,1) ,UBOUND(Array,1)
      ! do stuff with f(i,j,k)
      enddo
   enddo
enddo

RETURN
END SUBROUTINE MY$Work3d(Array, F_of_Array)

END MODULE Stuff


PROGRAM XX
USE STUFF
...

所以你可以有一个3D数组并执行此操作

DO K = 1, nk
  CALL MY$WORK(Array(:,:,k), ResultArray(:,:,K) )
ENDDO

那将使用2D版本并在所有K-case上循环...

答案 1 :(得分:1)

我决定使用超级索引。我的尺寸大小限制为2的幂。这允许我使用bitshift操作和定期添加。希望这比面向对象的计数器更快。

  pure function supI(j, k, n) result(vec)
    implicit none

    integer*8, intent(in)         :: j
    integer*8                     :: copy
    integer*4, intent(in)         :: k, n
    integer*4                     :: vec(n), x, mask
    ! transforms super index j to a n-dimensional iteration,
    ! where each dimension has a range of 2^k

    ! mask to extract left most indicies
    mask = ISHFT(1, k) -1
    copy = j

    do x = 1,n
      ! extract current value using mask
      vec(x) = and(copy, mask)

      ! shift to next value
      copy = ISHFT(copy, -k)
    enddo
  end function supI

每个维度中包含4个的3d计数器将如下所示:

  do j = 0,64
    write(*,*) supI(j, 2, 3)
  enddo

编辑:与没有优化的循环速度相比,它并不坏:大约20%的时间,但如果我使用优化器,它比嵌套循环慢得多。