【问题标题】:Puzzling performance difference between ifort and gfortranifort 和 gfortran 之间令人费解的性能差异
【发布时间】:2012-01-17 10:39:37
【问题描述】:

最近,我读到了一篇post on Stack Overflow 关于寻找完美平方整数的文章。由于我想玩这个,我写了以下小程序:

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 进行分析,Intel 编译的程序确实使用了更多的指令。

我的问题是为什么?有没有办法找出差异的确切来源?

编辑:

  • gfortran 4.6.2 版和 ifort 12.0.2 版
  • 时间是通过运行time ./a.out 获得的,是实际/用户时间(sys 总是几乎为 0)
  • 这是在 Linux x86_64 上,gfortran 和 ifort 都是 64 位版本
  • ifort 内联所有内容,gfortran 仅在 -O3 处,但后者的汇编代码比 ifort 更简单,ifort 大量使用 xmm 寄存器
  • 修复了代码行,在循环之前添加了NTOT=0,应该可以解决其他 gfortran 版本的问题

当复杂的IF 语句被删除时,gfortran 需要大约 4 倍的时间(10-11 秒)。这是意料之中的,因为该声明大约抛出了大约 75% 的数字,避免对它们执行 SQRT。另一方面,ifort 只使用了稍微多一点的时间。我的猜测是,当 ifort 尝试优化 IF 语句时出现问题。

EDIT2:

我尝试使用 ifort 版本 12.1.2.273,它的速度要快得多,所以看起来他们修复了这个问题。

【问题讨论】:

  • 这些是 wall 时间还是 CPU 时间?您可以为每个粘贴time <program> 的输出吗?这些是 32 位版本还是 64 位版本?
  • 你试过反汇编每个编译器发出的目标文件并比较它们吗?
  • @talonmies:不,我没有,因为我不太了解汇编。虽然运行通过valgrind --tool=callgrind --dump-instr=yes 也给出了汇编代码,但这确实很复杂(很多差异)并且取决于优化级别。
  • 您是否尝试过更激进的优化级别?他们可能是值得的。
  • 你确定你的程序是正确的吗?使用比 4.5 更新的 gfortran 版本,我得到了不同的答案。

标签: fortran


【解决方案1】:

您使用的是什么编译器版本? 有趣的是,它看起来像是从 11.1 到 12.0 的性能回归的情况——例如对我来说,11.1(ifort -fast square.f90)需要 3.96 秒,而 12.0(相同选项)需要 13.3 秒。 gfortran (4.6.1) (-O3) 仍然更快 (3.35s)。 我以前见过这种回归,虽然没有那么戏剧化。 顺便说一句,用

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

使用 ifort 12.0 使其运行速度提高一倍,但在 gfortran 和 ifort 11.1 中运行速度较慢。

看起来部分问题是 12.0 在尝试矢量化方面过于激进:添加

!DEC$ NOVECTOR

在 DO 循环之前(不更改代码中的任何其他内容)将运行时间缩短至 4.0 秒。

另外,附带的好处是:如果您有一个多核 CPU,请尝试在 ifort 命令行中添加 -parallel :)

【讨论】:

  • 查看编辑:使用 ifort 版本 12.1.2.273 它可以工作。此外,单独或在一行上编译和链接时存在差异,很奇怪。现在我有:带有或不带有 any 语句的 gfortran 3.1 s,以及带有 any 语句的 ifort 3.3 s 和 5.0 s。
  • 是的,我认为any 在 12.0 中让事情变得更快的原因是防止矢量化。我认为单独编译和单行编译之间的差异表明可能会发生类似指令缓存问题。此外,如果您使用单精度而不是双精度,整个事情会加快很多,而且我认为如果您修改函数以返回 1 或 0 并将结果相加而不是使用 if 语句(循环中的条件语句通常对性能不利)。
  • 感谢 cmets 尤其是添加提示,我会尝试的。至于双精度:我需要它来获得非常大整数的正确平方根。这只是对整数平方根的探索,想看看用其他方法能不能更快,结果发现sqrt指令是打不过的……
  • 好吧,结果证明使用整数 is_square 函数和循环中的简单加法,代码运行速度较慢,即 3.9s 而不是 3.1s :)
  • 我明白了。刚刚尝试过——对我来说这也发生了,但只有 gfortran(不是 ifort),并且只有当函数被声明为 integer(8) 时——使用机器默认整数,速度是相同的。不过没有任何改进——我想那里已经有很多跳跃了,所以再跳跃一次并不重要:)
猜你喜欢
  • 2015-05-13
  • 1970-01-01
  • 1970-01-01
  • 2014-08-31
  • 2020-08-09
  • 1970-01-01
  • 2010-11-21
  • 2012-05-26
  • 2013-09-02
相关资源
最近更新 更多