Производительность генератора случайных чисел зависит от платформы - PullRequest
1 голос
/ 06 ноября 2019

Я тестирую производительность генераторов случайных чисел в c ++ и натолкнулся на некоторые очень странные результаты, которые я не понимаю.

Я протестировал std :: rand vs std ::iform_real_distribution, который использует std:: minstd_rand.

Код для синхронизации std :: rand

auto start = std::chrono::high_resolution_clock::now();

for (int i = 0; i < 1000000; ++i)
    std::rand();

auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
std::cout << "Elapsed time: " << elapsed.count() * 1000 << " ms\n";

Код для синхронизации std ::iform_real_distribution с использованием std: minstd_rand

std::minstd_rand Mt(std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_real_distribution<float> Distribution(0, 1);

auto start = std::chrono::high_resolution_clock::now();

for (int i = 0; i < 1000000; ++i)
    Distribution(Mt);

auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
std::cout << "Elapsed time: " << elapsed.count() * 1000 << " ms\n";

При компиляции с Microsoft VisualStudio 2019, на Dell Latitude 7390 (I7-8650U 1,9 ГГц) я получаю следующие скорости:

std :: rand -> Истекшее время: 45,7106 мс std ::iform_real_distribution -> Истекшее время: 65,7437 мс

У меня включена оптимизация компилятора с дополнительной опцией командной строки -D__FMA __

Однако при компиляции с g ++ на MacBook Air на MacOS High Sierra (1.4Ghz i5) я получаю следующие скорости:

std :: rand -> Истекшее время: 9,4547 мс std ::iform_real_distribution -> Истекшее время: 7,9e-05 мс

с помощью команды терминала "g ++ prng.cpp -oprng -std = c ++ 17 -O3 "

Еще одна проблема заключалась в том, что на Mac, при тестировании скоростиiform_real_distribution, скорость менялась бы, если бы я делал / не печатал значение.

Так

std::minstd_rand Mt(std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_real_distribution<float> Distribution(0, 1);

float num;

auto start = std::chrono::high_resolution_clock::now();

for (int i = 0; i < 1000000; ++i)
    num = Distribution(Mt);

auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
std::cout << "Elapsed time: " << elapsed.count() * 1000 << " ms\n";
std::cout << num << '\n';

даст мне время 5,82409 мс

, тогда как без печати я получу 7,9e-05 мс. Обратите внимание, что печать влияет только на тест дляiform_real_distribution, мне не нужно это делатьдля std :: rand. Я также протестировал использование mersenne вместо которого не страдает от той же проблемы.

Изначально я думал, что это были оптимизации компилятора, в которых пропущен файлiform_real_distribution, когда он не был сохранен / напечатан, поскольку переменная не используется и, таким образом,может быть опущено, но тогда почему компилятор не делает то же самое для std :: rand, и почему эти случайные функции выполняются на Mac быстрее, чем Windows?

EDIT: для пояснения, mersenne ссылается на std ::mt19937_64 используется вместо std :: minstd_rand дляiform_real_distribution.

1 Ответ

4 голосов
/ 06 ноября 2019

Все дистрибутивы в стандартной библиотеке C ++ (включая uniform_real_distribution) используют алгоритм, определенный реализацией. (То же самое относится к std::rand, что относится к функции rand стандарта Си.) Таким образом, вполне естественно, что между этими дистрибутивами будут различия в производительности в различных реализациях стандартной библиотеки C ++. См. Также этот ответ .

Возможно, вы захотите попробовать проверить, есть ли различия в производительности в случайных механизмах C ++ (таких как std::minstd_rand и std::mt19937), которые указывают фиксированныйалгоритм в стандарте C ++. Для этого генерируйте случайное число в движке напрямую, а не через какой-либо дистрибутив C ++, такой как uniform_int_distribution или uniform_real_distribution.


. Сначала я думал, что это оптимизация компилятора, исключаяiform_real_distribution, когда она не была сохранена / напечатана, поскольку переменная не используется и, следовательно, может быть опущена, но тогда почему компилятор не делает то же самое для std :: rand [?]

Iпредположим, что компилятор может выполнить эту оптимизацию, поскольку на практике стандартная библиотека C ++ реализована в виде кода C ++, доступного для компилятора, чтобы компилятор мог выполнять определенные оптимизации для этого кода по мере необходимости. В отличие от std::rand, который реализован только как функция, реализация которой недоступна для компилятора, что ограничивает возможности оптимизации, которые может выполнять компилятор.

...