clang ++ / g ++ / gfortran之间的简单测试用例

时间:2013-05-19 21:18:53

标签: c++ gcc fortran llvm clang

我在scicomp上遇到了这个涉及计算总和的问题。在那里,您可以看到c++和类似的fortran实施。有趣的是,我看到fortran版本的速度提高了约32%。

我想,我不确定他们的结果,并试图重建这种情况。这是我跑的(非常轻微)不同的代码:

C ++

#include <iostream>
#include <complex>
#include <cmath>
#include <iomanip>

int main ()
{
    const double alpha = 1;
    std::cout.precision(16);

    std::complex<double> sum = 0;
    const std::complex<double> a = std::complex<double>(1,1)/std::sqrt(2.);
    for (unsigned int k=1; k<10000000; ++k)
    {
        sum += std::pow(a, k)*std::pow(k, -alpha);

        if (k % 1000000 == 0)
            std::cout << k << ' ' << sum << std::endl;
    }

    return 0;
}

FORTRAN

implicit none
integer, parameter :: dp = kind(0.d0)
complex(dp), parameter :: i_ = (0, 1)

real(dp) :: alpha = 1
complex(dp) :: s = 0
integer :: k
do k = 1, 10000000
    s = s + ((i_+1)/sqrt(2._dp))**k * k**(-alpha)
    if (modulo(k, 1000000) == 0) print *, k, s
end do
end

我在gcc 4.6.3计算机上使用clang 3.0Ubuntu 12.04 LTS使用-O3标记编译上述代码。这是我的时间:

time ./a.out

gfortran

real    0m1.538s
user    0m1.536s
sys     0m0.000s

克++

real    0m2.225s
user    0m2.228s
sys     0m0.000s

real    0m1.250s
user    0m1.244s
sys     0m0.004s

有趣的是,当使用fortran时,我还可以看到c++代码比gcc代码快大约相同的32%。但是,使用clang,我可以看到c++代码实际上运行速度提高了大约19%。以下是我的问题:

  1. 为什么g ++生成的代码比gfortran慢?既然它们来自同一个编译器系列,那么这意味着(这个)fortran代码可以简单地转换成更快的代码吗?这与Fortran vs c ++的情况一样吗?
  2. 为什么clang在这里做得很好? llvm编译器有没有fortran前端?如果有,那个生成的代码会更快吗?
  3. 更新

    使用-ffast-math -O3选项会生成以下结果:

    gfortran

    real    0m1.515s
    user    0m1.512s
    sys     0m0.000s
    

    克++

    real    0m1.478s
    user    0m1.476s
    sys     0m0.000s
    

    real    0m1.253s
    user    0m1.252s
    sys     0m0.000s
    

    Npw g++版本正在以gfortran的速度运行,而clang仍然比两者都快。将-fcx-fortran-rules添加到上述选项不会显着改变结果

2 个答案:

答案 0 :(得分:1)

我相信你的问题出在输出部分。众所周知,C ++流(std::cout)通常效率很低。虽然不同的编译器可以对此进行优化,但使用C printf函数而不是std::cout重写关键性能部分总是一个好主意。

答案 1 :(得分:1)

时间差异与执行pow所需的时间有关,因为其他代码相对简单。您可以通过分析来检查这一点。那么问题是编译器如何计算幂函数?

我的时间:使用gfortran -O3的Fortran版本为~1.20秒,使用g++ -O3 -ffast-math编译的C ++版本为1.07秒。请注意,-ffast-mathgfortran无关,因为pow将从库中调用,但它对g++产生巨大影响。

就我而言,对于gfortran,调用函数_gfortran_pow_c8_i4source code)。它们的实现是计算整数幂的常用方法。另一方面,使用g++,它是libstdc ++库中的函数模板,但我不知道它是如何实现的。显然,它的编写/优化稍好一些。我不知道函数在多大程度上是动态编译的,考虑到它是一个模板。对于它的价值,使用ifort编译的Fortran版本和使用icc编译的C ++版本(使用-fast优化标志)都提供相同的时序,所以我猜这些使用相同的库函数

如果我只是在Fortran中使用复杂的算法编写一个幂函数(明确地写出实部和虚部),那么它与使用g++编译的C ++版本一样快(但后来-ffast-math会减慢它的速度,所以我只坚持-O3gfortran}:

complex(8) function pow_c8_i4(a, k)
implicit none

integer, intent(in) :: k
complex(8), intent(in) :: a

real(8) :: Re_a, Im_a, Re_pow, Im_pow, tmp
integer :: i

Re_pow = 1.0_8
Im_pow = 0.0_8
Re_a = real(a)
Im_a = aimag(a)
i = k

do while (i.ne.0)
  if (iand(i,1).eq.1) then
    tmp = Re_pow
    Re_pow = Re_pow*Re_a-Im_pow*Im_a
    Im_pow = tmp   *Im_a+Im_pow*Re_a
  end if
  i = ishft(i,-1)
  tmp = Re_a
  Re_a = Re_a**2-Im_a**2
  Im_a = 2*tmp*Im_a
end do
pow_c8_i4 = cmplx(Re_pow,Im_pow,8)
end function

根据我的经验,在Fortran实现中使用显式的实部和虚部更快,尽管使用复杂类型当然非常方便。

最后注意事项:即使只是一个例子,每次迭代调用幂函数的方式都是非常低效的。相反,你当然应该在每次迭代时自己乘以a