对不同形状的数组使用单个伪参数

时间:2018-06-07 10:14:20

标签: fortran

假设我们有几个不同的连续数组

real :: a1(2),a2(2),a3(2)
real :: b1(2,2), b2(2,2),b3(2,2)
real :: c1(3,3,3), c2(3,3,3),c3(3,3,3)

和子程序

subroutine mtpy(x,y,z)
  real,contiguous, intent(in) :: x(:), y(:)
  real, intent(out) :: z(size(x))

  z=x*y
end subroutine mtpy

如何在以下一系列调用中使用mtpy:

call mtpy(a1,a2,a3)
call mtpy(b1,b2,b3)
call mtpy(c1,c2,c3)

显然,这会导致编译器错误,因为实际和伪参数的形状不匹配。在这些情况下,我曾经声明了几个特定的​​程序,每个程序处理一个特定的形状,然后使用一个接口包装所有这些程序。然而,这非常繁琐(想象一下,有大量的简单元素函数和纯过程将多维数组(最多三维)视为单维数组,然后为每个数组提供sub_1d,sub_2d,sub_3d等实例。尽管他们所有人都在做同样的工作)。

我想,一个部分解决方案是使用RESHAPE

call mtpy(reshape(b1,[4]),reshape(b2,[4]),bb)

但是,我能否确定编译器(我对gfortran和ifort主要感兴趣)不会开始创建1d临时值来保存重构的b1和b2数组?

现在,我也知道可以声明一个数组指针,例如

real, pointer, contiguous :: p1(:),p2(:),p3(:)

并进行以下指针赋值,例如

p1(1:size(c1))=>c1

但是,这种方法有一个缺点,我需要将原始数组声明为目标。这不会影响编译器能够执行的优化吗?

我认为另一个解决方案是使用假定大小的数组,但我注意到Metcalf等人称他们的用法“弃用”,并且我不确定对omptimisations的影响。

那么,是否有一种简单的方法可以将多维fortran数组视为单维数组(在子程序或函数中),而不会对该数组施加不必要的假设(如TARGET)?如果我可以使用RESHAPE而不用担心创建临时数据(我只处理连续数组)我会去那。有什么建议吗?

2 个答案:

答案 0 :(得分:3)

