падение производительности лягушатника при использованииiform_real_distribution - PullRequest
2 голосов
/ 08 октября 2019

Следующий код приводит к очень разному времени для g ++ и clang ++ при использовании uniform_real_distribution.

#include <iostream>
#include <sstream>
#include <fstream>

#include <chrono>
#include <random>


std::mt19937::result_type seed = 0;
std::mt19937 gen(seed);
// std::uniform_int_distribution<size_t> distr(0, 1);
std::uniform_real_distribution<double> distr(0.0,1.0);

int main()
{
    auto t_start = std::chrono::steady_clock::now();
    for (auto i = 1; i <= 1000000; ++i)
    {
        distr(gen);
    }
    auto t_end = std::chrono::steady_clock::now();
    std::cout << "elapsed time: " << std::chrono::duration_cast<std::chrono::nanoseconds>(t_end - t_start).count()  << " ns\n" << std::endl;

    return 0;
}

Скомпилировано со следующими командами:

clang++ -std=c++17 -O3 -flto -march=native -mllvm -inline-threshold=10000000 rng.cpp -o rng
g++ -std=c++17 -O3 -march=native rng.cpp -o rng

это приводит к следующемувремена:

clang:  272929774 ns

gcc:    12054635 ns

при использовании закомментированного дистрибутива время:

clang:  48155862 ns

gcc:    50226810 ns

Я нашел довольно старый вопрос, который решает ту же проблему, но ни одно из предложенных решенийв моем случае это сработало.

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

Кто-нибудь знает, что здесь происходит?

1 Ответ

2 голосов
/ 08 октября 2019

Взгляните на GodBolt

На мусоре компилятора gcc distr(gen); !!!

.L27:
        dec     esi
        je      .L25

Это для цикла, который ничего не делает!

На clang компилятор не был достаточно умен:

.LBB0_1:                                # =>This Inner Loop Header: Depth=1
        mov     edi, offset gen
        call    double std::generate_canonical<double, 53ul, std::mersenne_twister_engine<unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul> >(std::mersenne_twister_engine<unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>&)
        dec     ebx
        jne     .LBB0_1

И generate_canonical фактически был вызван.

В основном вы должны использовать результат distr(gen);, чтобы сделать с ним что-то, что будетповлиять на исход кода, в противном случае компилятор может удалить этот код.


Самый простой способ исправить i t - это собрать результаты distr(gen); и распечатать их.

Теперь, когда вы посмотрите на сборку, вы увидите, что clang вызывает функцию std::generate_canonical<double, 53ul, std::mersenne_twister_engine< .... >> и gcc только что поместили соответствующий код в строку.

Скорее всего, это различие вызвано различной организациейстандартная библиотека. Clang использовал версию, встроенную в стандартную библиотеку, и в шаблоне gcc из заголовочного файла был использован для генерации кода в только что созданной сборке. Когда компилятор достигает внешнего кода из библиотеки, он не может сказать, что именно он делает, поэтому он не может оптимизировать этот код (поскольку некоторые побочные эффекты могут быть скрыты в библиотеке).

...