Когда использовать схему разрушения, а когда локальное хранилище с кражей работы?
Правильно ли следующее?
- Шаблон разрушения имеет лучшую производительность и масштабируемость при параллельном обработке, если каждая запись должна обрабатываться несколькими способами (операции или аннотации), поскольку она может быть распараллелена с использованием нескольких потребителей без конфликтов.
- Напротив, кража работы (то есть локальное хранение записей и кража записей из других потоков) имеет лучшую параллельную производительность и масштабируемость, если каждая запись должна обрабатываться только одним способом, так как раздельное распределение записей по нескольким потокам в шаблоне прерывателя вызывает конфликт.
(И разве шаблон разрушения все еще намного быстрее, чем другие многопользовательские очереди без блокировки с несколькими производителями (например, от повышения), когда задействованы несколько производителей (например, операции 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, остальные все равно завершат их обработку.