Gra*_*Guy 8 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.0在Ubuntu 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++大约相同的32%更快gcc.clang但是,使用我可以看到c++代码实际上运行速度提高了大约19%.这是我的问题:
clang在这里做得这么好?llvm编译器有没有fortran前端?如果有,那个生成的代码会更快吗?更新:
使用-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上述选项不会显着改变结果
时间差异将与执行所需的时间有关pow,因为其他代码相对简单。您可以通过分析来检查这一点。那么问题是编译器如何计算幂函数?
我的计时:使用 编译的 Fortran 版本约为 1.20 秒gfortran -O3,使用 编译的 C++ 版本约为 1.07 秒g++ -O3 -ffast-math。请注意,这-ffast-math对于 来说并不重要gfortran,因为pow将从库中调用,但对于 来说却有很大的不同g++。
就我而言, for是被调用的gfortran函数(源代码)。它们的实现是计算整数幂的常用方法。另一方面,它是来自 libstdc++ 库的函数模板,但我不知道它是如何实现的。显然,它的编写/优化稍微好一些。考虑到它是一个模板,我不知道该函数在何种程度上是动态编译的。就其价值而言,使用编译的 Fortran 版本和使用优化标志编译的 C++ 版本都给出相同的计时,所以我猜它们使用相同的库函数。_gfortran_pow_c8_i4g++iforticc-fast
如果我只是用 Fortran 编写一个具有复杂算术的幂函数(显式地写出实部和虚部),它的速度与编译的 C++ 版本一样快g++(但随后-ffast-math会减慢速度,所以我坚持只-O3使用gfortran):
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每次迭代都乘以自身。
| 归档时间: | 
 | 
| 查看次数: | 1249 次 | 
| 最近记录: |