2014-04-23 24 views
22

Utilizzando il modulo casuale di C++ 11, ho riscontrato un calo di prestazioni dispari quando si utilizza std::mt19937 (versioni a 32 e 64 bit) in combinazione con uno uniform_real_distribution (mobile o doppio, non importa) . Rispetto ad una compilazione g ++, è più che un ordine di grandezza più lento!Riduzione delle prestazioni clan per generazione di numeri casuali C++ specifici

Il colpevole non è solo il generatore di mt, in quanto è veloce con uno uniform_int_distribution. E non è un difetto generale nel uniform_real_distribution dal momento che è veloce con altri generatori come default_random_engine. Solo quella combinazione specifica è stranamente lenta.

Non ho molta familiarità con gli elementi intrinseci, ma l'algoritmo di Mersenne Twister è definito più o meno rigorosamente, quindi una differenza nell'implementazione potrebbe non tenere conto di questa differenza, immagino? misurare Programma sta seguendo, ma qui sono i miei risultati per clang 3.4 e gcc 4.8.1 su una macchina a 64 bit linux:

gcc 4.8.1 
runtime_int_default: 185.6 
runtime_int_mt: 179.198 
runtime_int_mt_64: 175.195 
runtime_float_default: 45.375 
runtime_float_mt: 58.144 
runtime_float_mt_64: 94.188 

clang 3.4 
runtime_int_default: 215.096 
runtime_int_mt: 201.064 
runtime_int_mt_64: 199.836 
runtime_float_default: 55.143 
runtime_float_mt: 744.072 <--- this and 
runtime_float_mt_64: 783.293 <- this is slow 

programma per generare questo e provare voi stessi:

#include <iostream> 
#include <vector> 
#include <chrono> 
#include <random> 

template< typename T_rng, typename T_dist> 
double time_rngs(T_rng& rng, T_dist& dist, int n){ 
    std::vector< typename T_dist::result_type > vec(n, 0); 
    auto t1 = std::chrono::high_resolution_clock::now(); 
    for (int i = 0; i < n; ++i) 
     vec[i] = dist(rng); 
    auto t2 = std::chrono::high_resolution_clock::now(); 
    auto runtime = std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count()/1000.0; 
    auto sum = vec[0]; //access to avoid compiler skipping 
    return runtime; 
} 

int main(){ 
    const int n = 10000000; 
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); 
    std::default_random_engine rng_default(seed); 
    std::mt19937 rng_mt (seed); 
    std::mt19937_64 rng_mt_64 (seed); 
    std::uniform_int_distribution<int> dist_int(0,1000); 
    std::uniform_real_distribution<float> dist_float(0.0, 1.0); 

    // print max values 
    std::cout << "rng_default_random.max(): " << rng_default.max() << std::endl; 
    std::cout << "rng_mt.max(): " << rng_mt.max() << std::endl; 
    std::cout << "rng_mt_64.max(): " << rng_mt_64.max() << std::endl << std::endl; 

    std::cout << "runtime_int_default: " << time_rngs(rng_default, dist_int, n) << std::endl; 
    std::cout << "runtime_int_mt: " << time_rngs(rng_mt_64, dist_int, n) << std::endl; 
    std::cout << "runtime_int_mt_64: " << time_rngs(rng_mt_64, dist_int, n) << std::endl; 
    std::cout << "runtime_float_default: " << time_rngs(rng_default, dist_float, n) << std::endl; 
    std::cout << "runtime_float_mt: " << time_rngs(rng_mt, dist_float, n) << std::endl; 
    std::cout << "runtime_float_mt_64: " << time_rngs(rng_mt_64, dist_float, n) << std::endl; 
} 

compilare via clang++ -O3 -std=c++11 random.cpp o g ++ rispettivamente. Qualche idea?

modifica: Infine, Matthieu M. ha avuto una grande idea: il colpevole è l'inlining, o piuttosto una sua mancanza. Aumentando il limite di inlining clang eliminato la penalità delle prestazioni. Questo ha effettivamente risolto un certo numero di stranezze di performance che ho incontrato. Grazie, ho imparato qualcosa di nuovo.

+0

Forse vuoi profilare un po 'le cose (ad es. Con callgrind) e confrontare l'assemblatore generato ... – PlasmaHH

+3

Posso solo riprodurlo per il caso 'float_mt', non per' float_mt_64'. Ho usato il tuo codice con clang3.4 su Fedora 20 64-bit. –

+0

Stavo per pubblicare un bug report ma ho visto che l'hai già fatto, http://llvm.org/bugs/show_bug.cgi?id=19542 – pyCthon

risposta

4

Come già affermato nei commenti, il problema è causato dal fatto che gcc è più aggressivo di clang. Se facciamo clang linea molto aggressivo, l'effetto scompare:

Compilare il codice con g++ -O3 produce

runtime_int_default: 3000.32 
runtime_int_mt: 3112.11 
runtime_int_mt_64: 3069.48 
runtime_float_default: 859.14 
runtime_float_mt: 1027.05 
runtime_float_mt_64: 1777.48 

mentre clang++ -O3 -mllvm -inline-threshold=10000 rendimenti

runtime_int_default: 3623.89 
runtime_int_mt: 751.484 
runtime_int_mt_64: 751.132 
runtime_float_default: 1072.53 
runtime_float_mt: 968.967 
runtime_float_mt_64: 1781.34 

A quanto pare, clang ora fuori-inline GCC nella int_mt casi, ma tutti gli altri runtime sono ora nello stesso ordine di grandezza. Ho usato gcc 4.8.3 e clang 3.4 su Fedora 20 64 bit.