Подтвердить что ты не робот

С++ сверхбыстрая потокобезопасная функция 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() не вариант, потому что он еще хуже. Есть ли что-то, что я могу сделать, чтобы упорядочить это? Если это будет иметь значение, я думаю, что могу потерпеть потерю в статистической случайности. Будет ли предварительная генерация (перед исполнением) списка случайных чисел, а затем загрузка в потоковые стеки?

4b9b3361

Ответ 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++)