Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.12.06;
Скачать: [xml.tar.bz2];

Вниз

Вопрос о использовании Random в многопоточном приложении   Найти похожие ветки 

 
31512 ©   (2009-10-09 21:25) [0]

Здравствуйте, Уважаемые.
Меня интересует опыт использования функции Random в многопоточных приложениях при интенсивном её использовании.
Краткое описание проблемы.
Имеется статистическая модель, использующая метод Монте-Карло и реализованная на языке Delphi. При некоторых конфигурациях модели требуется (для получения хорошей статистики) колоссальное количество экспериментов. Речь идёт о миллиардах испытаний, что, разумеется, не обходится без солидных временных затрат. 3 млд. испытаний занимает в среднем 1 час 25 минут. Таких конфигураций нужно насчитать много. Однако, итоговые статистические матрицы обладают свойством аддитивности, что позволяет накапливать статистику частями. Т.е., если имеется двухпроцессорная машина с Xeon по 4 ядра, то запускаем 8 потоков по 375 млн. испытаний, а потом складываем результат и нормируем на общее число испытаний.
Эксперимент показал, что при таком подходе 3 млд. испытаний накапливается всего за полчаса. Однако расчёт, по полученным таким образом статданным, поплыл. Для одного объекта дал завышенный результат, для другого заниженный. И в общем физически ничем не обоснованный. Считая всё это в одном, главном, потоке приложения результат великолепный.
Тут я вспомним свою битву, когда переписывал модель на С++ для Linux. Копаясь в дебрях документации gcc обнаружил, что генератор случайных чисел в Linux очень навороченный. Там есть функции для получения случайных чисел в разных потоках отдельно (суффикс _r) и в "однопоточных" приложениях отдельно.

Всё тщательно проверено и грешить остаётся только на "проседание" Random при её интенсивном использовании из нескольких потоков.

Randomize вызывается 1 раз при старте программы.

Отсюда и возникают вопросы.
1. Есть ли подводные камни при использовании Random из нескольких потоков одновременно?
2. Нет ли в Windows API "потокобезопастного" Random?


 
KilkennyCat ©   (2009-10-09 21:39) [1]

Random можно считать генератором случайности с большой натяжкой. Для серъезных опытов используют внешние генераторы.


 
palva ©   (2009-10-09 21:40) [2]

1. Конечно есть. Обычно используется конгруэнтный метод, а он предполагает хранение предыдущего выработанного случайного числа. Если вам нужен потокобезопасный метод, то храните это число для каждого потока свое. То есть напишите свою функцию с использованием слова threadvar


 
DVM ©   (2009-10-09 21:44) [3]


> 2. Нет ли в Windows API "потокобезопастного" Random?

GetTickCount + QueryPerfomanceCounter + Загрузка ЦП + свободная память + Количество потоков + Трафик + Движения мыши + Нажатые клавиши + Количество окон и т.д. продолдать можно бесконечно. Берешь все вместе, к этому применяешь что-то вроде синуса и оборачиваешь критической секцией.


 
31512 ©   (2009-10-09 21:50) [4]


> KilkennyCat ©   (09.10.09 21:39) [1]

Сейчас экспериментирую с этим.

> palva ©   (09.10.09 21:40) [2]

threadvar тут не причём. У меня описан класс потока статистики и использую я не переменные, а поля класса.
Вопрос в другом. Если я использую Random во множестве потоков и каждый из потоков интенсивно использует это функцию, то не происходит ли того, то Random начинает выдавать в разных потоках одно и то же число. Именно так это описано в документации Linux.
Как я понял (если всё правильно понял), из своего маленького исследования, Random в Delphi использует CoCreateGUID. B если кванты времени между двумя вызовами Random очень малы, то он генерит число близкое к предыдущему.
Пытаюсь разобраться.


 
DVM ©   (2009-10-09 21:53) [5]


> Random в Delphi использует CoCreateGUID. B если кванты времени
> между двумя вызовами Random очень малы, то он генерит число
> близкое к предыдущему.

CoCreateGUID использует время потому что


 
31512 ©   (2009-10-09 22:00) [6]

С двумя потоками получилось 42 минуты на 3 млд. экспериментов.
8 потоков давали полчаса. Интересный результат.


 
palva ©   (2009-10-09 22:07) [7]


> не происходит ли того, то Random начинает выдавать в разных потоках одно и то же число

Теоретически это вполне возможно.

> threadvar тут не причём

threadvar один из способов решить проблему. У вас генератор внутри класса? Как я понимаю это в разы снижает производительность. Если для такой пустяковой функции, как выработка очередного, числа требуется обратиться к свойству, то это ухудшит производительность раза в два... Но это так, замечание в сторону. Вопрос в том как хранить предыдущее выработанное число свое для каждого потока. Если treadvar не подходит храните его не внутри функции, а вместе с переменными потока. У вас же есть такие переменные (всякие счетчики), которые свои для каждого потока. Вот вместе с ними и храните. И передавайте как дополнительный параметр функции random. Хотя на вашем месте я и функцию такую не писал бы, а прямо вставил формулу в код. Время бы сэкономил.


 
palva ©   (2009-10-09 22:11) [8]


> Random в Delphi использует CoCreateGUID

