Подтвердить что ты не робот

Снижение производительности Clang для генерации случайных чисел С++

Используя случайный модуль С++ 11, я столкнулся с нечетным снижением производительности при использовании std::mt19937 (32 и 64-разрядных версий) в сочетании с uniform_real_distribution (float или double, не имеет значения). По сравнению с компилятором g++ он более чем на порядок медленнее!

Преступник - это не просто генератор mt, так как он быстро с uniform_int_distribution. И это не общий недостаток в uniform_real_distribution, так как это быстро с другими генераторами, такими как default_random_engine. Именно эта конкретная комбинация странно медленная.

Я не очень хорошо знаком с внутренними функциями, но алгоритм Mersenne Twister более или менее строго определен, поэтому разница в реализации не могла объяснить эту разницу, я думаю? меру Программа следующая, но вот мои результаты для 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++ соответственно. Любые идеи?

edit: Наконец, у Маттье М. была отличная идея: виновник вкратце или, скорее, его отсутствие. Увеличение предела привязанности clang исключило штраф за производительность. Это фактически решило ряд странностей производительности, с которыми я столкнулся. Спасибо, я узнал что-то новое.

4b9b3361

Ответ 1

Как уже отмечалось в комментариях, проблема связана с тем, что gcc становится более агрессивным, чем clang. Если мы сделаем clang inline очень агрессивно, эффект исчезнет:

Компиляция вашего кода с помощью g++ -O3 yields

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 out-inlines gcc в случаях int_mt, но все остальные временные ряды теперь находятся в одном порядке. Я использовал gcc 4.8.3 и clang 3.4 на Fedora 20 64 бит.