Моя цель - создать оболочку для единого реального дистрибутива Boost с Mersenne Twister, чтобы сделать ее доступной в библиотеке. Поэтому я создал базовый c класс, подобный следующему:
class mt19937
{
protected:
boost::random::mt19937 gen_;
boost::random::uniform_real_distribution<double> real_;
public:
mt19937(unsigned long s = 5489UL) : gen_(s), real_(0., 1.) {};
double get() { return real_(gen_); };
};
Запустив тест производительности, я обнаружил, что мой класс намного медленнее, чем прямой вызов объектов Boost. Фактически, следующий код, который выбирает 10 миллиардов чисел, занимает на моей машине 30 с:
constexpr unsigned long seed = 5489UL;
constexpr size_t iter = 100000;
double x = 0.;
boost::random::mt19937 gen(seed);
boost::random::uniform_real_distribution<double> real(0., 1.);
for (size_t i = 0; i < iter; ++i)
for (size_t j = 0; j < iter; ++j)
x = real(gen);
Класс mt19937
, описанный выше, со следующим кодом занимает около 70 с:
mt19937 stduniform(seed);
for (size_t i = 0; i < iter; ++i)
for (size_t j = 0; j < iter; ++j)
x = stduniform.get();
Глядя на ассемблер в Windows, в первом случае код, выполняемый для x = real(gen)
, выглядит следующим образом, что мне кажется просто вызовом boost::random::detail::generate_uniform_real
и присвоением x
:
00007FF6D14639F0 movzx r9d,byte ptr [r15]
00007FF6D14639F4 lea rcx,[gen]
00007FF6D14639F9 movaps xmm2,xmm7
00007FF6D14639FC movaps xmm1,xmm8
00007FF6D1463A00 call boost::random::detail::generate_uniform_real<boost::random::mersenne_twister_engine<unsigned int,32,624,397,31,2567483615,11,4294967295,7,2636928640,15,4022730752,18,1812433253>,double> (07FF6D146141Ah)
С помощью функции get()
я вижу следующие ошибки - кажется, что он выполняет некоторые операции с регистрами, которые я не могу объяснить, и скачок:
00007FF6D1463B61 movsd xmm3,mmword ptr [rbp+900h]
00007FF6D1463B69 lea rcx,[stduniform]
00007FF6D1463B6E movsd xmm4,mmword ptr [rbp+8F8h]
00007FF6D1463B76 movaps xmm2,xmm3
00007FF6D1463B79 mulsd xmm2,xmm6
00007FF6D1463B7D movaps xmm1,xmm4
00007FF6D1463B80 mulsd xmm1,xmm6
00007FF6D1463B84 movaps xmm0,xmm2
00007FF6D1463B87 subsd xmm0,xmm1
00007FF6D1463B8B comisd xmm0,xmm7
00007FF6D1463B8F jbe main+2F8h (07FF6D1463B98h)
00007FF6D1463B91 call boost::random::detail::generate_uniform_real<boost::random::mersenne_twister_engine<unsigned int,32,624,397,31,2567483615,11,4294967295,7,2636928640,15,4022730752,18,1812433253>,double> (07FF6D14615D7h)
00007FF6D1463B96 jmp main+307h (07FF6D1463BA7h)
00007FF6D1463B98 movzx r9d,byte ptr [rbx]
00007FF6D1463B9C movaps xmm2,xmm3
00007FF6D1463B9F movaps xmm1,xmm4
00007FF6D1463BA2 call boost::random::detail::generate_uniform_real<boost::random::mersenne_twister_engine<unsigned int,32,624,397,31,2567483615,11,4294967295,7,2636928640,15,4022730752,18,1812433253>,double> (07FF6D146141Ah)
Возможно ли, что вызов Функция (которая должна быть встроена), выполненная 10 миллиардов раз, может добавить эти накладные расходы? Есть ли у вас какие-либо предложения по поводу кода для повышения производительности?
Я работаю в среде Windows и использую компилятор vc14 VisualStudio2015 с Boost 1.7.1. Я наблюдал аналогичное поведение с gcc4.9 на компьютере Linux, где прямой вызов Boost занимает 30 секунд, а новому классу - 45 секунд.
Большое спасибо за ваше время.