Rendimiento de Java XPath(implementación Apache JAXP)
performance (3)
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 bloqueo 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! :
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:
NOTA: Si también experimentas este problema, vuélgalo por favor sobre Apache JIRA:
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?
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:
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 objetivo. 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 ...