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

Моя цель - создать оболочку для единого реального дистрибутива 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 секунд.

Большое спасибо за ваше время.

1 Ответ

2 голосов
/ 19 февраля 2020

Вы отметили бит "некоторые операции с регистрами, которые я не могу объяснить":

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)  

, кажется, соответствует следующим строкам в generate_uniform_real:

T result = numerator / divisor * (max_value - min_value) + min_value;
if(result < max_value) return result;

Так что Похоже, что компилятор не может встроить аргументы min_value и max_value для этой функции.

Несмотря на то, что я не смог воспроизвести существенную разницу в производительности:

  • Apple Clang версия 11.0.0 (clang-1100.0.33.17)
  • Повышение 1.69.0_2
...