是什么导致这个琐碎的fortran代码的运行时差异?

Ste*_*ini 2 performance fortran

我在这个琐碎的节目中发现了一个非常奇怪的效果

module Moo 
contains
   subroutine main()
      integer :: res 
      real :: start, finish
      integer :: i

      call cpu_time(start)

      do i = 1, 1000000000
         call Squared(5, res) 
      enddo
      call cpu_time(finish)

      print '("Time = ",f6.3," seconds.")',finish-start
   end subroutine

   subroutine Squared(v, res)
      integer, intent(in) :: v
      integer, intent(out) :: res 

      res = v*v 
   end subroutine 

!   subroutine main2()
!      integer :: res
!      real :: start, finish
!      integer :: i
!
!      call cpu_time(start)
!      
!      do i = 1, 1000000000
!         res = v*v
!      enddo
!      call cpu_time(finish)
!
!      print '("Time = ",f6.3," seconds.")',finish-start
!   end subroutine

end module
program foo 
   use Moo 
   call main()
!   call main2()
end program
Run Code Online (Sandbox Code Playgroud)

编译器是mac上的gfortran 4.6.2.如果我编译-O0并运行程序,时间是4.36秒.如果我取消注释子程序main2(),而不取消其调用,则时间平均变为4.15秒.如果我也取消注释,call main2()第一个时间变为3.80,第二个时间变为1.86(可理解,我没有函数调用).

我比较了在第二和第三种情况下生成的汇编程序(例程未注释;调用注释和未注释)并且它们完全相同,除了实际调用main2例程.

代码如何通过调用将来会发生的例程来提高性能,并且在结果代码中基本没有区别?

mil*_*cic 6

我注意到的第一件事是你的程序太短,无法进行适当的基准测试.你用多少次跑步来平均?什么是标准偏差?我在代码中添加了一个嵌套的do循环,使其更长:

do i = 1, 1000000000
  do j=1,10
    call Squared(5, res) 
  enddo
enddo
Run Code Online (Sandbox Code Playgroud)

我只查看了案例1和案例2(main2评论和未注释),因为案例3是不同的,与此比较无关.我希望在案例2中运行时略有增加,因为需要将更大的可执行文件加载到内存中,即使该部分未在程序中使用.

因此,对于三个编译器,我为第1和第2个案例进行了计时(每次运行3次):

pgf90 10.6-0 x86-64上的64位目标Linux -tp istanbul-64

英特尔(R)Fortran英特尔(R)64编译器XE,适用于运行在英特尔(R)64,版本12.0.2.137 Build 20110112上的应用程序

GNU Fortran(GCC)4.1.2 20080704(Red Hat 4.1.2-51)

在AMD Opteron(tm)处理器6134上

我的脚本输出是:

exp 1 with pgf90:
Time = 30.619 seconds.
Time = 30.620 seconds.
Time = 30.686 seconds.
exp 2 with pgf90:
Time = 30.606 seconds.
Time = 30.693 seconds.
Time = 30.635 seconds.
exp 1 with ifort:
Time = 77.412 seconds.
Time = 77.381 seconds.
Time = 77.395 seconds.
exp 2 with ifort:
Time = 77.834 seconds.
Time = 77.853 seconds.
Time = 77.825 seconds.
exp 1 with gfortran:
Time = 68.713 seconds.
Time = 68.659 seconds.
Time = 68.650 seconds.
exp 2 with gfortran:
Time = 71.923 seconds.
Time = 74.857 seconds.
Time = 72.126 seconds.
Run Code Online (Sandbox Code Playgroud)

请注意,案例1和案例2之间的时间差异对于gfortran而言最大,而对于pgf90而言则最小.

编辑:在Stefano Borini指出我忽略了这样一个事实,即只使用调用cpu_time对循环进行基准测试,可执行加载时间是不合适的.AShelley的回答提出了可能的原因.对于更长的运行时间,两种情况之间的差异变得最小.仍然 - 我观察到gfortran的情况有显着差异(见上文)


ASh*_*lly 5

我认为@ IRO-bot有正确的答案,但我想指出代码放置会影响时序,即使是相同的汇编.

我有两个在相同处理器上运行的嵌入式应用程 每个都有相同的手动编码程序例程,以提供尽可能紧密的忙循环(用于插入亚微秒延迟).我最近惊讶地发现,在一个应用程序中,循环占用了50%!比另一个更长.两者都生成了完全相同的组件.

事实证明,在一个可执行文件中,循环体的起始地址允许它完全落在处理器的唯一指令缓存行中.在较慢的一个上,相同的功能从一个地址开始,导致它跨越两行.需要额外的提取主导了这种紧密循环的时间.

因此,由于指令缓存序列的变化,有可能找到添加未执行代码会影响代码时序的实例.