Performance & Scalability пост №4: thread pools
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 00:16
Оценка: 14 (2)
Фиксированное количество нитей в многонитевой системе не есть уж очень хорошо. Зачастую не угадаешь пиковую нагрузку, а, если число реквестов превысит число нитей — то в некоторых случаях, когда обслуживание реквеста требует блокирующего надолго вызова — возникнет узкое место.

Потому используются динамические пулы нитей.

Как правильно имплементировать сие диво (если кому надо будет)?

Нить в данном случае есть ресурс. Если нить начала исполнять реквест — то количество доступных (ждущих реквеста на потребляющем конце очереди) нитей уменьшилось, возможно — до нуля.

Так вот, это "до нуля" тут же создает накопление реквестов в очереди, причем только потому, что еще не успели породить нить для их обслуживания, и реквест ждет либо такого порождения, либо пока закончится обслуживание еще какого-то реквеста. Второе — потеря скалабильности.

Потому разумно вот что: после того, как пуловая нить сняла реквест с очереди, она должна взглянуть на счетчик доступных нитей и, если он упал ниже какого-то значения, _тут же рожать еще одну нить_, выполняя реквест уже после этого. CreateThread — это достаточно быстро, по сравнению с, например, дисковой операцией или тем более SQL query.

В итоге накопление большой очереди надолго на пуле практически невозможно, по крайней мере если не ограничивать сверху число пуловых нитей. Как только наложен такой лимит — см. мои же посты о лимитах. При нагрузке большей, чем лимит — потеря скалабильности. При меньшей же нагрузке вообще все равно, есть лимит или нет.

Таковой лимит — как и остальные лимиты — имеет смысл только из соображений ограничения нагрузки на машину в целом. Кстати, основная нагрузка, создаваемая нитью — это кернел- и юзер- стеки.

Естественно, что имеет смысл сделать самоумирание нитей по таймауту в случае, если долго не было никакой нагрузки и так много нитей не нужно. Для этого ожидание на потребляющем конце очереди должно быть с таймаутом, и, если он наступил — то проверяется счетчик нитей, если он выше минимума — нить умирает.

Главное, думаю, понятно, остальное все напишут сами.

P.S. Ex/IoQueueWorkItem в ядре туп до блеска. Находясь под файловой системой, им пользоваться просто нельзя. Нельзя сделать queue work item из нити, которая сама есть work item — а путь записи на диск зачастую зовется из work item слива кэша.

Дедлоки из этой имплементации летят только так.
Занимайтесь LoveCraftом, а не WarCraftом!
Re: Performance & Scalability пост №4: thread pools
От: remark Россия http://www.1024cores.net/
Дата: 17.08.07 10:32
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>Кстати, основная нагрузка, создаваемая нитью — это кернел- и юзер- стеки.


Для пуловых нитей можно вполне установить по-умолчанию размер стека порядка 64k. С возможностью пользователю перекрыть это значение.
Для большинства операций этого вполне достаточно, особенно учитывая, что пуловая нить выполняет только одну какую-то операцию, типа запроса SQL.
При этом заметно снижается потребление адресного пространства процесса.



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Performance & Scalability пост №4: thread pools
От: the_dip Таджикистан  
Дата: 17.08.07 12:39
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

MSS>P.S. Ex/IoQueueWorkItem в ядре туп до блеска. Находясь под файловой системой, им пользоваться просто нельзя. Нельзя сделать queue work item из нити, которая сама есть work item — а путь записи на диск зачастую зовется из work item слива кэша.

MSS>Дедлоки из этой имплементации летят только так.

Расшифруйте, пожалуйста.
Re[2]: Performance & Scalability пост №4: thread pools
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 13:01
Оценка:
MSS>>P.S. Ex/IoQueueWorkItem в ядре туп до блеска. Находясь под файловой системой, им пользоваться просто нельзя. Нельзя сделать queue work item из нити, которая сама есть work item — а путь записи на диск зачастую зовется из work item слива кэша.
MSS>>Дедлоки из этой имплементации летят только так.

_>Расшифруйте, пожалуйста.


Ex/IoQueueWorkItem проимплементирован плохо, и склонен к дедлокам. Там ограничено общее количество нитей, там часто возникает дедлок, если выполнение одного work item завязано на ожидание выполнения другого work item, и так далее.

Так как Cache Manager и файловые системы активно пользуются этим пулом для lazy writing и кое-каких своих нужд, запросы к дисковому стеку (под файловой системой) зачастую приходят в контексте вот этого work item. Если дисковый фильтр хочет воспользоваться этим же пулом для каких-то своих нужд — велика вероятность дедлока.
Занимайтесь LoveCraftом, а не WarCraftом!
Re: Performance & Scalability пост №4: thread pools
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 20.08.07 16:33
Оценка:
Здравствуйте, Maxim S. Shatskih, Вы писали:

Прежде чем реализовывать хитрые динамические пулы нужно сперва просто разделить потоки хотя бы на две части — жрущие процессор и не жрущие, и развести их по разным пулам.
... << RSDN@Home 1.2.0 alpha rev. 716>>
AVK Blog
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.