С++ сверхбыстрая потокобезопасная функция rand
void NetClass::Modulate(vector <synapse> & synapses )
{
int size = synapses.size();
int split = 200 * 0.5;
for(int w=0; w < size; w++)
if(synapses[w].active)
synapses[w].rmod = ((rand_r(seedp) % 200 - split ) / 1000.0);
}
Функция rand_r(seedp)
серьезно расширяет мою программу. В частности, он замедляет меня на 3 раза при серийном запуске и 4.4X при работе на 16 ядрах. rand()
не вариант, потому что он еще хуже. Есть ли что-то, что я могу сделать, чтобы упорядочить это? Если это будет иметь значение, я думаю, что могу потерпеть потерю в статистической случайности. Будет ли предварительная генерация (перед исполнением) списка случайных чисел, а затем загрузка в потоковые стеки?
Ответы
Ответ 1
Проблема заключается в том, что переменная seedp
(и ее местоположение памяти) разделяется между несколькими потоками. Ядра процессора должны синхронизировать свои кеши каждый раз, когда они обращаются к этой постоянно изменяющейся стоимости, что затрудняет работу. Решение состоит в том, что все потоки работают со своим собственным seedp
, поэтому избегайте синхронизации кеша.
Ответ 2
Это зависит от того, насколько хороша статистическая случайность. Для высокого качества хороший выбор - Mersenne twister или его SIMD-вариант. Вы можете генерировать и буферировать большой блок псевдослучайных чисел за раз, и каждый поток может иметь свой собственный вектор состояния. Park-Miller-Carta PRNG чрезвычайно прост - эти ребята даже реализовал его как ядро CUDA.
Ответ 3
Marsaglia генератор xor-shift - это, вероятно, самый быстрый генератор "разумного качества", который вы можете использовать. У него совсем не такое же "качество", как MT19937 или WELL, но, честно говоря, эти различия - академические софисты.
Для всех реальных, практических применений нет заметной разницы, за исключением 1-2 порядков величины разницы в скорости выполнения и 3 порядка разницы в потреблении памяти.
Генератор xor-shift также естественно потокобезопасен (в том смысле, что он будет генерировать недетерминированные, псевдослучайные результаты, и он не будет сбой) без каких-либо особых, и он может быть тривиально выполненным в потоковом режиме в другом (в том смысле, что он будет генерировать независимые, детерминированные, псевдослучайные числа на потоке), имея один экземпляр для потока.
Это также можно было бы сделать потокобезопасными в еще одном смысле (генерировать детерминированную, псевдослучайную последовательность, передаваемую потокам по мере их поступления), используя атомный обмен по обмену, но я не думаю, что это очень полезно.
Единственными тремя заметными проблемами с генератором xor-shift являются:
- Это не k-распределенный до 623 измерений, но, честно говоря, кто это волнует. Я не могу думать более чем в четырех измерениях (и даже в том, что ложь!), И не может представить много приложений, где может иметь значение более 10 или 20 измерений. Это должно быть довольно эзотерическое моделирование.
- Это проходит больше всего, но не когда-либо педантичный статистический тест. Опять же, кого это волнует. Большинство людей используют случайный генератор, который даже не пропускает ни одного теста и никогда не замечает.
- Нулевое семя будет генерировать нулевую последовательность. Это тривиально фиксируется добавлением ненулевой константы к одному из временных (интересно, почему Марсалья никогда не думал об этом?). Сказав это, MT19937 также ведет себя крайне плохо, учитывая нулевое семя, и не восстанавливается почти так же.
Ответ 4
Посмотрите Boost
: http://www.boost.org/doc/libs/1_47_0/doc/html/boost_random.html
Он имеет несколько вариантов, которые различаются по сложности (= скорость) и случайности (длина цикла).
Если вам не нужна максимальная случайность, вы можете уйти с помощью простого Mersenne Twister.
Ответ 5
Вам абсолютно нужно иметь 1 общий случайный?
У меня была аналогичная спорная проблема некоторое время назад, лучшим решением для меня было создание нового класса Random (я работал на С#) для каждого потока. они все равно мертвы.
Если вы правильно засеваете их, чтобы убедиться, что вы не создаете повторяющиеся семена, вы должны быть в порядке. Тогда у вас не будет общего состояния, поэтому вам не нужно использовать функцию threadafe.
С уважением, GJ
Ответ 6
Может быть, вам не нужно называть его на каждой итерации? вы можете инициализировать массив предварительно рандомизированных элементов и последовательно использовать его...
Ответ 7
Я думаю, вы можете использовать OpenMP для параллельного использования следующим образом:
#pragma omp parallel
for(int w=0; w < size; w++)