2012-01-17 86 views
2

最近,我讀了post on Stack Overflow關於找到完美正方形的整數。因爲我想玩這個,我寫了下面的小程序:ifort和gfortran之間令人費解的性能差異

PROGRAM PERFECT_SQUARE 
IMPLICIT NONE 
INTEGER*8 :: N, M, NTOT 
LOGICAL :: IS_SQUARE 

N=Z'D0B03602181' 
WRITE(*,*) IS_SQUARE(N) 

NTOT=0 
DO N=1,1000000000 
    IF (IS_SQUARE(N)) THEN 
    NTOT=NTOT+1 
    END IF 
END DO 
WRITE(*,*) NTOT ! should find 31622 squares 
END PROGRAM 

LOGICAL FUNCTION IS_SQUARE(N) 
IMPLICIT NONE 
INTEGER*8 :: N, M 

! check if negative 
IF (N.LT.0) THEN 
    IS_SQUARE=.FALSE. 
    RETURN 
END IF 

! check if ending 4 bits belong to (0,1,4,9) 
M=IAND(N,15) 
IF (.NOT.(M.EQ.0 .OR. M.EQ.1 .OR. M.EQ.4 .OR. M.EQ.9)) THEN 
    IS_SQUARE=.FALSE. 
    RETURN 
END IF 

! try to find the nearest integer to sqrt(n) 
M=DINT(SQRT(DBLE(N))) 
IF (M**2.NE.N) THEN 
    IS_SQUARE=.FALSE. 
    RETURN 
END IF 

IS_SQUARE=.TRUE. 
RETURN 
END FUNCTION 

gfortran -O2編譯,運行時間爲4.437秒,與-O3是2.657秒。然後我認爲用ifort -O2編譯可能會更快,因爲它可能有更快的SQRT函數,但是原來運行時間現在是9.026秒,並且與ifort -O3相同。我試圖用Valgrind來分析它,而英特爾編譯的程序的確使用了更多的指令。

我的問題是爲什麼?有沒有辦法找出差異究竟在哪裏?

EDITS:

  • gfortran版本4.6.2和ifort版本12.0.2
  • 倍從運行time ./a.out獲得,並且是真正的/用戶時間(SYS總是幾乎爲0)
  • 此在Linux x86_64上,gfortran和ifort都是64位版本
  • ifort內聯了所有內容,gfortran僅在-O3,但後者的彙編代碼比ifort更簡單,它使用xmm寄存器很多
  • 固定的代碼行,循環前加入NTOT=0,應該可以解決問題與其他gfortran版本

當複數IF聲明被刪除,gfortran大約需要4倍的時間(10-11秒)。這是預料之中的,因爲該聲明大約會拋出約75%的數字,從而避免對它們執行SQRT。另一方面,只能使用更多的時間。我的猜測是,當ifort嘗試優化IF聲明時出現問題。

EDIT2:

我試着用ifort版本12.1.2.273它的速度更快,所以看起來他們固定的。

+0

是那些牆倍或CPU時間?你能爲每一個粘貼'time '的輸出嗎?這些32位版本還是64位版本? – 2012-01-17 10:49:58

+0

您是否嘗試過反彙編每個編譯器發出的對象文件並對它們進行比較? – talonmies 2012-01-17 11:02:28

+0

@talonmies:不,我沒有,因爲我真的不懂組裝。儘管通過'valgrind --tool = callgrind --dump-instr = yes'運行也提供了彙編代碼,但這真的很複雜(很多不同),並且取決於優化級別。 – steabert 2012-01-17 11:08:53

回答

3

你使用的是什麼編譯器版本?有趣的是,它看起來像是從11.1到12.0的性能迴歸 - 例如對我來說,11.1(ifort -fast square.f90)需要3.96s,12.0(相同的選項)需要13.3s。 gfortran(4.6.1)(-O3)仍然更快(3.35s)。 我之前看到過這種迴歸,雖然沒有那麼戲劇化。 順便說一句,在gfortran和ifort 11.1

is_square = any(m == [0, 1, 4, 9]) 
if(.not. is_square) return 

使得它運行快兩倍ifort 12.0,但速度較慢替換if語句。

看起來是問題的一部分是,12.0是在試圖向量化的東西過於激進:添加

!DEC$ NOVECTOR 

DO循環前右(無需代碼改變任何東西)削減運行時間縮短到4.0秒。

此外,作爲一個附帶的好處:如果你有一個多核心CPU,嘗試添加-parallel到ifort命令行:)

+0

請參閱編輯:使用ifort版本12.1.2.273它工作。另外,單獨編譯或鏈接時出現差異,很奇怪。現在我有:gfortran 3.1 s,帶或不帶'any'聲明,以及3.3聲明的原聲和5.0聲明的'any'聲明。 – steabert 2012-01-25 07:37:47

+0

是的,我認爲'any'的原因在12.0中使事情更快是通過防止矢量化。在單獨編譯和一行編譯之間的區別我認爲這種指令緩存問題可能正在發生。另外,如果你使用單精度而不是雙精度,那麼整個事情會加速很多,而且我認爲如果你修改函數返回1或0並且增加結果而不是具有'if'語句(條件語句在循環中通常對性能不利)。 – laxxy 2012-01-25 17:12:11

+0

也嘗試-fp-speculation =關閉 – laxxy 2012-01-25 17:19:47

相關問題