Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A simple test case between clang++/g++/gfortran

I ran across this question on scicomp which involves computing a sum. There, you can see a c++ and a similar fortran implementation. Interestingly I saw the fortran version was faster by about 32%.

I thought, I was not sure about their result and tried to regenerate the situation. Here is the (very slightly) different codes I ran:

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

I compile the above codes using gcc 4.6.3 and clang 3.0 on a Ubuntu 12.04 LTS machine all with -O3 flag. Here's my timings:

time ./a.out

gfortran

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

g++

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

clang

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

Interestingly I can also see that the fortran code is faster than the c++ by about the same 32% when gcc is used. Using clang, however, I can see that the c++ code actually runs faster by about 19%. Here are my questions:

  1. Why is g++ generated code slower than the gfortran? Since they are from the same compiler family does this mean (this) fortran code can simply be translated into a faster code? Is this generally the case with fortran vs c++?
  2. Why is clang doing so well here? Is there a fortran front-end for llvm compiler? If there, will the code generated by that one be even faster?

UPDATE:

Using -ffast-math -O3 options generates the following results:

gfortran

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

g++

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

clang

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

Npw g++ version is running as fast gfortran and still clang is faster than both. Adding -fcx-fortran-rules to the above options does not significantly change the results

like image 614
mmirzadeh Avatar asked May 19 '13 21:05

mmirzadeh


1 Answers

The time differences will be related to the time it takes to execute pow, as the other code is relatively simple. You can check this by profiling. The question then is what the compiler does to compute the power function?

My timings: ~1.20 s for the Fortran version with gfortran -O3, and 1.07 s for the C++ version compiled with g++ -O3 -ffast-math. Note that -ffast-math doesn't matter for gfortran, as pow will be called from a library, but it makes a huge difference for g++.

In my case, for gfortran, it's the function _gfortran_pow_c8_i4 that gets called (source code). Their implementation is the usual way to compute integer powers. With g++ on the other hand, it's a function template from the libstdc++ library, but I don't know how that's implemented. Apparently, it's slightly better written/optimizable. I don't know to what extent the function is compiled on the fly, considering it's a template. For what it's worth, the Fortran version compiled with ifort and C++ version compiled with icc (using -fast optimization flag) both give the same timings, so I guess these use the same library functions.

If I just write a power function in Fortran with complex arithmetic (explicitely writing out real and imaginary parts), it's as fast as the C++ version compiled with g++ (but then -ffast-math slows it down, so I stuck to only -O3 with 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

In my experience, using explicit real and imaginary parts in Fortran implementations is faster, allthough it's very convenient of course to use the complex types.

Final note: even though it's just an example, the way to call the power function each iteration is extremely inefficient. Instead, you should of course just multiply a by itself each iteration.

like image 89
steabert Avatar answered Sep 20 '22 11:09

steabert