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

Правильно ли следующее?

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

(И разве шаблон разрушения все еще намного быстрее, чем другие многопользовательские очереди без блокировки с несколькими производителями (например, от повышения), когда задействованы несколько производителей (например, операции CAS)?)


Моя ситуация в деталях:

Обработка записи может привести к появлению нескольких новых записей, которые также должны быть обработаны. Производительность имеет наивысший приоритет, записи, обрабатываемые в порядке FIFO, имеют второй приоритет.

В текущей реализации каждый поток использует локальный FIFO, куда он добавляет свои новые записи. Свободные потоки крадут работу из локального FIFO другого потока. Зависимости между обработкой потока разрешаются с использованием механически симпатичной хеш-таблицы без блокировки (CAS при записи с гранулярностью сегмента). Это приводит к довольно низкой конкуренции, но порядок FIFO иногда нарушается.

Использование шаблона разрушения гарантирует порядок FIFO. Но разве распределение записей по потокам не приведет к гораздо большему конфликту (например, CAS на курсоре чтения), чем к локальным FIFO с кражей работы (пропускная способность каждого потока примерно одинакова)?


Ссылки, которые я нашел

Тесты производительности в стандартном техническом документе о прерывателе (глава 5 + 6) не охватывают непересекающееся распределение работы.

https://groups.google.com/forum/?fromgroups= - это единственное упоминание, которое я нашел о нарушителе + краже работы. В нем говорится, что очередь на поток значительно медленнее, если есть какое-либо общее состояние, но не вдаваться в подробности и не объяснять, почему. Я сомневаюсь, что это предложение относится к моей ситуации с:

  • разделяемое состояние разрешается с помощью хэш-таблицы без блокировки;
  • необходимость раздельного распределения записей среди потребителей;
  • за исключением кражи работы, каждый поток читает и пишет только в своей локальной очереди.

1 ответ

Обновление - в нижней строке для максимальной производительности: вам нужно написать как в идиоматическом синтаксисе для разрушения и кражи работы, а затем тесты производительности.

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

  • Используйте кражу работы, когда ваша реализация сфокусирована на сообщениях. Каждый поток может получить сообщение и выполнить его до конца. Для примера HTTP-сервера - каждому входящему http-запросу выделяется поток. Этот поток сосредоточен на обработке начала и конца запроса - регистрации запроса, проверке мер безопасности, поиске vhost, получении файла, отправке ответа и закрытии соединения.

  • Используйте прерыватель, когда ваша реализация сфокусирована на задачах. Каждый поток может работать на определенном этапе обработки. Альтернативный пример: для фокуса задачи обработка будет разбита на этапы, поэтому у вас будет поток, ведущий запись в журнал, поток для элементов управления безопасностью, поток для поиска vhost и т. Д.; каждый поток фокусируется на своей задаче и передает запрос следующему потоку в конвейере. Этапы могут быть распараллелены, но общая структура представляет собой поток, ориентированный на конкретную задачу, и передает сообщение между потоками.

Конечно, вы можете изменить свою реализацию для лучшего соответствия каждому подходу.

В вашем случае я бы структурировал проблему иначе, если бы вы хотели использовать Disruptor. Как правило, вы устраняете общее состояние, имея единственный поток, владеющий состоянием, и пропускаете все задачи через этот поток работы - поищите в SEDA множество подобных диаграмм. Это может иметь много преимуществ, но, опять же, действительно зависит от вашей реализации.

Еще немного многословия:

  • Разрушитель - очень полезен, когда требуется строгий порядок этапов, дополнительные преимущества, когда все задачи имеют одинаковую длину, например: отсутствие блокировки во внешней системе и очень похожий объем обработки для задачи. В этом сценарии вы можете предположить, что все потоки будут работать равномерно через систему, и, следовательно, организовать N потоков для обработки каждого N сообщений. Мне нравится думать о Disruptor как об эффективном способе внедрения SEDA-подобных систем, где потоки обрабатывают этапы. Конечно, у вас может быть приложение с одним этапом и несколькими параллельными блоками, выполняющими одну и ту же работу на каждом этапе, однако, на мой взгляд, это не совсем так. Это полностью исключило бы стоимость общего состояния.
  • Воровство работы - используйте это, когда задачи имеют различную продолжительность, и порядок обработки сообщений не важен, так как это позволяет потокам, которые свободны и уже использовали свои сообщения, продолжать выполнение из другой очереди задач. Таким образом, если, например, у вас есть 10 потоков и 1 заблокирован в IO, остальные все равно завершат их обработку.
Другие вопросы по тегам