Boost.random и потокобезопасность
От: prrt  
Дата: 15.01.18 22:00
Оценка: 3 (1)
Как известно, boost::random не является thread safe.
Тогда если глобально есть
boost::random::mt19937 gen(42);

И из разных потоков одновременно очень часто вызывать
uint32_t randVal = gen();


то что может произойти?

Я вижу 2 опасности:
1) Для одновременных вызовов всегда будут сгенерированы одинаковые значения;
2) Произойдет undefined behaviour и вызов либо "зависнет", либо вывалится с segmentation fault.

Моя задача — с максимальной производительностью получать псевдо-случайные числа от gen.min() до gen.max(), качество чисел не важно. Поэтому без нужны закрывать мьютексом не хотелось бы.
Отредактировано 15.01.2018 22:01 prrt . Предыдущая версия .
Re: Boost.random и потокобезопасность
От: watchmaker  
Дата: 15.01.18 22:20
Оценка:
Здравствуйте, prrt, Вы писали:

P>Моя задача — с максимальной производительностью получать псевдо-случайные числа от gen.min() до gen.max(), качество чисел не важно. Поэтому без нужны закрывать мьютексом не хотелось бы.


Что мешает просто иметь в каждом потоке свой генератор? Просто добавь ключевое слове thread_local:
thread_local boost::random::mt19937 gen;
Ну и по желанию инициализируй эти генераторы не константой, а комбинацией из времени и номера потока.
Re: Boost.random и потокобезопасность
От: MTD https://github.com/mtrempoltsev
Дата: 16.01.18 05:35
Оценка:
Здравствуйте, prrt, Вы писали:

P>Моя задача — с максимальной производительностью получать псевдо-случайные числа от gen.min() до gen.max()


Максимально быстро — иметь в каждом потоке свой генератор.
Re[2]: Boost.random и потокобезопасность
От: MTD https://github.com/mtrempoltsev
Дата: 16.01.18 05:37
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Просто добавь ключевое слове thread_local:


Это еще зачем? Просто создать новый объект уже не модно?
Re[2]: Boost.random и потокобезопасность
От: prrt  
Дата: 16.01.18 10:42
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Максимально быстро — иметь в каждом потоке свой генератор.


В каждом потоке свой генератор далеко не очевидно, как сделать. Потоки — это boost::thread из асинхронного http сервера boost::asio. Свой генератор для каждого Connection — это легко, но смысла в данном случае не имеет (большое число соединений, в т.ч. короткоживущих).
Re[3]: Boost.random и потокобезопасность
От: MTD https://github.com/mtrempoltsev
Дата: 16.01.18 10:54
Оценка:
Здравствуйте, prrt, Вы писали:

P>В каждом потоке свой генератор далеко не очевидно, как сделать.


А в чем сложность? Пишешь примерно так:

auto gen = RandomGenerator();


И используешь.

P>Потоки — это boost::thread из асинхронного http сервера boost::asio.


К чему тогда разговоры про максимально быстро? Делай как хочешь, генератор случайных чисел будет последнее что надо оптимизировать.

P>Свой генератор для каждого Connection — это легко, но смысла в данном случае не имеет (большое число соединений, в т.ч. короткоживущих).


И что? Если они короткоживущие, то любой конект убъет все твои оптимизации. Да и для keep-alive соединений генератор случайных чисел будет последнее что надо оптимизировать.

Понимаешь, точка синхронизации, даже неявная в виде, например, shared_ptr переданного по значению приводит к тому, что твои потоки и ядра процессора начинают простаивать. Перед тем как синхронизировать потоки надо 2 раза подумать, а надо ли?
Re[3]: Boost.random и потокобезопасность
От: watchmaker  
Дата: 16.01.18 11:01
Оценка: +2
Здравствуйте, MTD, Вы писали:

MTD>Здравствуйте, watchmaker, Вы писали:


W>>Просто добавь ключевое слове thread_local:


MTD>Это еще зачем? Просто создать новый объект уже не модно?


Добавить одно ключевое слово намного проще.

Но если хочешь самостоятельно управлять этими объектами, то, разумеется, можешь делать и так. Просто, судя по ответу автора в соседней ветке
Автор: prrt
Дата: 16.01.18
, это всё же чуть более сложное действие.
Re[2]: Boost.random и потокобезопасность
От: prrt  
Дата: 16.01.18 21:57
Оценка: :)
Здравствуйте, watchmaker, Вы писали:

