8

我在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;
}

正则

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%。以下是我的问题:

  1. 为什么 g++ 生成的代码比 gfortran 慢?由于它们来自同一个编译器系列,这是否意味着(这个)fortran 代码可以简单地翻译成更快的代码?fortran vs c++ 通常是这种情况吗?
  2. 为什么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

Npwg++版本的运行速度一样快gfortran,并且仍然clang比两者都快。添加-fcx-fortran-rules到上述选项不会显着改变结果

4

2 回答 2

1

我相信您的问题出在输出部分。众所周知,C++ 流 ( std::cout) 通常效率很低。虽然不同的编译器可能会对此进行优化,但使用 Cprintf函数而不是std::cout.

于 2013-05-22T08:15:39.283 回答
1

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

我的时间安排:Fortran 版本约 1.20 秒,gfortran -O3C++ 版本用 .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每次迭代时乘以自身。

于 2013-05-22T13:15:30.083 回答