вторник, 2 января 2018 г.

C++: Оптимальная длина очереди в thread pool

Пулы тредов используются весьма широко. Однако часто вызывают один раздражающе постоянный вопрос. Связанный с наличием очереди в пулах.

А именно - какой длины должна быть очередь?

Гугл не знает ответа на этот вопрос, кроме самого общего соображения, в вольном переводе звучащего так: "Очередь должна многократно превосходить длиной число тредов в пуле".

Хе-хе. Непонятно звучит. Насколько - "многократно"?

Так получилось, что у меня образовался тестовый стенд, на котором можно было поэкспериментировать, наблюдая задержки при обработке пулом тредов, при изменениях длины очереди.

Для опытов использовался вот этот пул тредов (по моему мнению, наилучший из известных).

Пул использует неблокирующую очередь MPMC, с ограниченной длиной (длина очереди может быть изменена при создании экземпляра пула и кратна степени 2).

Опыты проводились с начальной длиной очереди равной удвоенному количеству ядер (тредов пула) процессора.

Оказалось следующее. Разумеется, оптимальная длина очереди данной реализации пула сильно зависит от логики обрабатывающей процедуры (той, которая выполняется пулом). Соотношение длины очереди относительно числа тредов находилось как произведение числа ядер на мультипликатор длины:

static std::size_t v_queue = v_threads * QUEUE_MUL;

По результатам экспериментов (повторюсь - для конкретно данной реализации пула), оказалось, что наилучшие результаты, без задержек, связанных с заполнением очереди, дает величина мультипликатора QUQUE_MUL = 128.

То есть, оптимальная длина очереди должна в 128 раз превосходить число обрабатывающих тредов.

Дисклеймер. Я все равно советую в каждом конкретном случае проводить профилирование и тестирование. Моя рекомендация не догма и не идеал. Это число лишь означает, что оно оптимально в данном конкретном случае, с данным типом нагрузки и данной конкретной реализацией пула. Все же, можно считать его хорошим начальным приближением.