Используя случайный модуль C ++ 11, я столкнулся со странным падением производительности при использовании std::mt19937
(32- и 64-битные версии) в сочетании с uniform_real_distribution
(плавающий или двойной, не имеет значения). По сравнению с компиляцией g ++ она более чем на порядок медленнее!
Виновник не только в генераторе mt, так как он быстр с uniform_int_distribution
, И это не общий недостаток в uniform_real_distribution
так как это быстро с другими генераторами, такими как default_random_engine
, Просто эта конкретная комбинация странно медленная.
Я не очень знаком с внутренностями, но алгоритм Мерсенна Твистера более или менее строго определен, так что разница в реализации не может объяснить эту разницу, я думаю? Программа measure следующая, но вот мои результаты для clang 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 ++ соответственно. Есть идеи?
редактировать: наконец, у Матье М. была отличная идея: виновник вменяется, или, скорее, в его отсутствие. Увеличение предела наклона лямки устранило снижение производительности. Это фактически решило ряд проблем с производительностью, с которыми я столкнулся. Спасибо, я узнал что-то новое.
Как уже говорилось в комментариях, проблема вызвана тем, что gcc встроен более агрессивно, чем clang. Если мы сделаем clang inline очень агрессивным, эффект исчезнет:
Компиляция вашего кода с 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
Судя по всему, Clang теперь встраивает gcc в int_mt
случаи, но все другие среды выполнения теперь в том же порядке. Я использовал gcc 4.8.3 и clang 3.4 на Fedora 20 64 bit.