2015-07-05 3 views
2

Я пытаюсь найти наиболее эффективный способ генерации случайных чисел для моделирования MC, над которым я работаю. Я много читал о алгоритме двойной точности Mersenne Twister, и я хотел понять пару основных вещей, прежде чем двигаться дальше.О генерации случайных чисел dSFMT на C++

я составил и запустить тест предоставленный официальных файлов dSFMT и это лучший результат для моей системы:

C:\TDM-GCC-64\C++ Tests\dSFMT>test-sse2-M19937 -s 
consumed time for generating 100000000 randoms. 
ST BLOCK [0, 1) AVE: 115ms. 
ST BLOCK (0, 1] AVE: 108ms. 
ST BLOCK (0, 1) AVE: 106ms. 
ST BLOCK [1, 2) AVE: 77ms. 
ST SEQ [0, 1) 1 AVE: 174ms. 
ST SEQ [0, 1) 2 AVE: 207ms. 
total = 500014655.815776 
ST SEQ (0, 1] 1 AVE: 173ms. 
ST SEQ (0, 1] 2 AVE: 205ms. 
total = 500035344.184224 
ST SEQ (0, 1) 1 AVE: 209ms. 
ST SEQ (0, 1) 2 AVE: 247ms. 
total = 500014655.815776 
ST SEQ [1, 2) 1 AVE: 173ms. 
ST SEQ [1, 2) 2 AVE: 204ms. 
total = 1500064655.815183 

Мои вопросы:

  1. Почему генерации [1,2) быстрее, чем [0,1]?
  2. Почему генерация блоков быстрее, чем последовательная? Не следует ли выделять большой массив и удалить его и переписать на него медленнее?
  3. Если мне нужно сгенерировать цифры 1e12, какая была бы лучшая стратегия? Если вы делаете это в блоках, каков оптимальный размер массива?
+0

Возможно, вы захотите ознакомиться с http://www.pcg-random.org/ - будет намного быстрее, чем MT, с аналогичными функциями блока. –

+0

Спасибо за ссылку. Я попробовал шаги, предложенные на www.pcg-random.org/using-pcg-c.html, но по какой-то причине не быстрее, чем dSFMT для генерации поплавков. Мне, наверное, нужно еще кое-что прочитать, но предварительные тесты показывают разницу по порядку величины. – Esteban

+0

Действительно ?! Это сюрприз для меня. –

ответ

2

Номера внутри библиотеки генерируются из интервала [1,2). Другие диапазоны выражаются как функции сверху этого интервала.

интервал [1,2) Генератор "Базовый":

inline static double dsfmt_genrand_close1_open2(dsfmt_t *dsfmt) { 
    double r; 
    double *psfmt64 = &dsfmt->status[0].d[0]; 

    if (dsfmt->idx >= DSFMT_N64) { 
     dsfmt_gen_rand_all(dsfmt); 
     dsfmt->idx = 0; 
    } 
    r = psfmt64[dsfmt->idx++]; 
    return r; 
} 

интервал [0, 1):

inline static double dsfmt_genrand_close_open(dsfmt_t *dsfmt) { 
    return dsfmt_genrand_close1_open2(dsfmt) - 1.0; 
} 

поколения Блок может быть быстрее по многим причинам, в том числе кэш местности, менее вызовы функций, разворот цикла и т. д. На практике операции с блоками часто бывают быстрее, чем отдельные операции.

В данном конкретном случае, генерация блока также быстрее, так как числа генерируются пары (W128_T типа):

union W128_T { 
    __m128i si; 
    __m128d sd; 
    uint64_t u[2]; 
    uint32_t u32[4]; 
    double d[2]; 
}; 

Блок версия использует это свойство, и копирует оба номер из W128_T в результирующий массив. Последовательная версия использует только первое число и отбрасывает второе.

Что касается вашего третьего вопроса, используйте генерацию блоков, как оказалось на вашем компьютере быстрее. У вас есть 1e8 номеров на 100 мс, поэтому для 1e12 вам нужно около двадцати минут. Если это нормально для вас, то просто используйте размер блока NUM_RANDS, не должно быть большой разницы для любого разумного размера блока. В противном случае рассмотрим генерацию чисел из независимых генераторов в нескольких потоках.

Смежные вопросы