Fortran内置的计时例程,哪个更好?cpu_time还是system_clock?

37

当计时一个FORTRAN程序时,我通常只使用命令call cpu_time(t)。然后我偶然发现了call system_clock([count,count_rate,count_max]),它似乎也可以完成同样的任务,但是更加复杂。我的知识来源于:旧的英特尔文档。我在英特尔的主页上没有找到它。请参见下面的标记。

  1. 哪个更准确,或者它们相似吗?
  2. 它们中的一个会计算缓存未命中(或其他类似的东西),而另一个不会吗?或者它们中的任何一个都会计算?
  3. 还是下面标记的唯一区别?

这些是我的问题,下面我提供了一些代码,供您查看一些计时和用法。它们显示输出非常相似,因此在实现上似乎是相似的。我应该指出,我可能会一直使用cpu_time,我并不需要更精确的计时。

在下面的代码中,我尝试进行比较。(我还尝试了更复杂的事情,但为了保持简洁,将不提供)所以基本上我的结果是:

  • cpu_time
    1. 更易于使用,不需要初始化调用
    2. 直接计算差值时间
    3. 应该也是编译器特定的,但没有办法看到精度。(通常为毫秒)
    4. 是线程时间的总和。即不建议用于并行运行。
  • system_clock
    1. 需要预先初始化。
    2. 在处理后以除法形式表示。(虽然很小,但是仍然有区别)
    3. 是编译器特定的。在我的PC上发现以下内容:
      • Intel 12.0.4 使用10000的计数率,由于INTEGER精度。
      • gcc-4.4.5 使用1000,不知道如何区分
    4. 容易遇到wraparounds,即如果c1 > c2,由于count_max
    5. 是从一个标准时间开始计算的。因此,这将产生一个线程的实际时间,而不是总和。

代码:

PROGRAM timer
  IMPLICIT NONE
  REAL :: t1,t2,rate 
  INTEGER :: c1,c2,cr,cm,i,j,n,s
  INTEGER , PARAMETER :: x=20000,y=15000,runs=1000
  REAL :: array(x,y),a_diff,diff

  ! First initialize the system_clock
  CALL system_clock(count_rate=cr)
  CALL system_clock(count_max=cm)
  rate = REAL(cr)
  WRITE(*,*) "system_clock rate ",rate

  diff = 0.0
  a_diff = 0.0
  s = 0
  DO n = 1 , runs
     CALL CPU_TIME(t1)
     CALL SYSTEM_CLOCK(c1)
     FORALL(i = 1:x,j = 1:y)
        array(i,j) = REAL(i)*REAL(j) + 2
     END FORALL
     CALL CPU_TIME(t2)
     CALL SYSTEM_CLOCK(c2)
     array(1,1) = array(1,2)     
     IF ( (c2 - c1)/rate < (t2-t1) ) s = s + 1
     diff = (c2 - c1)/rate - (t2-t1) + diff
     a_diff = ABS((c2 - c1)/rate - (t2-t1)) + a_diff
  END DO

  WRITE(*,*) "system_clock : ",(c2 - c1)/rate
  WRITE(*,*) "cpu_time     : ",(t2-t1)
  WRITE(*,*) "sc < ct      : ",s,"of",runs
  WRITE(*,*) "mean diff    : ",diff/runs
  WRITE(*,*) "abs mean diff: ",a_diff/runs
END PROGRAM timer

为了完成,我在这里提供了我的Intel 12.0.4和gcc-4.4.5编译器的输出。

  • Intel 12.0.4 with -O0

    system_clock rate    10000.00    
    system_clock :    2.389600    
    cpu_time     :    2.384033    
    sc < ct      :            1 of        1000
    mean diff    :   4.2409324E-03
    abs mean diff:   4.2409897E-03
    
    real    42m5.340s
    user    41m48.869s
    sys 0m12.233s
    
  • gcc-4.4.5 with -O0

    system_clock rate    1000.0000    
    system_clock :    1.1849999    
    cpu_time     :    1.1840820    
    sc < ct      :          275 of        1000  
    mean diff    :   2.05709646E-03  
    abs mean diff:   2.71424348E-03  
    
    real    19m45.351s  
    user    19m42.954s  
    sys 0m0.348s  
    

感谢阅读...


3
在 gfortran 中使用 INTEGER(8) 来表示 SYSTEM_CLOCK 可以显著增加 count_max 的值,对于其他一些编译器也应该这样做。 - gauteh
4个回答

26

这两个内置函数报告不同类型的时间。system_clock报告“墙上时间”或经过的时间。cpu_time报告CPU使用的时间。在多任务机器上,这些可能非常不同,例如,如果进程与其他三个进程平均共享CPU,并因此获得25%的CPU并使用10 CPU秒,则需要约40秒的实际经过或挂钟时间。


正如我在问题中所写的那样,这也是我认为的主要区别。但是还有其他更微妙的区别吗? - nickpapior

5

在兼容英特尔的CPU上,cpu_time()通常的分辨率约为0.01秒。这意味着更小的时间间隔可能计为零时间。 目前大多数Linux编译器的system_clock()分辨率取决于参数的数据类型,因此整数(int64)将提供比1微秒更好的分辨率,并允许在显著的时间间隔内计数。 最近(2015年)更改了Windows版本的gfortran,使system_clock()等价于query_performance调用。然而,ifort Windows仍然显示system_clock的约0.01分辨率,即使omp_get_wtime被更改为使用query_performance。 我会忽略以前关于以时钟滴答数来测量cpu_time或system_clock分辨率的评论,特别是如果认为这可能与CPU或数据总线时钟滴答数有关,例如rdtsc指令可能会报告。


3

我发现 itime (见gfortran手册)是测量Fortran程序时间的很好的替代方法,与system_clock相比。它非常容易使用:

integer, dimension(3) :: time
call itime(time)
print *, 'Hour:  ', time(1)
print *, 'Minute:', time(2)
print *, 'Second:', time(3)

6
问题在于这是一种非标准的扩展。 - Vladimir F Героям слава
1
它可能是非标准的(GNU扩展),但被许多编译器支持。我测试过,发现它可以在ifort、pgf90、sunf90和g95上运行。 - Karl Yngve Lervåg
1
那并没有改变任何事情。而且,问题根本就没有要求这个。 - Vladimir F Героям слава
2
这是一个很好的观点。最好在注释中提到itime而不是在回答中提到它。 - Karl Yngve Lervåg

0

我发现secnds()是获取墙上时间最简单的方法。它的使用方式几乎与cpu_time()相同。

real(8)::t1,delta
t1=secnds(0.0)
!Do stuff
delta=seconds(t1)

1
这是所有编译器的内在函数吗? 那么与其他函数相比有什么区别? - nickpapior
1
似乎gfortran没有内置此函数。 - user2589273
我现在使用 system_clock - bdforbes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接