Параллельный синтаксический анализ XML в Java - PullRequest
9 голосов
/ 17 ноября 2010

Я пишу приложение, которое обрабатывает много XML-файлов (> 1000) с глубокими структурами узлов. С помощью woodstox (Event API) требуется около шести секунд для анализа файла с 22 000 узлов.

Алгоритм помещен в процесс с взаимодействием с пользователем, где приемлемо время ответа всего несколько секунд Поэтому мне нужно улучшить стратегию обработки XML-файлов.

  1. Мой процесс анализирует XML-файлы (извлекает только несколько узлов).
  2. Извлеченные узлы обрабатываются, и новый результат записывается в новый поток данных (в результате получается копия документа с измененными узлами).

Теперь я думаю о многопоточном решении (которое лучше масштабируется на 16 ядрах + оборудование). Я думал о следующих состояниях:

  1. Создание нескольких парсеров и их параллельное выполнение в источниках xml.
  2. Переписав мой алгоритм синтаксического анализа с сохранением потока, чтобы использовать только один экземпляр синтаксического анализатора (фабрики, ...)
  3. Разделить источник XML на куски и назначить куски нескольким потокам обработки ( map-redux xml - serial )
  4. Оптимизация моего алгоритма (лучше парсер StAX, чем woodstox?) / Использование парсера со встроенным параллелизмом

Я хочу улучшить как общую производительность, так и производительность "на файл".

У вас есть опыт с такими проблемами? Какой лучший путь?

Ответы [ 3 ]

4 голосов
/ 18 ноября 2010
  1. Это очевидно: достаточно создать несколько парсеров и запустить их параллельно в нескольких потоках.

  2. Взгляните на Производительность Woodstox (в данный момент попробуйте google cache).

  3. Это может бытьГотово ЕСЛИ структура вашего XML предсказуема: если в нем много одинаковых элементов верхнего уровня.Например:

    <element>
        <more>more elements</more>
    </element> 
    <element>
        <other>other elements</other>
    </element>
    

    В этом случае вы можете создать простой сплиттер, который выполняет поиск <element> и передает эту часть конкретному экземпляру синтаксического анализатора.Это упрощенный подход: в реальной жизни я бы использовал RandomAccessFile, чтобы найти начальные точки остановки (<element>), а затем создал пользовательский FileInputStream, который просто работает с частью файла.

  4. Взгляните на Аалто .Те же самые ребята, которые создали Woodstox.Это эксперты в этой области - не изобретайте велосипед.

4 голосов
/ 17 ноября 2010

Я согласен с Джимом. Я думаю, что если вы хотите улучшить производительность общей обработки 1000 файлов, ваш план хорош, кроме # 3, который в данном случае не имеет значения. Однако если вы хотите улучшить производительность разбора одного файла, у вас есть проблема. Я не знаю, как можно разбить XML-файл без его разбора. Каждый чанк будет недопустимым XML, и ваш парсер не удастся.

Я считаю, что улучшение общего времени достаточно для вас. В этом случае прочитайте этот урок: http://download.oracle.com/javase/tutorial/essential/concurrency/index.html затем создайте пул потоков, например, из 100 потоков и очереди, содержащей источники XML. Каждый поток будет анализировать только 10 файлов, что принесет серьезный выигрыш в производительности в многопроцессорной среде.

2 голосов
/ 22 декабря 2010

В дополнение к существующим хорошим предложениям есть одна довольно простая вещь: использовать API курсора (XMLStreamReader), а не API событий.Event API добавляет 30-50% накладных расходов без (только IMO), что значительно упрощает обработку.На самом деле, если вам нужно удобство, я бы рекомендовал вместо этого использовать StaxMate ;он построен на основе Cursor API без значительных накладных расходов (не более 5-10% по сравнению с рукописным кодом).

Теперь: я полагаю, что вы провели базовую оптимизацию с Woodstox;но если нет, ознакомьтесь с « 3 простых правил быстрой обработки XML с использованием Stax ».В частности, вам абсолютно необходимо:

  1. Убедитесь, что вы создаете экземпляры XMLInputFactory и XMLOutputFactory только один раз
  2. Закройте устройства чтения и записи, чтобы гарантировать, что переработка буфера (и другое полезное повторное использование) работает как положено.1012 *

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

Запуск нескольких экземпляров также имеет смысл;хотя обычно с не более 1 потока на ядро.Однако вы получите выгоду только в том случае, если ваш ввод / вывод для хранения данных поддерживает такие скорости;если диск является узким местом, это не поможет, а в некоторых случаях может повредить (если диск стремится конкурировать).Но попробовать стоит.

...