在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$属性值
。使用gfortranBIND(C)
在该上下文中不必强制执行真正的传递值。伴随处理器可能是Fortran编译器本身,在这种情况下,数字类型的互操作性将没有问题。无论如何,如果有必要,可以设置rglu=C_DOUBLE
和igul=C_INT
或igul=C_INT64_T
。@即使没有派生类型,VladimirF运行时切换也是可能的,我确实对发布后将其放在那里有些担心,但是为了保护我的方法,我可以设想派生类型可能导致更快代码的实例。如果数组hR
(他们必须在这个站点上启用MathJax;没有它,注释看起来很原始)的形状在编译时就知道了,但是数组或函数是运行时决定的,那会怎么样?那么'hR'必须是可分配的,但是对于派生类型,编译器仍然可以硬连接到它的形状,以获得可能更高效的代码。我理解,没有这种可能性。这个问题的题目很有挑衅性。我只是想,除了我之外,每个人都有一些显而易见的解决办法。请问一下你到底想问什么。这是我们在这个网站上通常做的事情。