我在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++
代码比gcc
代码快大约相同的32%。但是,使用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
添加到上述选项不会显着改变结果
答案 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-math
与gfortran
无关,因为pow
将从库中调用,但它对g++
产生巨大影响。
就我而言,对于gfortran
,调用函数_gfortran_pow_c8_i4
(source code)。它们的实现是计算整数幂的常用方法。另一方面,使用g++
,它是libstdc ++库中的函数模板,但我不知道它是如何实现的。显然,它的编写/优化稍好一些。我不知道函数在多大程度上是动态编译的,考虑到它是一个模板。对于它的价值,使用ifort
编译的Fortran版本和使用icc
编译的C ++版本(使用-fast
优化标志)都提供相同的时序,所以我猜这些使用相同的库函数
如果我只是在Fortran中使用复杂的算法编写一个幂函数(明确地写出实部和虚部),那么它与使用g++
编译的C ++版本一样快(但后来-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
。