ifort和gfortran之间令人费解的性能差异
最近,我读了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它的速度更快,所以看起来他们固定的。
你使用的是什么编译器版本?有趣的是,它看起来像是从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命令行:)
请参阅编辑:使用ifort版本12.1.2.273它工作。另外,单独编译或链接时出现差异,很奇怪。现在我有:gfortran 3.1 s,带或不带'any'声明,以及3.3声明的原声和5.0声明的'any'声明。 – steabert 2012-01-25 07:37:47
是的,我认为'any'的原因在12.0中使事情更快是通过防止矢量化。在单独编译和一行编译之间的区别我认为这种指令缓存问题可能正在发生。另外,如果你使用单精度而不是双精度,那么整个事情会加速很多,而且我认为如果你修改函数返回1或0并且增加结果而不是具有'if'语句(条件语句在循环中通常对性能不利)。 – laxxy 2012-01-25 17:12:11
也尝试-fp-speculation =关闭 – laxxy 2012-01-25 17:19:47
是那些墙倍或CPU时间?你能为每一个粘贴'time'的输出吗?这些32位版本还是64位版本? –
2012-01-17 10:49:58
您是否尝试过反汇编每个编译器发出的对象文件并对它们进行比较? – talonmies 2012-01-17 11:02:28
@talonmies:不,我没有,因为我真的不懂组装。尽管通过'valgrind --tool = callgrind --dump-instr = yes'运行也提供了汇编代码,但这真的很复杂(很多不同),并且取决于优化级别。 – steabert 2012-01-17 11:08:53