在Fortran中使用指针引用函数或数组

在Fortran中使用指针引用函数或数组,fortran,intel-fortran,Fortran,Intel Fortran,问题在于编写的代码允许使用相同名称的变量,这些变量可以根据条件引用数组或函数 更详细地说:我有一个real*8函数,它接受4个整数索引。这种函数的计算相当昂贵。返回此函数的值被使用数百万次。最明显的解决方案是计算一次,然后使用4维数组而不是函数。它节省了大量的时间 但是当任务的大小增加时,就不可能在内存中存储这样的数组。因此,要独立于硬件,需要关闭存储在内存中的功能 我想到的唯一解决方案是使用带有“虚拟函数”的抽象接口,该函数只返回数组的四个索引值 首先,需要定义抽象接口: abstract i

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

更详细地说:我有一个
real*8
函数,它接受4个整数索引。这种函数的计算相当昂贵。返回此函数的值被使用数百万次。最明显的解决方案是计算一次,然后使用4维数组而不是函数。它节省了大量的时间

但是当任务的大小增加时,就不可能在内存中存储这样的数组。因此,要独立于硬件,需要关闭存储在内存中的功能

我想到的唯一解决方案是使用带有“虚拟函数”的
抽象接口
,该函数只返回数组的四个索引值

首先,需要定义抽象接口:

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编译 在四个核上,它给出的结果比相同的代码差两倍,但有一个数组(没有虚函数)


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

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


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

您可以尝试的一件事是将
integral
storage\u int
my\u integral
声明为BIND(C),并按值传递它们的4个参数。这可能会导致调用
stored_int
的速度稍快

如果做不到这一点,还有一些事情可能对你有用。您可以尝试创建一个用户定义的类型
Tarray
,其中包含
hR
作为组件
R
,以及类型
Tfun
,其中包含
my\u integral
作为组件
R
。然后,访问
hR
元素的语法将与调用函数
my_integral
的语法相同。您只需要维护一个代码库,它将被移动到
INCLUDE
文件中。然后,您可以通过通用名称调用其中一个。下面是这样一个
INCLUDE
文件:

! 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
以及建立通用机械所需的材料:

! 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
带ifort的输出:

Invoked for function
3.000000000000000
Invoked for array
4.000000000000000

顺便说一句,在Fortran中把
return
放进每个函数中是没有意义的。我以前把
return
放进去,如果它不影响性能,我会继续写它。关于
bind(c)
,为什么必须用它作为传递值,如果
real(rglu)
integer(igul)呢
难道C不可互操作吗?@francescalus仅与
bind(C)
一起获得真正的类C传递值。也许在这种函数指针的情况下,当编译器不能执行通常的优化机制时,它会产生一些不同。通常不会。32位x86和64位x86调用约定之间的差异可能会有所不同。我不确定其他解决方案,我认为运行时切换是必要的。编译时在函数和数组之间切换很简单,不需要派生类型。@francescalus Intel Fortran某种程度上破坏了Fortran的传递值,除了
BIND(C)
的一些用法之外!DEC$属性值
。使用gfortran
BIND(C)
在该上下文中不必强制执行真正的传递值。伴随处理器可能是Fortran编译器本身,在这种情况下,数字类型的互操作性将没有问题。无论如何,如果有必要,可以设置
rglu=C_DOUBLE
igul=C_INT
igul=C_INT64_T
。@即使没有派生类型,VladimirF运行时切换也是可能的,我确实对发布后将其放在那里有些担心,但是为了保护我的方法,我可以设想派生类型可能导致更快代码的实例。如果数组
hR
(他们必须在这个站点上启用MathJax;没有它,注释看起来很原始)的形状在编译时就知道了,但是数组或函数是运行时决定的,那会怎么样?那么'hR'必须是可分配的,但是对于派生类型,编译器仍然可以硬连接到它的形状,以获得可能更高效的代码。我理解,没有这种可能性。这个问题的题目很有挑衅性。我只是想,除了我之外,每个人都有一些显而易见的解决办法。请问一下你到底想问什么。这是我们在这个网站上通常做的事情。