----------------------------------------------------------------------------------
@MSGID: 2:5055/182 11c7647f
@PID: jNode ver. 1.5
@TID: jNode ver. 1.5
@CHRS: CP866 2
[](
https://telegra.ph/file/a70b47fc44ec899fe5ed1.jpg)**Верхушка айсберга**
Время воскресных очешуительных историй.
[Хорошая статья, которая, как "Титаник", лишь поверхностно чаряпнула
айсберг потокового выполнения, плавающий в
Атлантике.](
https://habr.com/ru/articles/766028/)
Так получилось, что мне довелось много лет заниматься параллельным
процессингом и тредовыми вычислениями.
Постулаты, с которыми я согласен - и они подтверждены практикой -
пребывание в юзерспейсе дешевле в плане накладных расходов и утилизации
процессорных ядер. Простым языком - мьютексы дорогие.
Второе. Треды - это обычно не о лейтенси. Если вы хотите получить
соизмеримую с однопотоком латентность тред-пула, к примеру, вам придется принимать
целый ряд мер архитектурного порядка для достижения этой цели. Как-то -
фиксированное число воркеров (и - да, один поток на физическое ядро), множество
очередей (MPMC) фиксированной длины (причем очереди желательны неблокирующие,
что хотите, то и делайте), job stealing - причем его надо очень
вдумчиво реализовывать.
Все вместе означает весьма тщательное программирование с целью
минимизации паразитной работы и оверхеда.
И вот что еще важно - это, наверное, самое важное - минимизация
общих, совместно используемых данных. Это бывает очень непросто достичь,
например, делая отдельные TLS (thread local storage). Причем следует иметь в
виду, что реализация TLS в крестах, например, достаточно медленная и на
реально высоких скоростях следует принимать ряд специальных мер по ускорению
- вплоть до частично или полностью своей собственной реализации TLS на
основе pthreads.
Отдельная проблема - тредовый IO. IO bound задачи характерны тем,
что, как правило (бывают исключения) надолго блокируют ядра и воркеры,
вызывая характерные всплески утилизации CPU. Причем никаким разумным образом
эти всплески невозможно уменьшить.
Имеющиеся реализации AIO на тредах проблемны в плане утилизации
ресурсов, фрагментации памяти и крайне затруднительна реализаци тредовой
__записи__. Для тредовой записи нужны особые архитектурные решения, наподобие
партишенинга, и особая организация данных и/или совместного доступа. Что само по
себе крайне непросто.
Частное решение задачи, связанной одновременно с IO и CPU-bound
тредами, было выполнено [здесь](
https://github.com/yvoinov/thread-pool-cpp).
Реализация нацелена на решение всех вышеперечисленных задач, с
одновременной постановкой задачи "Пул не является постоянно нагруженным и должен
спать, если задач нет, при этом мгновенно просыпаться при появлении заданий
в очередях".
Практическое решение, использующее данную архитектуру, было применено
[в этом приложении](
https://github.com/yvoinov/store-id-helper) и позволило
достичь экстремально высокой масштабируемости в рамках конечного числа
процессорных ядер при минимальной латентности и пропускной способности в десятки и
сотни тысяч RPS.
Хочу заметить, что R&D, позволивший получить такой результат, занял
свыше 2500 человеко-часов.
Finally, подобные архитектурные выводы, как правило, подкреплены
солидной исследовательской базой и к ним следует, вообще говоря, прислушиваться
и учитывать их в проектировании ПО.
Особенно если мы говорим про тредовые многоядерники.
http://fido.ortoped.org.ru/photo_2023-10-08_10-37-41.jpg
--- hssergey station
* Origin: jNode ver. 1.5 (2:5055/182)
SEEN-BY: 301/1 460/58 4500/1 5001/100 5005/49
5015/255 5019/40 5020/715 848
SEEN-BY: 5020/1042 4441 12000 5030/49 1081 5055/182
5058/104 5061/133
SEEN-BY: 5083/444
@PATH: 5055/182 5020/1042 4441