【问题标题】:Why is the gcc math library so inefficient?为什么 gcc 数学库效率这么低?
【发布时间】:2012-12-14 09:06:54
【问题描述】:

当我将一些 fortran 代码移植到 c 时,令我惊讶的是,使用 ifort(intel fortran 编译器)编译的 fortran 程序和使用 gcc 编译的 c 程序之间的大部分执行时间差异来自三角函数的评估函数(sincos)。这让我感到惊讶,因为我曾经相信answer 解释的内容,正弦和余弦等函数是在微处理器内部的微码中实现的。

为了更明确地发现问题,我在 fortran 中做了一个小测试程序

program ftest
  implicit none
  real(8) :: x
  integer :: i
  x = 0d0
  do i = 1, 10000000
    x = cos (2d0 * x)
  end do
  write (*,*) x
end program ftest

intel Q6600 处理器和3.6.9-1-ARCH x86_64 Linux 我得到ifort version 12.1.0

$ ifort -o ftest ftest.f90 
$ time ./ftest
  -0.211417093282753     

real    0m0.280s
user    0m0.273s
sys     0m0.003s

gcc version 4.7.2 我明白了

$ gfortran -o ftest ftest.f90 
$ time ./ftest
  0.16184945593939115     

real    0m2.148s
user    0m2.090s
sys     0m0.003s

这几乎是 10 倍的差异!我仍然可以相信cos 的 gcc 实现是微处理器实现的包装器,其方式与英特尔实现中可能完成的方式类似吗?如果这是真的,瓶颈在哪里?

编辑

根据 cmets,启用的优化应该会提高性能。我的观点是优化不会影响库函数……这并不意味着我不在非平凡的程序中使用它们。但是,这里有两个额外的基准测试(现在在我的家用电脑上intel core2

$ gfortran -o ftest ftest.f90
$ time ./ftest
  0.16184945593939115     

real    0m2.993s
user    0m2.986s
sys     0m0.000s

$ gfortran -Ofast -march=native -o ftest ftest.f90
$ time ./ftest
  0.16184945593939115     

real    0m2.967s
user    0m2.960s
sys     0m0.003s

您(评论员)想到了哪些特定的优化?在这个特定示例中,编译器如何利用多核处理器,其中每次迭代都取决于前一次的结果?

编辑 2

Daniel Fisher 和 Ilmari Karonen 的基准测试让我认为问题可能与 gcc 的特定版本(4.7.2)有关,也可能与我正在使用的特定版本(Arch x86_64 Linux)有关我的电脑。所以我用debian x86_64 Linuxgcc version 4.4.5ifort version 12.1.0intel core i7盒子上重复了测试

$ gfortran -O3 -o ftest ftest.f90
$ time ./ftest
  0.16184945593939115     

real    0m0.272s
user    0m0.268s
sys     0m0.004s

$ ifort -O3 -o ftest ftest.f90
$ time ./ftest
  -0.211417093282753     

real    0m0.178s
user    0m0.176s
sys     0m0.004s

对我来说,这是一个非常可接受的性能差异,我永远不会问这个问题。看来我将不得不在 Arch Linux 论坛上询问这个问题。

不过,对整个故事的解释还是很受欢迎的。

【问题讨论】:

  • 为什么计算出来的值不一样?
  • 更接近x8,有8核的机器吗?自动矢量化也应该发挥作用,但你禁用了它。在不打开优化器的情况下对代码进行基准测试是在浪费您的时间。
  • +1:编译优化或离开。
  • 优化可能很重要的原因是 gcc 在优化时有时会用其他东西(其他库调用或内联代码)替换库调用。
  • @Digikata, ifort 不执行任何类型的循环展开。它非常字面地翻译循环并调用自己优化的cos 例程,该例程以某种方式设法执行比fcos 更快的余弦计算。粗略一看,反汇编的cos 例程看起来像使用列表参数缩减。 GCC 调用系统范围的数学库,它实现了类似的东西,但没有像 libimf 那样针对英特尔处理器进行优化。

标签: c performance gcc fortran archlinux


【解决方案1】:

这主要是由于数学库的差异。需要考虑的几点:

  • 是的,带有 x87 单元的 x86 处理器具有 fsin 和 fcos 指令。但是,它们是用微码实现的,没有什么特别的理由说明它们必须比纯软件实现更快。
  • GCC 没有自己的数学库,而是使用系统提供的数学库。在 Linux 上,这通常由 glibc 提供。
  • 32 位 x86 glibc 使用 fsin/fcos。
  • x86_64 glibc 使用使用 SSE2 单元的软件实现。很长一段时间,这比只使用 x87 指令的 32 位 glibc 版本慢 很多。但是,已经(最近)进行了改进,因此根据您使用的 glibc 版本,情况可能不再像以前那么糟糕了。
  • 英特尔编译器套件拥有非常快速的数学库 (libimf)。此外,它还包括矢量化超越数学函数,通常可以进一步加快这些函数的循环速度。

【讨论】:

  • fsinfcos 比 C 中的实现快,但它们是 fairly imprecise,因为它们基于错误的 pi 值(原文如此!)。
  • @fuz:这并不完全是一个 错误 值,而是“仅”80 位 long double 精度。 (64 位有效数,精确值的下两位数字恰好是0,因此是 66 位有效数)。 fsin 范围缩减使用 Pi 正确舍入为 80 位 long double。但是,是的,这在+-Pi 附近还不够好,它会导致灾难性的取消,但是使用软件做得更好需要更高的精度。 (例如double double 我猜)
  • 也相关:Calling fsincos instruction in LLVM slower than calling libc sin/cos functions? 显示了软件数学库比 fsincos 快而不是慢的情况。
猜你喜欢
  • 1970-01-01
  • 2013-12-18
  • 1970-01-01
  • 2010-10-01
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 2019-06-21
  • 2020-08-25
相关资源
最近更新 更多