Многопоточность Java регулярное выражение - PullRequest
3 голосов
/ 18 марта 2012

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

ParsedDoc article = parseDoc(rawDocument);

Класс parseDoc представляет собой набор из примерно 20 методов со следующим шаблоном:

public String clearContent(String document) {
  Pattern regex = Pattern.compile(pattern);
  Matcher matcher = regex.matcher(document);
  matcher.find();
  ....
}

public String removeHTML(String document) {
  Pattern regex = Pattern.compile(pattern);
  Matcher matcher = regex.matcher(document);
  matcher.replaceAll("");
  ....
}

Проблема, с которой я сталкиваюсь, заключается в том, что код работает достаточно быстро на моем локальном (2-ядерном) компьютере. Но когда я запускаю тот же код на 8-ядерном компьютере, производительность потребителя падает почти до полной остановки. Я пытался оптимизировать параметры jvm, но безрезультатно. Удаление этапа обработки регулярных выражений привело к ожидаемому увеличению производительности x4 на 8 ядрах. Так что проблема в регулярных выражениях. Я понимаю, что Pattern является потокобезопасным, и может потребоваться сброс matcher (). Но проблема в том, как перепроектировать банк регулярных выражений (в классе parseDoc) так, чтобы он был ориентирован на многопотоковое выполнение для M потребителей.

Любая помощь будет высоко ценится

Спасибо

Ответы [ 3 ]

1 голос
/ 18 марта 2012

Компиляция регулярного выражения выполняется медленно.Вы должны сделать это только один раз для данного шаблона.Если переменная pattern, показанная в вашем образце, действительно отличается для каждого вызова, экземпляры Pattern могут быть членами класса static.Pattern явно безопасен для одновременного использования несколькими потоками.(Все изменяемое состояние поддерживается Matcher.)

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

1 голос
/ 18 марта 2012

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

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

Вот edit , чтобы ответить на ваши вопросы: Проблема (подробнее) : чем больше ядер, тем медленнее становится. Зачем? Общая память.

@ Peyman use ConcurrentLinkedQueue (это неблокирующая очередь без ожидания, в которой одновременно может выполняться одна очередь и одна очередь). Даже попробуйте это в своем первоначальном проекте и сравните оба проекта. Я ожидаю, что ваш пересмотренный дизайн будет работать лучше, потому что вы можете иметь только 1 очередь и 1 очередь одновременно, в отличие от одной очереди и очереди n, как в первоначальном проекте (но это только мои предположения).

A Отличная статья о масштабируемой потребительской продукции с использованием балансировщиков

Чтение этой страницы (или можно посмотреть только на "переход с подхода с общей рабочей очередью на подход с очередью на поток")

Вот список из http://www.javaperformancetuning.com/news/newtips128.shtml. Я думаю, что последние 3 пункта более применимы к вам:

  • Большинство серверных приложений используют общую рабочую очередь и пул потоков; общая рабочая очередь содержит короткие задачи, поступающие из удаленных источников; пул потоков извлекает задачи из очереди и обрабатывает задачи; потоки блокируются в очереди, если нет задачи для обработки.
  • Очередь фидеров, разделяемая между потоками, является узким местом доступа (из-за конфликта), когда число задач велико, а время задания очень мало. Узкое место ухудшается с увеличением количества используемых ядер.
  • Решения, доступные для преодоления конфликтов при доступе к общей очереди, включают: использование структур данных без блокировки; Использование параллельных структур данных с несколькими блокировками; Поддержание нескольких очередей для изоляции конфликта.
  • Подход «очередь на поток» устраняет конфликт доступа к очереди, но не оптимален, когда очередь очищается, когда в других очередях находятся необработанные данные в очереди. Чтобы улучшить это, свободные потоки должны иметь возможность украсть работу из других очередей. Чтобы свести к минимуму конкуренцию, «кража» должна выполняться из хвоста другой очереди (где обычное удаление из собственной очереди потока выполняется из начала очереди).
0 голосов
/ 18 марта 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...