Следующий код приводит к очень разному времени для 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 ответ
Взгляните на godbolt
Компилятор gcc снес distr(gen);
!!!
.L27:
dec esi
je .L25
Это цикл for, который ничего не делает!
Компилятор на 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);
, чтобы что-то с ним сделать, что повлияет на результат кода, иначе компилятор может удалить этот код.
Самый простой способ исправить я
distr(gen);
Теперь, когда вы посмотрите на сборку, вы увидите, что clang
вызывает функцию std::generate_canonical<double, 53ul, std::mersenne_twister_engine< .... >>
, а gcc
только что поместил соответствующий код в строку.
Скорее всего, это различие вызвано разной организацией стандартной библиотеки. Clang использовал версию, встроенную в стандартную библиотеку, а в шаблоне gcc из файла заголовка для генерации кода в только что созданной сборке. Когда компилятор достигает внешнего кода из библиотеки, он не может сказать, что именно он делает, поэтому он не может оптимизировать этот код (поскольку некоторые побочные эффекты могут быть скрыты в библиотеке).
Похожие вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .