Производительность Java XPath (реализация Apache JAXP)

ПРИМЕЧАНИЕ. Если вы столкнулись с этой проблемой, проголосуйте за нее в Apache JIRA:

https://issues.apache.org/jira/browse/XALANJ-2540

Я пришел к удивительному выводу, что это:

Element e = (Element) document.getElementsByTagName("SomeElementName").item(0);
String result = ((Element) e).getTextContent();

Кажется, это невероятно в 100 раз быстрее, чем это:

// Accounts for 30%, can be cached
XPathFactory factory = XPathFactory.newInstance();

// Negligible
XPath xpath = factory.newXPath();

// Negligible
XPathExpression expression = xpath.compile("//SomeElementName");

// Accounts for 70%
String result = (String) expression.evaluate(document, XPathConstants.STRING);

Я использую реализацию JAXP по умолчанию для JVM:

org.apache.xpath.jaxp.XPathFactoryImpl
org.apache.xpath.jaxp.XPathImpl

Я действительно запутался, потому что легко увидеть, как JAXP может оптимизировать приведенный выше запрос XPath, чтобы вместо этого фактически выполнить простой getElementsByTagName(). Но, похоже, это не так. Эта проблема ограничена примерно 5-6 часто используемыми вызовами XPath, которые абстрагируются и скрываются API. Эти запросы включают простые пути (например, /a/b/c, без переменных, условий) только к всегда доступному документу DOM. Таким образом, если оптимизация может быть выполнена, ее будет довольно легко достичь.

Мой вопрос: является ли медлительность XPath признанным фактом, или я что-то упускаю из виду? Есть ли лучшая (более быстрая) реализация? Или мне следует вообще избегать XPath для простых запросов?


person Lukas Eder    schedule 14.06.2011    source источник
comment
Пробовали ли вы сравнить свои результаты с результатами скомпилировано, повторное использование XPathExpression?   -  person McDowell    schedule 14.06.2011
comment
Это проблематично медленно? Одна из возможностей — оценить другую библиотеку, например jaxen.   -  person Johan Sjöberg    schedule 14.06.2011
comment
@ Макдауэлл, нет. Я попробую это, спасибо   -  person Lukas Eder    schedule 14.06.2011
comment
@Йохан: Это так. Как правило, эти запросы занимают 2-5 мс каждый, или 10% времени запроса пользователя. Мы думаем, что это становится проблематичным, так как в будущем мы собираемся использовать больше XPath. jaxen действительно может стать вариантом в будущем...   -  person Lukas Eder    schedule 14.06.2011
comment
@McDowell: кэширование выражения незначительно, особенно по сравнению с кэшированием фабрики. Но после нескольких повторных тестов мне приходится исправлять время. Кэширование фабрики ускорится примерно на 30%   -  person Lukas Eder    schedule 14.06.2011
comment
Для интересующихся добавил ответ на вопрос   -  person Lukas Eder    schedule 14.06.2011
comment
@Johan, я также измерил jaxen. В моем тестовом случае это еще хуже, по сравнению с saxon или xalan...   -  person Lukas Eder    schedule 14.06.2011
comment
@Лукас, интересно. Хорошая работа по выполнению теста.   -  person Johan Sjöberg    schedule 14.06.2011
comment
@Johan, да, приятно знать, что в конце концов наш выбор технологии был правильным, даже если xalan выглядит как динозавр :-)   -  person Lukas Eder    schedule 14.06.2011
comment
@johan, лучшая производительность xpath - это vtd-xml, вы пробовали это?   -  person vtd-xml-author    schedule 29.07.2013
comment
см. также: stackoverflow.com/q/3782618/363573   -  person Stephan    schedule 03.07.2018


Ответы (3)


Я отладил и профилировал свой тестовый пример и Xalan/JAXP в целом. Мне удалось определить большую основную проблему в

org.apache.xml.dtm.ObjectFactory.lookUpFactoryClassName()

Можно видеть, что каждая из 10k тестовых оценок XPath приводила к тому, что загрузчик классов пытался найти экземпляр DTMManager в какой-то конфигурации по умолчанию. Эта конфигурация не загружается в память, а используется каждый раз. Кроме того, этот доступ, по-видимому, защищен блокировкой самого ObjectFactory.class. При неудачном доступе (по умолчанию) конфигурация загружается из файла xalan.jar

META-INF/service/org.apache.xml.dtm.DTMManager

конфигурационный файл. Каждый раз!:

Результаты профилирования JProfiler

К счастью, это поведение можно переопределить, указав параметр JVM следующим образом:

-Dorg.apache.xml.dtm.DTMManager=
  org.apache.xml.dtm.ref.DTMManagerDefault

or

-Dcom.sun.org.apache.xml.internal.dtm.DTMManager=
  com.sun.org.apache.xml.internal.dtm.ref.DTMManagerDefault

Приведенное выше работает, так как это позволит обойти дорогостоящую работу в lookUpFactoryClassName(), если имя фабричного класса в любом случае является значением по умолчанию:

// Code from com.sun.org.apache.xml.internal.dtm.ObjectFactory
static String lookUpFactoryClassName(String factoryId,
                                     String propertiesFilename,
                                     String fallbackClassName) {
  SecuritySupport ss = SecuritySupport.getInstance();

  try {
    String systemProp = ss.getSystemProperty(factoryId);
    if (systemProp != null) { 

      // Return early from the method
      return systemProp;
    }
  } catch (SecurityException se) {
  }

  // [...] "Heavy" operations later

Итак, вот обзор улучшения производительности для 10 000 последовательных оценок XPath для //SomeNodeName по сравнению с 90 000 XML-файлом (измерено с System.nanoTime()):

measured library        : Xalan 2.7.0 | Xalan 2.7.1 | Saxon-HE 9.3 | jaxen 1.1.3
--------------------------------------------------------------------------------
without optimisation    :     10400ms |      4717ms |              |     25500ms
reusing XPathFactory    :      5995ms |      2829ms |              |
reusing XPath           :      5900ms |      2890ms |              |
reusing XPathExpression :      5800ms |      2915ms |      16000ms |     25000ms
adding the JVM param    :      1163ms |       761ms |        n/a   |

Обратите внимание, что эталонный тест был очень примитивным. вполне может быть, что ваш собственный тест покажет, что saxon превосходит xalan

Я сообщил об этом как об ошибке ребятам Xalan из Apache:

https://issues.apache.org/jira/browse/XALANJ-2540

person Lukas Eder    schedule 14.06.2011
comment
Очень интересно. Я был бы очарован, увидев, какие числа вы получите, если замените реализацию XPathFactory на Saxon. Я знаю, что поиск пути к классам для фабрики XPath сопряжен с большими накладными расходами, и я подозреваю, что синтаксический анализ выражения XPath сопряжен с большими накладными расходами, но я думаю, что фактическое выполнение должно быть довольно быстрым. - person Michael Kay; 14.06.2011
comment
@Michael: Я вижу, ты связан с Saxon? Я скачал домашнюю версию и провел с ней тесты. Там, где у Xalan было 5800 мс, у Saxon - 16000 мс... - person Lukas Eder; 14.06.2011
comment
Отличная и недорогая оптимизация - Спасибо, Лукас! Я отмечаю, что повторное использование XpathFactory, Xpath, XpathExpression невозможно в нескольких потоках, все три, к сожалению, не являются потокобезопасными. Нашли ли вы дальнейшую оптимизацию более быстрых альтернативных библиотек?? - person joelittlejohn; 17.06.2011
comment
@joelittlejohn, нет. Только то, что я упомянул. 1) преобразовать простые выражения XPath в вызовы API DOM, 2) добавить параметр JVM и, возможно, я собираюсь добавить 3) общий пул XPathFactory для безопасного повторного использования их. С учетом этих измерений я ожидаю, что XPath запросы займут всего около 5% сегодняшнего XPath времени процессора. - person Lukas Eder; 17.06.2011
comment
Я удивлен, что Saxon был таким медленным по сравнению с Xalan — обычно он немного быстрее. У вас есть код для теста? - person Raman; 26.10.2012
comment
@Raman: Нет, к сожалению, у меня его больше нет ... Однако он был в некоторой степени эквивалентен коду в вопросе ... - person Lukas Eder; 26.10.2012
comment
См. также сообщение в блоге 2009 г. scn.sap.com/community/java/blog/2009/12/04/ - person JasonPlutext; 14.11.2013
comment
@JasonPlutext: Спасибо за ссылку! Интересно, что это было обнаружено двумя годами ранее. Жаль, что автор этой статьи еще не зарегистрировал ошибку. Я дам ссылку на этот вопрос. - person Lukas Eder; 14.11.2013
comment
@Lukas В моих тестах Saxon быстрее, чем xalan: github.com/gatling/xpath-benchmark - person Stephane Landelle; 04.05.2014
comment
@StephaneLandelle: Это был очень быстрый бенчмарк, проверяющий только мое очень специфическое выражение XPath, которое я сделал по просьбе Майкла Кея (первый комментарий) три года назад. Вполне может быть, что Саксон тем временем улучшился. - person Lukas Eder; 04.05.2014

Не решение, а указатель на основную проблему: самая медленная часть процесса оценки xpath по отношению к произвольному узлу — это время, которое требуется диспетчеру DTM для поиска дескриптора узла:

http://javasourcecode.org/html/open-source/jdk/jdk-6u23/com/sun/org/apache/xml/internal/dtm/ref/dom2dtm/DOM2DTM.html#getHandleOfNode%28org.w3c.dom.Node%29

Если рассматриваемый узел находится в конце документа, он может пройти по всему дереву, чтобы найти рассматриваемый узел для каждого запроса.

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

person Robbie Matthews    schedule 23.12.2011
comment
Хм, это интересный вклад. Я должен снова профилировать, хотя. До сих пор мне не приходило в голову, что этот метод оказал какое-либо существенное влияние на общую производительность. - person Lukas Eder; 23.12.2011
comment
Недавно я столкнулся с этим и обнаружил смехотворно неэффективный поиск дескриптора DTM, пройдясь по нему в отладчике. Кто бы мог подумать, что передача узла контекста и выражения, которое выбирает прямой дочерний элемент узла контекста, потребует обхода всего дерева DOM? Я переписал код загрузки, используя StAX вместо XPath, и время выполнения сократилось с 5 часов до полсекунды. - person Wyzard; 27.03.2012

Чтобы ответить на ваш вопрос, vtd-xml намного быстрее, чем Jaxen или Xalan) (я бы сказал, в среднем в 10 раз, а сообщалось о 60x...

person vtd-xml-author    schedule 29.07.2013
comment
Интересный. Можете ли вы предоставить немного больше деталей в поддержку ваших утверждений? Хотя это больше не будет применимо к моей первоначальной проблеме, я обязательно буду использовать ваш инструмент! - person Lukas Eder; 29.07.2013
comment
Что ж, vtd-xml — это простой инструмент для загрузки, его тестирование занимает 10 минут, перейдите на веб-сайт vtd-xml, и там есть много соответствующей информации, включая тесты производительности по сравнению с Jaxen и xalan. Наши внутренние результаты фактически показывают, что производительность jaxen значительно ухудшилась за последние несколько выпусков. - person vtd-xml-author; 29.07.2013