未来的Fortran 2018标准将提供假定等级数组(允许接收任何等级的数组)和select rank结构,这些结构很容易通过一个假定等级数组来解决这种情况(参见例如,从第16页开始的The new features of Fortran 2018,并且对于多个假定秩数组有更多困难。

假设大小的数组虽然不时尚或推荐,但它们是当前标准(Fortran 2008)以及下一个标准草案(Fortran 2018)的有效,非过时功能,因此可以根据需要使用它们。由于很多Fortran 77代码依赖于此,并且其中大部分已经存在了几十年,我希望它能在大多数编译器中得到显着优化。

但是,您不需要使用假定大小的数组,您可以使用显式形状数组(具有显式维度的数组),并且只要数组实际参数具有足够的元素,代码就是有效的,因为,根据2008年标准第12.5.2.11节第4段,

  

如果伪参数是显式形状或假定大小的数组,则表示元素序列并且对应于作为数组的伪参数的实际参数是与伪参数关联的序列。实际参数的等级和形状不需要与伪参数的等级和形状一致,但伪参数中的元素数量不应超过实际参数的元素序列中的元素数量。如果伪参数是假定大小,则伪参数中的元素数正好是元素序列中元素的数量。

所以你可以

call mtpy(a1,a2,a3,size(a3))
call mtpy(b1,b2,b3,size(b3))
call mtpy(c1,c2,c3,size(c3))
...
subroutine mtpy(x,y,z,n)
  integer, intent(in) :: n
  real, intent(in) :: x(n), y(n)
  real, intent(out) :: z(n)
  z=x*y
end subroutine mtpy

答案 1 :(得分:1)

因为即使是连续的情况,我也不确定reshape()是否构成临时数组,我尝试打印原始地址并通过c_loc()传递数组。然后,即使对于小的1维数组,gfortran-8和ifort-16中的reshape()似乎也创造了临时数(因为第一个元素的地址不同)。因此,假设即使对于简单的情况也会创建临时表似乎更安全(有关更多信息,请参阅下面的francescalus的评论。)

module test
    use iso_c_binding, only: c_loc
    implicit none

    interface linear
        module procedure linear_r2d, linear_r3d
    endinterface
contains

subroutine calc_ver1( a )  !! assumed-shape dummy array
    real, contiguous, target :: a(:)
    print *, "addr = ", c_loc( a(1) )
    print *, "vals = ", a
endsubroutine

subroutine calc_ver2( a, n )  !! explicit-shape dummy array
    integer      :: n
    real, target :: a( n )
    print *, "addr = ", c_loc( a(1) )
    print *, "vals = ", a
endsubroutine

function linear_r2d( a ) result( ptr )  !! returns a 1-d pointer from 2-d array
    real, contiguous, target  :: a(:,:)
    real, contiguous, pointer :: ptr(:)
    ptr( 1 : size(a) ) => a
endfunction

function linear_r3d( a ) result( ptr )  !! returns a 1-d pointer from 3-d array
    real, contiguous, target  :: a(:,:,:)
    real, contiguous, pointer :: ptr(:)
    ptr( 1 : size(a) ) => a
endfunction

endmodule

program main
    use test
    implicit none
    integer i
    real, target :: a(2), b(2,2), c(2,2,2)

    a = [1,2]
    b = reshape( [( 2*i, i=1,4 )], [2,2]   )
    c = reshape( [( 3*i, i=1,8 )], [2,2,2] )

    print *, "addr(a) = ", c_loc( a(1) )
    print *, "addr(b) = ", c_loc( b(1,1) )
    print *, "addr(c) = ", c_loc( c(1,1,1) )

    print *, "[ use assumed-shape dummy ]"
    call calc_ver1( a )
    ! call calc_ver1( b )  ! rank mismatch
    ! call calc_ver1( c )  ! rank mismatch

    print *, "--- with reshape() ---"
    call calc_ver1( reshape( b, [size(b)] ) )
    call calc_ver1( reshape( c, [size(c)] ) )

    print *, "--- with linear() ---"
    call calc_ver1( linear( b ) )
    call calc_ver1( linear( c ) )

    print *
    print *, "[ use explicit-shape dummy ]"
    call calc_ver2( a, size(a) )
    call calc_ver2( b, size(b) )
    call calc_ver2( c, size(c) )
end

Linux上ifort-16的结果:

 addr(a) =                7040528
 addr(b) =                7040544
 addr(c) =                7040560
 [ use assumed-shape dummy ]
 addr =                7040528
 vals =    1.000000       2.000000    
 --- with reshape() ---
 addr =        140736361693536
 vals =    2.000000       4.000000       6.000000       8.000000    
 addr =        140736361693560
 vals =    3.000000       6.000000       9.000000       12.00000       15.00000       18.00000       21.00000       24.00000  
 --- with linear() ---
 addr =                7040544
 vals =    2.000000       4.000000       6.000000       8.000000    
 addr =                7040560
 vals =    3.000000       6.000000       9.000000       12.00000       15.00000       18.00000       21.00000       24.00000    

 [ use explicit-shape dummy ]
 addr =                7040528
 vals =    1.000000       2.000000    
 addr =                7040544
 vals =    2.000000       4.000000       6.000000       8.000000    
 addr =                7040560
 vals =    3.000000       6.000000       9.000000       12.00000       15.00000       18.00000       21.00000       24.00000    

OSX10.11上gfortran-8的结果:

 addr(a) =       140734555734776
 addr(b) =       140734555734752
 addr(c) =       140734555734720
 [ use assumed-shape dummy ]
 addr =       140734555734776
 vals =    1.00000000       2.00000000    
 --- with reshape() ---
 addr =       140734555734672
 vals =    2.00000000       4.00000000       6.00000000       8.00000000    
 addr =       140734555733984
 vals =    3.00000000       6.00000000       9.00000000       12.0000000       15.0000000       18.0000000       21.0000000       24.0000000    
 --- with linear() ---
 addr =       140734555734752
 vals =    2.00000000       4.00000000       6.00000000       8.00000000    
 addr =       140734555734720
 vals =    3.00000000       6.00000000       9.00000000       12.0000000       15.0000000       18.0000000       21.0000000       24.0000000    

 [ use explicit-shape dummy ]
 addr =       140734555734776
 vals =    1.00000000       2.00000000    
 addr =       140734555734752
 vals =    2.00000000       4.00000000       6.00000000       8.00000000    
 addr =       140734555734720
 vals =    3.00000000       6.00000000       9.00000000       12.0000000       15.0000000       18.0000000       21.0000000       24.0000000

我还认为显式形状的虚拟数组根据情况很有用,而问题中的代码似乎就是这种情况。 (因为实际的参数是连续的,所以没有临时创建的数组。)如果不需要n中的size参数calc_ver2(),我们可以使用一个返回1-d数组指针的函数(参见linear()上面的calc_ver2(),但我认为考虑到target的简单性,这可能有点过头了......(顺便说一下,我在代码中的不同位置附加了c_loc(),这是只是因为let navigationController = mainStoryBoard.instantiateViewController(withIdentifier: "NavigationController") as! UINavigationController let viewController = mainStoryBoard.instantiateViewController(withIdentifier: "ParentViewController") as! ParentViewController navigationController.pushViewController(viewController, animated: true) self.present(navigationController, animated: true, completion: nil) 需要它。)