2013-05-19 6 views
8

私は合計を計算することを含むscicompでこの問題に遭遇しました。そこには、c++と同様のfortranの実装があります。興味深いことに、私はfortranバージョンが約32%速いことを見た。clang ++/g ++/gfortranの簡単なテストケース

私はその結果についてはわからないと思って、状況を再現しようとしました。私はすべてのUbuntu 12.04 LTSマシン上gcc 4.6.3clang 3.0を使用して上記のコードをコンパイル

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 

:ここで私は走った(非常にわずかに)異なるコードです-O3フラグ。ここに私のタイミングです:

time ./a.out 

のgfortran

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

G ++

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

打ち鳴らす

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

興味深いことに、gccを使用した場合、コードがc++よりも約32%早くなっています。しかし、clangを使用すると、c++コードが実際には約19%速く実行されることがわかります。ここに私の質問があります:

  1. なぜg ++生成コードがgfortranよりも遅いのですか?同じコンパイラファミリのものなので、このFortranコードは単純に高速なコードに変換できますか?これは一般的にfortran vs C++の場合ですか?
  2. clangはなぜここでうまくいくのですか? llvmコンパイラ用のfortranフロントエンドはありますか?もしあれば、それによって生成されたコードはさらに速くなりますか?

UPDATE:-ffast-math -O3オプションを使用

は、以下の結果を生成する:

のgfortran

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

G ++

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を追加しても、結果は大きく変わりません。

+5

コンパイルに使用するコンパイラオプションを指定してください。 gccの-phast-mathのようなオプションは、タイミングに大きな影響を与える可能性があります。 –

+0

@ NikolayViskov私が明示的に使用する唯一のフラグは、すべてのコンパイラで '-O3'です。 – GradGuy

+0

私のマシン上で:clang '0.62'(-ffast-math' 0.60')、g ++ 4.6 '1.23'(-ffast-math' 0.78')、g ++ 4.7 '1.19'(-ffast-math' 0.76 ') – leemes

答えて

1

あなたの問題は出力部分にあると思います。 C++ストリーム(std::cout)はしばしば非常に非効率的であることはよく知られている。さまざまなコンパイラでこれを最適化することができますが、std::coutの代わりにC printf関数を使用して、重要なパフォーマンスパーツを書き換えることをお勧めします。

+2

10枚の印刷物が〜1.0秒の範囲でパフォーマンスに影響するとは思わない。 – steabert

+0

あなたは正しいかもしれません。私は自分でそれをテストしなかったが、それは間違いなく常に見ているべきだと思う。 – varepsilon

1

powを実行するのにかかる時間との時間差は、他のコードが比較的単純であるため、時間差はpowの実行に関連します。これはプロファイリングで確認できます。それでは、問題は、コンパイラがパワー関数を計算することですか?

私のタイミング:gfortran -O3のFortranバージョンでは〜1.20秒、g++ -O3 -ffast-mathでコンパイルされたC++バージョンでは1.07秒です。 -ffast-mathgfortranには関係ありません。powはライブラリから呼び出されますが、g++では大きな違いがあります。

私の場合、gfortranの場合は、_gfortran_pow_c8_i4が呼び出されます(source code)。それらの実装は、整数のべき乗を計算する通常の方法です。一方、g++では、libstdC++ライブラリの関数テンプレートですが、その実装方法はわかりません。どうやら、それはやや優れていると書かれている/最適化可能です。関数がテンプレートであると考えて、その場で関数がどの程度コンパイルされているのかわかりません。それが価値があるのであれば、ifortでコンパイルされたFortranバージョンとiccでコンパイルされたC++バージョン(-fast最適化フラグを使用)は同じタイミングを与えますので、これらは同じライブラリ関数を使用すると思います。私は複雑な算術とFortranでの電力機能を記述する場合

、それはg++(が、その後-ffast-mathがそれを遅くしてコンパイルC++バージョンほど高速です(明示的に実部と虚部を書き出す)ので、私は唯一の-O3にこだわっgfortran)と:それは複雑な型を使用するためにコースの非常に便利ですallthough私の経験で

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を掛けるだけです。

関連する問題