Rendimiento de Java XPath (implementación Apache JAXP)

NOTA: Si también experimentas este problema, vuélgalo por favor sobre Apache JIRA:

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

He llegado a una sorprendente conclusión de que esto:

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

Parece ser un increíble 100 veces más rápido que esto:

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

Estoy usando la implementación predeterminada de JVM de JAXP:

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

Estoy realmente confundido, porque es fácil ver cómo JAXP podría optimizar la consulta anterior de XPath para ejecutar realmente un simple getElementsByTagName() lugar. Pero no parece hacer eso. Este problema se limita a alrededor de 5-6 llamadas XPath de uso frecuente, que una API abstrae y oculta. Esas consultas involucran caminos sencillos (p /a/b/c Ej., /a/b/c , sin variables, condiciones) frente a un documento DOM disponible siempre únicamente. Entonces, si se puede hacer una optimización, será bastante fácil de lograr.

Mi pregunta: ¿la lentitud de XPath es un hecho aceptado, o estoy pasando por alto algo? ¿Hay una mejor implementación (más rápida)? ¿O debería simplemente evitar XPath por completo, para consultas simples?

He depurado y perfilado mi caso de prueba y Xalan / JAXP en general. Logré identificar el gran problema principal en

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

Se puede ver que cada una de las 10k evaluaciones XPath de prueba llevó al cargador de clases a intentar buscar la instancia DTMManager en algún tipo de configuración predeterminada. Esta configuración no se carga en la memoria, sino que se accede a ella en todo momento. Además, este acceso parece estar protegido por un locking en ObjectFactory.class . Cuando el acceso falla (de forma predeterminada), la configuración se carga desde el archivo xalan.jar

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

archivo de configuración. ¡Cada vez! :

Resultados del perfil de JProfiler

Afortunadamente, este comportamiento puede anularse especificando un parámetro de JVM como este:

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

o

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

Lo anterior funciona, ya que esto permitirá pasar por alto el costoso trabajo en lookUpFactoryClassName() si el nombre de clase de fábrica es el predeterminado de todos modos:

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

Aquí hay una descripción general de la mejora del rendimiento para 10k evaluaciones XPath consecutivas de //SomeNodeName en un archivo XML de 90k (medido con 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 | 

tenga en cuenta que el punto de referencia fue muy primitivo. bien puede ser que su propio punto de referencia muestre que saxon supera a xalan

He archivado esto como un error para los tipos de Xalan en Apache:

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

No es una solución, sino un indicador del problema principal: la parte más lenta del proceso para evaluar un xpath en relación con un nodo arbitrario es el tiempo que le toma al administrador de DTM buscar el manejador del nodo:

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

Si el nodo en cuestión está al final del documento, puede terminar recorriendo todo el árbol para encontrar el nodo en cuestión, para cada consulta.

Esto explica por qué funciona el truco para huérfano del nodo objective. Debería haber una forma de almacenar en caché estas búsquedas, pero en este punto no puedo ver cómo.

Para responder a su pregunta, vtd-xml es mucho más rápido que Jaxen o Xalan) (yo diría que en promedio 10 veces, y 60 veces se ha informado …