是否应使用 INTENT 关键字加快代码速度?
Should the use of the INTENT keyword speed the code up?
此问题基于对 post Fortran intent(inout) versus omitting intent 的回答,即用户 Vladimyr @Vladimyr 的回答。
他说“<...> Fortran 将数据复制到内存的连续部分,并将新地址传递给例程。返回后,数据将复制回其原始位置。通过指定 INTENT , 编译器可以知道跳过其中一个复制操作。"
我完全不知道这一点,我以为 Fortran 和 C 完全一样通过引用传递。
第一个问题是,为什么 Fortran 会这样做,这个选择背后的基本原理是什么?
作为第二点,我对这种行为进行了测试。如果我理解正确,使用 INTENT(IN)
将节省将数据复制回原始位置所花费的时间,因为编译器确定数据没有被更改。
我试过这段代码
function funco(inp) result(j)
!! integer, dimension(:), intent (in) :: inp
integer, dimension(:):: inp
integer, dimension(SIZE(inp)) :: j ! output
j = 0.0 !! clear whole vector
N = size(inp)
DO i = 1, N
j(i) = inp(i)
END DO
end function
program main
implicit none
interface
function funco(inp) result(j)
!! integer, dimension(:), intent (in) :: inp
integer, dimension(:) :: inp
integer, dimension(SIZE(inp)) :: j ! output
end function
end interface
integer, dimension(3000) :: inp , j
!! integer, dimension(3000) :: funco
integer :: cr, cm , c1, c2, m
real :: rate, t1, t2
! Initialize the system_clock
CALL system_clock(count_rate=cr)
CALL system_clock(count_max=cm)
CALL CPU_TIME(t1)
rate = REAL(cr)
WRITE(*,*) "system_clock rate ",rate
inp = 2
DO m = 1,1000000
j = funco(inp) + 1
END DO
CALL SYSTEM_CLOCK(c2)
CALL CPU_TIME(t2)
WRITE(*,*) "system_clock : ",(c2 - c1)/rate
WRITE(*,*) "cpu_time : ",(t2-t1)
end program
函数复制一个数组,在主体中重复多次
根据上面的说法,复制回数组所花费的时间应该以某种方式显示出来。
system_clock rate 1000.00000
system_clock : 2068.07910
cpu_time : 9.70935345
但无论是否使用 INTENT
,结果都几乎相同。
谁能分享一下这两点,为什么 Fortran 执行额外的复制(一开始看起来效率不高)而不是通过引用传递,并且确实 INTENT
节省了时间复制操作?
您所指的答案是关于传递某些特定类型的小节,而不是整个数组。在那种情况下,临时副本 可能 是必要的,具体取决于功能。您的函数使用并假定了形状数组,即使您非常努力地尝试也不需要临时数组。
作者(不是我)可能想到的例子是
module functions
implicit none
contains
function fun(a, n) result(res)
real :: res
! note the explicit shape !!!
integer, intent(in) :: n
real, intent(in) :: a(n, n)
integer :: i, j
do j = 1, n
do i = 1, n
res = res + a(i,j) *i + j
end do
end do
end function
end module
program main
use functions
implicit none
real, allocatable :: array(:,:)
real :: x, t1, t2
integer :: fulln
fulln = 400
allocate(array(1:fulln,1:fulln))
call random_number(array)
call cpu_time(t1)
x = fun(array(::2,::2),(fulln/2))
call cpu_time(t2)
print *,x
print *, t2-t1
end program
与 Gfortran 中的 intent(inout)
相比,此程序在 intent(in)
中要快一些(在 Intel 中不是那么快)。但是,使用假定的形状数组 a(:,:)
甚至更快。然后不执行复制。
当 运行 没有运行时检查时,我也在 gfortran 中得到一些奇怪的未初始化访问。我不明白为什么。
当然这是一个人为的例子,在生产程序中有真实的案例,其中制作数组副本然后 intent(in)
可以有所作为。
此问题基于对 post Fortran intent(inout) versus omitting intent 的回答,即用户 Vladimyr @Vladimyr 的回答。
他说“<...> Fortran 将数据复制到内存的连续部分,并将新地址传递给例程。返回后,数据将复制回其原始位置。通过指定 INTENT , 编译器可以知道跳过其中一个复制操作。"
我完全不知道这一点,我以为 Fortran 和 C 完全一样通过引用传递。 第一个问题是,为什么 Fortran 会这样做,这个选择背后的基本原理是什么?
作为第二点,我对这种行为进行了测试。如果我理解正确,使用 INTENT(IN)
将节省将数据复制回原始位置所花费的时间,因为编译器确定数据没有被更改。
我试过这段代码
function funco(inp) result(j)
!! integer, dimension(:), intent (in) :: inp
integer, dimension(:):: inp
integer, dimension(SIZE(inp)) :: j ! output
j = 0.0 !! clear whole vector
N = size(inp)
DO i = 1, N
j(i) = inp(i)
END DO
end function
program main
implicit none
interface
function funco(inp) result(j)
!! integer, dimension(:), intent (in) :: inp
integer, dimension(:) :: inp
integer, dimension(SIZE(inp)) :: j ! output
end function
end interface
integer, dimension(3000) :: inp , j
!! integer, dimension(3000) :: funco
integer :: cr, cm , c1, c2, m
real :: rate, t1, t2
! Initialize the system_clock
CALL system_clock(count_rate=cr)
CALL system_clock(count_max=cm)
CALL CPU_TIME(t1)
rate = REAL(cr)
WRITE(*,*) "system_clock rate ",rate
inp = 2
DO m = 1,1000000
j = funco(inp) + 1
END DO
CALL SYSTEM_CLOCK(c2)
CALL CPU_TIME(t2)
WRITE(*,*) "system_clock : ",(c2 - c1)/rate
WRITE(*,*) "cpu_time : ",(t2-t1)
end program
函数复制一个数组,在主体中重复多次
根据上面的说法,复制回数组所花费的时间应该以某种方式显示出来。
system_clock rate 1000.00000
system_clock : 2068.07910
cpu_time : 9.70935345
但无论是否使用 INTENT
,结果都几乎相同。
谁能分享一下这两点,为什么 Fortran 执行额外的复制(一开始看起来效率不高)而不是通过引用传递,并且确实 INTENT
节省了时间复制操作?
您所指的答案是关于传递某些特定类型的小节,而不是整个数组。在那种情况下,临时副本 可能 是必要的,具体取决于功能。您的函数使用并假定了形状数组,即使您非常努力地尝试也不需要临时数组。
作者(不是我)可能想到的例子是
module functions
implicit none
contains
function fun(a, n) result(res)
real :: res
! note the explicit shape !!!
integer, intent(in) :: n
real, intent(in) :: a(n, n)
integer :: i, j
do j = 1, n
do i = 1, n
res = res + a(i,j) *i + j
end do
end do
end function
end module
program main
use functions
implicit none
real, allocatable :: array(:,:)
real :: x, t1, t2
integer :: fulln
fulln = 400
allocate(array(1:fulln,1:fulln))
call random_number(array)
call cpu_time(t1)
x = fun(array(::2,::2),(fulln/2))
call cpu_time(t2)
print *,x
print *, t2-t1
end program
与 Gfortran 中的 intent(inout)
相比,此程序在 intent(in)
中要快一些(在 Intel 中不是那么快)。但是,使用假定的形状数组 a(:,:)
甚至更快。然后不执行复制。
当 运行 没有运行时检查时,我也在 gfortran 中得到一些奇怪的未初始化访问。我不明白为什么。
当然这是一个人为的例子,在生产程序中有真实的案例,其中制作数组副本然后 intent(in)
可以有所作为。