![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Очередь потоков
DevilDevil © (2012-07-23 16:57:00)Здравствуйте уважаемые мастера
Существует задача, которую необходимо распараллелить на несколько процессоров. Для этого планируется создать очередь потоков, количество потоков = количеству ядер.
В цикле (основной поток):
- собираем некоторую инфо
- ждём первый освободившийся поток
- кидаем ему инфо и адрес куда он должен записать результат
Ну и соответственно когда всё инфо считанно:
- ждём обработки всех потоков
- удаляем все потоки
Что интересует
Вообще я иногда программирую потоки, но уверенного владения ими нет и нет уверенности, что я всё делаю правильно. Во-первых, интересует, как узнать количество ядер. Во-вторых, интересует, как грамотнее организовать эту очередь потоков. Ну и в-третьих, интересны толковые статьи по программировании потоков, чтобы как можно меньше было воды и как можно больше сути
DevilDevil (2012-07-27 16:41:00) [1]> EgorovAlex © (26.07.12 22:36) [30]
спасибо, посмотрю
EgorovAlex (2012-07-26 22:36:00) [2]Посмотри OmniThreadLibrary, там многие полезные возможности реализованы
han_malign (2012-07-26 09:59:00) [3]
> Я в свое время эксперементировал
- I/O Completion Ports...
- с указанием количества конкурентных потоков по числу ядер, и можно делать потоков сколько угодно(с разумным ограничением по памяти) - "лишние" разблокируются только если один из уже запущенных завершается или переходит в состояние ожидания(не связанное с целевым GetQueuedCompletionStatus)...
Rouse_ (2012-07-25 00:27:00) [4]
> Надо было по количеству ядер делать и железо бы целее было ;)
Тю, маэстро - что есть железо, зато каков был эффект :)))
Dennis I. Komarov (2012-07-24 22:22:00) [5]
> Дык собственно так оно обычно и делается :)
>
Ну дык,
> это наоборот по глупому.
Вот и результат :) Надо было по количеству ядер делать и железо бы целее было ;)
Андреевич (2012-07-24 21:07:00) [6]
> мне тут на одном форуме сказали, что оптимально - рабочих
> потоков создавать столько сколько ядер.
наделать по кол-ву ядер, но с привязкой не играться, самое простое
Rouse_ (2012-07-24 19:58:00) [7]
> Если вычисления действительно длительные можно собирать
> статистику о количестве вычисленных итераций в единицу времени
> и самостоятельно регулировать число потоков :)
Дык собственно так оно обычно и делается :)
Я в свое время эксперементировал: http://www.delphimaster.net/view/15-1178118027
Dennis I. Komarov (2012-07-24 16:39:00) [8]
> Все зависит от конкретной задачи и возможностей конкретной
> машины.
> Начиная с определенного количества нитей (потоков) производительность
> начинает падать.
> Т.е. грубо при одной нити мы имеем 100 вычислений в секунду,
> при четырех 400 при восьми всего 600, значит золотая серединка
> где-то почти рядом :)
Доверим это основному потоку. Если вычисления действительно длительные можно собирать статистику о количестве вычисленных итераций в единицу времени и самостоятельно регулировать число потоков :)
P.S. Где тут воробьи?
DevilDevil (2012-07-24 14:20:00) [9]> Rouse_ © (24.07.12 01:12) [21]
> http://rouse.drkb.ru/books/rihter.zip
Спасибо, то что нужно !
Сергей М. (2012-07-24 10:05:00) [10]
> нужно создавать потоки в "спящем" режиме
Ты имеешь ввиду suspended mode ?
Это лишь усложнит задачу.
Создавай в нормальном (running mode). Сразу после получения управления поток организует цикл, в теле которого он "вычерпывает" инф-цию из ассоциированной с ним APC-очереди или очереди сообщений, переходя в ожидании при пустой очереди в kernel mode, дабы не тратить бездарно кванты процессорного времени (SleepEx, MsgWaitForMultipleObjectsEx - для APC-очереди, Get/WaitMessage - для очереди сообщений)
Rouse_ (2012-07-24 01:12:00) [11]
> это наоборот по глупому. Для ресурсоёмких обсчётов не использовать
> всю мощность компьютера - глупо
Боюсь ты не внимательно прочитал мой пост за номером [12]
Никто тебя не лишит возможности использовать железо на всю его дурь :)
Тебе просто могут помешать сделать это нерациональным способом, с точки зрения системы.
По поводу глупости - спасибо, буду работать над собой.
Теперь по пунктам:
КС необходимо использовать при записи/чтении критических для модификации блоков данных. Если ты читаешь данные размером до 4 байт, то, как правило, такие операции атомарны, т.е. синхронизация не нужна (с оговоркой - при чтении).
В частности в этом вот моменте:
> Вообще можно ли в каких-то случаях обойтись без критической
> секции, синхронизировать как то boolean-ом ? Может ли произойти
> неправильное чтение из кэша. Например если 2 потока на разных
> ядрах обращаются к одному участку памяти. В первом ядре
> я пишу в глобальную переменную значение (и значение сидит
> в кэше этого ядра), а на другом ядре я читаю из этой ячейки
> (и читаю уже из кэша своего ядра).
тут немного сумбурно.
любой блок данных до 4 байт (допустим в нем будет булевый параметр) может быть атомарно изменен при помощи Interlocked функций.
По поводу КЭШ-а не ясно что имеется ввиду, TLB? Ну тут вообще не наша епархия.
Локальный стэк нити? Если да, то нет - разные адреса.
Если TLS - то опять-же разные адреса.
Если Heap - то тут уже все зависит от реализации кода.
> В общем проясните пожалуйста эти азы. Или дайте толковую
> статейку
Тут я думаю нужно начать с Рихтера.
http://rouse.drkb.ru/books/rihter.zip
DevilDevil (2012-07-24 00:41:00) [12]> Rouse_ © (23.07.12 22:57) [18]
> По умному - не учитывать количество ядер и делать все по
> классике.
это наоборот по глупому. Для ресурсоёмких обсчётов не использовать всю мощность компьютера - глупо
> Юрий Зотов © (23.07.12 23:39) [19]
давайте опишу типичные ситуации. Скажите как реализовать
- нужно создавать потоки в "спящем" режиме. По идее suspended, но не знаю, насколько это корректно
- т.е. изначально количество свободных потоков = N. Необходимо иметь свойство "Количество свободных потоков"
- если в очереди свободных потоков, то дождатьсяпервого свободного
- изъять первый свободный поток
- "запустить"поток ?
- когда поток отработал - он должен сам себя пометить как освободившийся и засунуть себя в очередь. приостановить?
- дождатьсяпока число свободных потоков не равно N
Вот эти моменты я не знаю точно как правильно реализовать.
Не совсем понимаю как правильно использовать критическую секцию. Ей обязательно перекрывать функцию, или можно обойтись без неё, если используется простое обращение к полю:
property Count: integer read FCount;
Вообще можно ли в каких-то случаях обойтись без критической секции, синхронизировать как то boolean-ом ? Может ли произойти неправильное чтение из кэша. Например если 2 потока на разных ядрах обращаются к одному участку памяти. В первом ядре я пишу в глобальную переменную значение (и значение сидит в кэше этого ядра), а на другом ядре я читаю из этой ячейки (и читаю уже из кэша своего ядра).
Не совсем понимаю, зачем нужны другие синхронищирующие сущности кроме критической секции. И не понимаю как правильно организовать простои и ожидания для потоков. Через while (условие) do; Или через suspend. Или может через sleep. Или через WaitForSingleObject
В общем проясните пожалуйста эти азы. Или дайте толковую статейку
Юрий Зотов (2012-07-23 23:39:00) [13]
> DevilDevil © (23.07.12 22:32) [17]
> как ... синхронизировать ... очередь.
Дык... как любой список - критической секцией, например. Или взять готовый TThreadList.
Rouse_ (2012-07-23 22:57:00) [14]
> Но суть вопроса больше не в этом, а как именно по умному
> синхронизировать такую очередь.
По умному - не учитывать количество ядер и делать все по классике.
DevilDevil (2012-07-23 22:32:00) [15]> Rouse_ © (23.07.12 21:57) [16]
мне тут на одном форуме сказали, что оптимально - рабочих потоков создавать столько сколько ядер. Потоки могут прыгать по ядрам, но если им явно задать SetThreadAffinityMask - не должны. Другое дело, хочется мне сделать претензию на кроссплатформ, поэтому завязываться на API ОС честно говоря не хочется. Я поэтому и спросил, насколько это оправдано
Но суть вопроса больше не в этом, а как именно по умному синхронизировать такую очередь.
Rouse_ (2012-07-23 21:57:00) [16]ЗЫ: алго естественно асинхронное, дабы не потерять производительность на ожидании окончания работы каждой нити на каждой итерации. :)
Rouse_ (2012-07-23 21:54:00) [17]Кстати до кучи приведу такую схемку, дабы было понятно, когда нужно и не нужно использовать нити.
Задача простая: необходимо написать алгоритм рекурсивного обхода каталогов в двух вариантах.
Первый - стандартная рекурсия, второй - на каждую рекурсивную итерацию создается собственная нить. Замерить скорость - сделать вывод.
Rouse_ (2012-07-23 21:46:00) [18]
> Inovet © (23.07.12 21:32) [13]
> А сколько потоков создавать 1-2 или 100, может 1000000.
Все зависит от конкретной задачи и возможностей конкретной машины.
Начиная с определенного количества нитей (потоков) производительность начинает падать.
Т.е. грубо при одной нити мы имеем 100 вычислений в секунду, при четырех 400 при восьми всего 600, значит золотая серединка где-то почти рядом :)
Inovet (2012-07-23 21:32:00) [19]> [11] Сергей М. © (23.07.12 20:37)
> А зачем тогда вообще задумываться про "ядра" ?
А сколько потоков создавать 1-2 или 100, может 1000000.
Rouse_ (2012-07-23 21:18:00) [20]Достаточно бестолковая задача, ось все равно сделает так, как ей более удобно. Нет, ну она конечно постарается придерживаться условиям, заданной при SetThreadAffinityMask, но не удивляйся моменту когда все нити поочередно будут исполнятся на одном ядре или прыгать с ядра на ядро. Года два назад была примерная тема, я там пример писал, который кажет сей нюанс.
Оть если твой код использует расширенные возможности процессора (ну там устаревший MMX или SSE) тут уже более реально (почему - линк искать лениво - в инете все есть), по поводу банального прикладного кода, плюнь и забудь, по любому ось быстрее и правильней сделает.