Если так (с удивлением об этом узнал), то откажитесь от Random Delphi, - сэкономите время. Есть много других отличных и простых генераторов.


 
palva ©   (2009-10-09 22:15) [9]


> Random в Delphi использует CoCreateGUID

Может, Randomize использует? Тогда другое дело.


 
31512 ©   (2009-10-09 22:57) [10]


> palva ©   (09.10.09 22:11) [8]

Скорее я чего-то неправильно понял.


 
TUser ©   (2009-10-09 23:01) [11]

Вообще, сомневаюсь, что проблема в рандоме. ОС переключает потоки, как ей взумеатся, то етсь если ситуация заывисит от порядка работы потоков, то результата должен быть б.м. случаен.

К каким общим ресурсам еще обращаются потоки?


 
31512 ©   (2009-10-09 23:07) [12]


> palva ©   (09.10.09 22:07) [7]

В внутри класса у меня нет генератора случайных чисел. Я использую Random.
В том-то всё и дело. Сейчас изучаю генераторы случайных чисел, которые смог найти в сети. Но все они так или иначе в итоге используют Random.
Или такой вот эксклюзив:

procedure TMaster.DoGenerate(Sender:TObject);
var i:Integer;
   g:array[0..4] of TGUID;
   b:array[0..63] of Byte absolute g;
   AList:TStrings;

 procedure FillRandBools;
 var j,k:Integer;
 begin
   with lbBool do
   begin
     Clear;
     AList:=Items;
   end;

   for j:=Low(b) to High(b) do
   for k:=0 to 7 do AList.Add(TrueFalse[b[j] and (1 shl k) > 0]);
 end;

 procedure FillRandInts;
 var j:Integer;
 begin
   with lbInt do
   begin
     Clear;
     AList:=Items;
   end;

   for j:=Low(b) to High(b) do AList.Add(IntToStr(b[j]));
 end;

begin
 for i:=Low(g) to High(g) do CreateGUID(g[i]);
 FillRandBools;
 FillRandInts;
end;


 
31512 ©   (2009-10-09 23:13) [13]


> TUser ©   (09.10.09 23:01) [11]

Потоки абсолютно независимы.
> то етсь если ситуация заывисит от порядка работы потоков

Никак не зависит. Это просто n независимых задач. По окончании работы потоков матрицы суммируются.
На двух потоках отклонение существенно меньше, чем на восьми.

Идеально только в одном потоке.


> результата должен быть б.м. случаен.


Результат совершенно не должен быть случаен, поскольку оцениваются математические ожидания физических величин.
Вспомни дифракцию электронов на решётке.


 
palva ©   (2009-10-09 23:25) [14]

Я посмотрел в System.pas, как всё это устроено.
Промежуточное значение хранится в переменной
RandSeed: longint
и хранится не потокобезопасно.
Вы можете хранить ее в своих переменных потока и перед обращением к Random войти в критическую секцию, переменную RandSeed записать из своей переменной, а после обращения снова сохранить у себя и выйти из критической секции. Так будет потокобезопасно. Но насколько это скажется на производительности - не знаю.

> Но все они так или иначе в итоге используют Random.

Это вы зря. Ищите сишную реализацию и переделывайте на паскале. А зачем вам это нужно? Вас что, не устраивает линейный конгруэнтный метод?


 
palva ©   (2009-10-09 23:29) [15]

Вот здесь интересные примеры:
http://www.gnu.org/software/gsl/manual/html_node/Random-number-generator-algorithms.html
Но найдете еще, если не будете ограничиваться паскалем.


 
31512 ©   (2009-10-09 23:43) [16]


> palva ©   (09.10.09 23:25) [14]

Ок. Вот что я сделал. Я перенёс всю логику в поток. Загнал туда же генератор случайных чисел из ALGLIB. В принципе это дало больший квант времени между обращениями к Random.
Результат: теперь 3 млд. на восьми ядрах считается не за полчаса, а за 14 минут.
Сходимость результатов:

1.45337418476940E+0002 (8 потоков)/1.45362162532453E+0002 (0 потоков) = 99.982977650385824621397117929928%

Твой подход спытаю непременно чуть позже.


 
palva ©   (2009-10-09 23:54) [17]


> Твой подход спытаю непременно чуть позже.

И не надо. Наверняка медленнее будет.


 
31512 ©   (2009-10-10 10:11) [18]


> palva ©   (09.10.09 23:54)
Это будет не наверняка, а точно медленнее, поскольку каждому потоку придётся ждать, когда любой другой потока не "освободит Random". Поэкспериментировать, однако, тоже полезно будет.



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2009.12.06;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.51 MB
Время: 0.008 c
1-1227792603
Дмитрий Белькевич
2008-11-27 16:30
2009.12.06
Приложение блокирует папку после копирования файлов в неё


13-1124001775
oslep
2005-08-14 10:42
2009.12.06
Множественный оператор SELECT для DataAdapter


2-1255701197
Артём
2009-10-16 17:53
2009.12.06
Динамические окна


2-1255533352
madmech
2009-10-14 19:15
2009.12.06
Как отстроить ширину и высоту у DBGrid


2-1255722657
AlexDan
2009-10-16 23:50
2009.12.06
игнорировать biMinimaze, biMaximize..





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский