Выполнение 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);

Я использую JVM по умолчанию JAXP:

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

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

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

Ответ 1

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

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

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

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

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

JProfiler profiling results

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

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

или

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

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

// 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

Итак, вот обзор улучшения производительности для 10k последовательных оценок XPath //SomeNodeName в файле 90k 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   |

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

Я зарегистрировал это как ошибку для парней Xalan в Apache:

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

Ответ 2

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

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

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

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

Ответ 3

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