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

时间:2011-12-01 16:38:14

标签: 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

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

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

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

2 个答案:

答案 0 :(得分:6)

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

do i = 1, 1000000000
  do j=1,10
    call Squared(5, res) 
  enddo
enddo

我只查看了案例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.

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

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

答案 1 :(得分:5)

我认为@ IRO-bot有正确的答案,但我想指出代码放置会影响时间,即使是相同的装配。

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

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

因此,由于指令缓存序列发生了变化,因此可以找到添加未执行代码会影响代码时序的实例。