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

Примечание: Если вы испытываете эту проблему, а также, пожалуйста, upvote его на Apache JIRA:

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

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

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

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

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

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

3 ответов


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

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

видно, что каждая из оценок XPath теста 10k привела к тому, что загрузчик классов пытается найти DTMManager например, в какой-то конфигурации по умолчанию. Эта конфигурация не загружается в память, но доступна каждый раз. Кроме того, этот доступ, похоже, защищен блокировкой . При сбое доступа (by по умолчанию), затем конфигурация загружается из 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() если имя Заводского класса по умолчанию во всяком случае:

// 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 против файла XML 90k (измеряется с 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 в Апач:

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


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

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

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

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


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