2013-05-19 11 views
8

Mi sono imbattuto in questa domanda su scicomp che comporta il calcolo di una somma. Lì, è possibile vedere un c++ e un'implementazione simile fortran. È interessante notare che la versione di Fortran è stata più veloce di circa il 32%.Un semplice test case tra clang ++/g ++/gfortran

Ho pensato, non ero sicuro del loro risultato e ho cercato di rigenerare la situazione. Ecco la (leggermente) diversi codici che ho incontrato:

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 

posso compilare codici di cui sopra utilizzando gcc 4.6.3 e clang 3.0 su una macchina Ubuntu 12.04 LTS tutto con flag -O3. Ecco i miei tempi:

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 

È interessante notare che il codice fortran è più veloce dello c++ di circa lo stesso 32% quando si utilizza gcc. Utilizzando clang, tuttavia, posso vedere che il codice c++ viene eseguito più velocemente di circa il 19%. Ecco le mie domande:

  1. Perché il codice generato g ++ è più lento di gfortran? Dal momento che provengono dalla stessa famiglia di compilatori, questo significa che il codice fortran può essere semplicemente tradotto in un codice più veloce? Questo è generalmente il caso di fortran vs C++?
  2. Perché lo clang funziona così bene qui? C'è un front-end fortran per il compilatore llvm? Se esiste, il codice generato da questo sarà ancora più veloce?

UPDATE:

Utilizzando -ffast-math -O3 opzioni genera i seguenti risultati:

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 

versione NPW g++ è in esecuzione più veloce gfortran ed ancora clang è più veloce di entrambe le cose. L'aggiunta di -fcx-fortran-rules alle opzioni di cui sopra non modifica in modo significativo i risultati

+5

Fornire le opzioni del compilatore utilizzate per la compilazione. Tali opzioni come -ffast-math per gcc possono influenzare in modo significativo i tempi. –

+0

@NikolayViskov l'unico flag che uso esplicitamente è '-O3' su tutti i compilatori. – GradGuy

+0

Sulla mia macchina: 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

risposta

1

Credo che il problema sia nella parte di uscita. È noto che gli stream C++ (std::cout) sono spesso molto inefficienti. Sebbene i diversi compilatori possano ottimizzarlo, è sempre una buona idea riscrivere le parti con prestazioni critiche utilizzando la funzione C printf anziché std::cout.

+2

Non penso che 10 stampe influenzeranno le prestazioni nell'intervallo ~ 1.0 s. – steabert

+0

Potrebbe aver ragione. Non l'ho provato da solo, ma questo è sicuramente il pensiero che si dovrebbe sempre guardare. – varepsilon

1

Le differenze di orario saranno correlate al tempo necessario per eseguire pow, in quanto l'altro codice è relativamente semplice. Puoi verificarlo con il profiling. La domanda quindi è che cosa fa il compilatore per calcolare la funzione di potenza?

I miei tempi: ~ 1.20 s per la versione Fortran con gfortran -O3 e 1,07 s per la versione C++ compilata con g++ -O3 -ffast-math. Notare che -ffast-math non ha importanza per gfortran, poiché pow verrà chiamato da una libreria, ma fa un'enorme differenza per g++.

Nel mio caso, per gfortran, viene chiamata la funzione _gfortran_pow_c8_i4 (source code). La loro implementazione è il solito modo per calcolare i poteri interi. Con g++ d'altra parte, è un modello di funzione dalla libreria libstdC++, ma non so come sia implementato. Apparentemente, è leggermente meglio scritto/ottimizzato. Non so fino a che punto la funzione sia compilata al volo, considerando che è un modello. Per quello che vale, la versione Fortran compilata con ifort e la versione C++ compilata con icc (utilizzando il flag di ottimizzazione -fast) restituiscono entrambi gli stessi tempi, quindi suppongo che utilizzino le stesse funzioni di libreria.

Se ho appena scritto una funzione di potenza in Fortran con l'aritmetica complessa (esplicitamente scrittura di parti reale e immaginaria), è veloce come il C++ versione compilata con g++ (ma poi -ffast-math lo rallenta, quindi ho attaccato solo -O3 con 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 

nella mia esperienza, utilizzando parti reale e immaginaria espliciti nelle implementazioni Fortran è più veloce, allthough è molto conveniente, naturalmente, di utilizzare i tipi complessi.

Nota finale: anche se è solo un esempio, il modo di chiamare la funzione di alimentazione ogni iterazione è estremamente inefficiente.Invece, dovresti ovviamente moltiplicare lo stesso a per ogni iterazione.

Problemi correlati