Estoy intentando utilizar el paquete javax.xml.xpath para ejecutar expresiones XPath en un documento con varios espacios de nombres, y estoy teniendo problemas de rendimiento ridículos.El rendimiento de XPath.evaluate se ralentiza (absurdamente) en llamadas múltiples
Mi documento de prueba se extrae de un ejemplo de producción real. Se trata de 600k de xml. El documento es un feed Atom bastante complejo.
Me doy cuenta de que lo que estoy haciendo con XPath podría hacerse sin él. Sin embargo, la misma implementación en otras plataformas infinitamente inferiores funciona de manera absurdamente mejor. En este momento, reconstruir mi sistema para no usar XPath está más allá del alcance de lo que puedo hacer en el tiempo que tengo.
Mi código de prueba es algo como esto:
void testXPathPerformance()
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(loadTestDocument());
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
NamespaceContext names = loadTestNamespaces();
//there are 12 namespaces in names. In this example code, I'm using
//'samplens' instead of the actual namespaces that my application uses
//for simplicity. In my real code, the queries are different text, but
//precisely the same complexity.
xp.setNamespaceContext(names);
NodeList nodes = (NodeList) xp.evaluate("/atom:feed/atom:entry",
doc.getDocumentElement(), XPathConstants.NODESET);
for(int i=0;i<nodes.getLength();i++)
{
printTimestamp(1);
xp.evaluate("atom:id/text()", nodes.item(i));
printTimestamp(2);
xp.evaluate("samplens:fieldA/text()", nodes.item(i));
printTimestamp(3);
xp.evaluate("atom:author/atom:uri/text()", nodes.item(i));
printTimestamp(4);
xp.evaluate("samplens:fieldA/samplens:fieldB/&at;attrC", nodes.item(i));
printTimestamp(5);
//etc. My real example has 10 of these xp.evaluate lines
}
}
Cuando corro en un Nexus One, (no en el depurador, pero si se usa USB), la primera vez a través del bucle, cada xp.evaluate lleva de 10 ms a 20 ms. A la decimoquinta vez a través del ciclo, cada xp.evaluate toma entre 200 ms y 300 ms. Al final del ciclo (hay 150 elementos en nodes
), se requieren aproximadamente 500ms-600ms para cada xp.evaluate.
He intentado usar xp.compile(). Las compilaciones toman < 5ms. He hecho xp.reset() (no hace diferencia). He hecho un nuevo objeto XPath para cada evaluación (agrega unos 4 ms).
El uso de la memoria no parece descontrolarse durante la ejecución.
Estoy ejecutando esto en un solo hilo en un caso de prueba JUnit que no crea una actividad ni nada.
Estoy realmente desconcertado.
¿Alguien tiene alguna idea de qué más probar?
Gracias!
actualización
Si funciono el bucle hacia atrás (for(int i=nodes.getLength()-1;i>=0;i--)
), entonces los primeros nodos toman los 500ms-600ms, y los últimos en ir rápido 10ms-20ms. Por lo tanto, parece que no tiene nada que ver con el número de llamadas, sino que las expresiones cuyo contexto está cerca del final del documento toman más tiempo que las expresiones cuyo contexto está cerca del comienzo del documento.
¿Alguien tiene alguna idea sobre lo que puedo hacer al respecto?
@Andrew Shelansky: ¿Intentó ejecutar solo una consulta utilizando el nodo 'union' set oparator? El conjunto de nodos resultante estaría en orden de documento. –
@Andrew Shelansky: supongo que la NodeList que devuelve la expresión XPath se evalúa con pereza. Entonces, cada vez que haces nodes.item (i) es necesario contar a través de ítems para encontrar el nodo. Intente almacenar el nodo en la variable al comienzo del ciclo y vea si eso ayuda. –
@Nick Jones. En mi código de prueba, estoy haciendo lazy eval para nodes.item (i). En mi código de producción, en realidad estoy iterando a través de los nodos inmediatamente después de llamar al primer xp.evaluate. Los nodos resultantes se almacenan en un hashmap de UUID a Node, y se evalúan de esa manera. El código de producción muestra el mismo problema. Buena idea, sin embargo. –