W>Просто добавь ключевое слове thread_local:

W>
thread_local boost::random::mt19937 gen;


Да, это, похоже, то, что нужно. Только вот компилятор старый, thread_local не поддерживает. Поковырялся с boost::thread_specific_ptr, не вышло, или ошибки при компиляции, или на этапе выполнения.
В итоге сделал пока так:
Глобально
std::map<boost::thread::id, boost::random::mt19937> gens;

При создании потоков:
    for (std::size_t i = 0; i < thread_pool_size_; ++i)
    {
        boost::shared_ptr<boost::thread> thread(new boost::thread(
              boost::bind(&boost::asio::io_service::run, io_service_ptr)));
        gens[thread->get_id()] =  boost::random::mt19937(i);
    }

И сама генерация случайного числа в обработчике Asio:
uint rand = gens[boost::this_thread::get_id()]();
Re[3]: Boost.random и потокобезопасность
От: AleksandrN Россия  
Дата: 17.01.18 07:08
Оценка: :)
Здравствуйте, prrt, Вы писали:

P>В итоге сделал пока так:

P>Глобально
P>
P>std::map<boost::thread::id, boost::random::mt19937> gens;
P>

P>При создании потоков:
P>
P>    for (std::size_t i = 0; i < thread_pool_size_; ++i)
P>    {
P>        boost::shared_ptr<boost::thread> thread(new boost::thread(
P>              boost::bind(&boost::asio::io_service::run, io_service_ptr)));
P>        gens[thread->get_id()] =  boost::random::mt19937(i);
P>    }
P>

P>И сама генерация случайного числа в обработчике Asio:
P>
P>uint rand = gens[boost::this_thread::get_id()]();
P>


Если качество чисел не важно, но нужна потокобезопасность, не проще ли использовать rand_s под Windows или rand_r под UNIX?
Re[4]: Boost.random и потокобезопасность
От: prrt  
Дата: 17.01.18 08:19
Оценка:
Здравствуйте, AleksandrN, Вы писали:

AN>Если качество чисел не важно, но нужна потокобезопасность, не проще ли использовать rand_s под Windows или rand_r под UNIX?

Судя по ответу на этот вопрос — https://stackoverflow.com/questions/29465758/correct-way-to-use-rand-r-for-multithreaded-programs-in-c-c, для rand_r (у меня Linux) нужно делать seed из каждого потока. Т.е. всё-равно получается похожий огород...
Re[5]: Boost.random и потокобезопасность
От: AleksandrN Россия  
Дата: 17.01.18 11:22
Оценка:
Здравствуйте, prrt, Вы писали:

P>Здравствуйте, AleksandrN, Вы писали:


P>Судя по ответу на этот вопрос — https://stackoverflow.com/questions/29465758/correct-way-to-use-rand-r-for-multithreaded-programs-in-c-c, для rand_r (у меня Linux) нужно делать seed из каждого потока. Т.е. всё-равно получается похожий огород...


int myrnd()
{
    unsigned int init;
    init ^= pthread_self() ^ time( NULL );

    return rand_r( &init );
}
Re[5]: Boost.random и потокобезопасность
От: andyp  
Дата: 17.01.18 11:32
Оценка:
Здравствуйте, prrt, Вы писали:

P>Судя по ответу на этот вопрос — https://stackoverflow.com/questions/29465758/correct-way-to-use-rand-r-for-multithreaded-programs-in-c-c, для rand_r (у меня Linux) нужно делать seed из каждого потока. Т.е. всё-равно получается похожий огород...


rand_r здорово быстрее твистера Мерсенна, если тебе качество чисел не важно.
Re: Boost.random и потокобезопасность
От: andyp  
Дата: 17.01.18 11:41
Оценка:
Здравствуйте, prrt, Вы писали:

P>Моя задача — с максимальной производительностью получать псевдо-случайные числа от gen.min() до gen.max(), качество чисел не важно. Поэтому без нужны закрывать мьютексом не хотелось бы.


Делаешь пул из десятка генераторов. Сид каждого генератора инициализируешь его номером в пуле. Каждый генератор в пуле защищаешь мьютексом. В нитках используешь генератор под номером thread_id % 10. Так вероятность коллизий будет маленьклй.

PS 10 взято для примера и может быть любым.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.