Параллельный синтаксический анализ XML в Java

Я пишу приложение, которое обрабатывает много xml-файлов (>1000) с глубокими узловыми структурами. Она занимает около шести секунд с woodstox (Event API) для анализа файла с 22.000 узлами.

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

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

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

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

я хочу улучшить как производительность в целом, так и производительность" на файл".

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

3 ответов


  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. Это специалисты в этой области - не изобретайте колесо.


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

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


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

теперь: я предполагаю, что вы сделали basic оптимизация с помощью Woodstox; но если нет, проверьте"3 простых правила быстрой XML-обработки с помощью Stax". В частности, вы абсолютно должны:

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

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

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