2014-04-23 29 views
22

使用C++ 11的隨機模塊時,當使用std::mt19937(32位和64位版本)和uniform_real_distribution(float或double,無所謂)組合時, 。與g ++編譯相比,它的速度要慢一個數量級!特定C++隨機數生成的Clang性能下降

罪魁禍首不僅僅是mt發電機,因爲它的速度很快,uniform_int_distribution。它不是uniform_real_distribution中的一個普通缺陷,因爲其他生成器如default_random_engine的速度很快。只是這種特定的組合很古怪。

我對intrinsics不是很熟悉,但是Mersenne Twister算法或多或少有嚴格的定義,所以實現上的差異不能解釋我猜想的這種差異嗎?測量計劃是繼,但這裏是我對鐺3.4和gcc 4.8.1在64位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 

計劃產生這一點,並嘗試自己:

#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; 
} 

通過clang++ -O3 -std=c++11 random.cpp編譯或g ++。有任何想法嗎?

編輯:最後,Matthieu M.有一個好主意:罪魁禍首是內聯,或者說是缺乏。增加內聯限制消除了性能損失。這實際上解決了我遇到的一些性能怪異問題。謝謝,我學到了一些新東西。

+0

也許你想分析一些東西(例如用callgrind)並比較生成的彙編器... – PlasmaHH

+3

我只能爲'float_mt'情況而不是'float_mt_64'重現此事。我在Fedora 20 64位上使用clang3.4的代碼。 –

+0

打算髮佈一個錯誤報告,但我看到你已經做到了,http://llvm.org/bugs/show_bug.cgi?id=19542 – pyCthon

回答

4

正如評論中已經指出的那樣,問題是由於gcc內聯比clang更積極而引起的。如果我們鐺內聯非常積極,效果消失:

編譯與g++ -O3你的代碼產生

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 

clang++ -O3 -mllvm -inline-threshold=10000產量

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 

顯然,鐺現在在int_mt出內聯GCC但是所有其他運行時間現在都處於相同的數量級。我在Fedora 20 64位上使用了gcc 4.8.3和clang 3.4。