使用指针在Fortran

时间:2018-02-02 21:58:34

标签: fortran intel-fortran

问题是编写的代码允许使用相同的变量名称,该变量可以根据条件引用数组或函数。

更详细:我有real*8函数,需要4个整数索引。计算这种功能是非常昂贵的。返回此函数的值使用了数百万次。最明显的解决方案 - 计算一次并使用4维数组而不是函数。它节省了大量的时间。

但是当任务的大小增加时,就不可能将这样的数组存储在内存中。因此,为了与硬件无关,需要关闭存储器并使用功能。

我遇到的唯一一个解决方案就是将abstract interface与“虚函数”一起使用,除了将数组的值返回四个索引外,什么都不做。

首先,有必要定义抽象接口:

abstract interface
   real(kind=rglu) function integral(a,b,c,d)
   use glob, only: iglu,rglu
   integer(kind=iglu), intent(in) :: a,b,c,d
   end function integral
end interface
procedure (integral), pointer :: R=>null()

接下来,编写函数:

real(kind=rglu) function stored_int(a,b,c,d) result(ret)
implicit none
integer(kind=iglu), intent(in) :: a,b,c,d

ret=hR(a,b,c,d); return
end function stored_int

然后我们将以下列方式使用它:

       if (storeIntegrals) then
            do i = 1,N
            do j = 1,N
            do a = 1,N
            do b = 1,N
                hR(i,j,a,b)=my_integral(i,j,a,b)
            enddo
            enddo
            enddo
            enddo
            R=>stored_int
       else
            R=>my_integral
       endif

函数my_integral是我们需要用数组替换的。 不幸的是,这种方法表现得非常差。 使用ifort -O3 -fpp -openmp编译 在四个核心上,它产生的结果比同一个代码差两倍,但是有一个数组(没有虚函数)。

是否有其他变体可以解决这个问题?

2 个答案:

答案 0 :(得分:2)

One thing you could try is to declare integral, stored_int, and my_integral to be BIND(C) and have their 4 arguments passed by value. This might result in stored_int being invoked a little faster.

Failing this there are still some things that might work for you. You could try creating a user-defined type Tarray that contains hR as component R and type Tfun that contains my_integeral as component R. Then the syntax for accessing an element of hR would be identical to that for invoking function my_integral. You need only maintain one code base, which would be moved to an INCLUDE file. Then you can invoke one or the other via a generic name. Here is such an INCLUDE file:

! sub.i90
subroutine sub1(T1,k,mess)
   implicit none
   type(T) T1
   integer k
   character(*) mess
   write(*,'(a)') mess
   write(*,'(*(g0:1x))') T1%R(k)
end subroutine sub1

And the stuff needed to set up the generic machinery:

! funarray.f90
module funmod
   use ISO_FORTRAN_ENV, only: wp => REAL64
   implicit none
   private
   type, public :: Tfun
      contains
      procedure, NOPASS :: R => fun
   end type Tfun
   contains
      function fun(i) bind(C)
         integer, value :: i
         real(wp) fun
         fun = i ! or whatever
      end function fun
end module funmod

module arraymod
   use ISO_FORTRAN_ENV, only: wp => REAL64
   implicit none
   private
   integer, parameter :: N = 10
   type, public :: Tarray
      real(wp) R(N)
   end type Tarray
end module arraymod

module genfun
   use funmod, only: T => Tfun
   implicit none
   private
   public sub
   interface sub
      module procedure sub1
   end interface sub
   contains
include 'sub.i90'
end module genfun

module genarray
   use arraymod, only: T => Tarray
   implicit none
   private
   public sub
   interface sub
      module procedure sub1
   end interface sub
   contains
include 'sub.i90'
end module genarray

module gencombine
   use genfun
   use genarray
   implicit none
end module gencombine

program gentest
   use funmod
   use arraymod
   use gencombine
   implicit none
   type(Tfun) T1
   type(Tarray) T2
   integer i
   do i = 1, size(T2%R)
      T2%R(i) = T1%R(i)
   end do
   call sub(T1,3,'Invoked for function')
   call sub(T2,4,'Invoked for array')
end program gentest

Output with ifort:

Invoked for function
3.000000000000000
Invoked for array
4.000000000000000

答案 1 :(得分:1)

使用相同的指针指向函数和数组是完全不可能的。即使在C数据指针(void *)和函数指针也不同。即使在CPU的硬件中,数据和代码的地址也可以以不同的方式实现。

所以是的,您使用的包装器“虚拟”函数似乎是我可以看到使用相同指针的唯一方法。