XPath.évaluer la performance ralentit (par l'absurde) sur les appels multiples

Je suis en train d'utiliser le javax.xml.xpath package pour exécuter des expressions XPath sur un document avec plusieurs espaces de noms, et je vais avoir dingo des problèmes de performances.

Mon test de document est tiré à partir d'un réel, exemple de production. Il est d'environ 600k de xml. Le document est assez complexe Atom.

Je me rends compte que ce que je fais avec XPath qui pourrait être fait sans. Cependant, la même mise en œuvre sur d'autres, largement inférieure plates-formes effectue par l'absurde mieux. Maintenant, la reconstruction de mon système de ne pas utiliser XPath est au-delà de la portée de ce que je peux faire dans le temps que j'ai.

Mon code de test est quelque chose comme ceci:



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

     }
}

Quand je le lance sur un Nexus One, (pas dans le débogueur, mais avec l'USB connecté), le premier passage dans la boucle, chaque xp.évaluer prend quelque part de 10ms à 20ms. Par la 15e fois dans la boucle, chaque xp.évaluer prend quelque part à partir de 200 ms à 300 ms. D'ici à la fin de la boucle (il y a 150 articles dans nodes), il faut environ 500ms-600ms pour chaque xp.évaluer.

J'ai essayé d'utiliser xp.compiler(). La compile tous les prendre <5ms. J'ai fait xp.reset() (ne fait pas de différence). J'ai fait une nouvelle XPath objet à évaluer (ajoute à propos de 4ms).

L'utilisation de la mémoire ne semble pas en spirale hors de contrôle pendant l'exécution.

Je suis l'exécution de ce sur un seul thread dans un cas de test JUnit qui n'est pas de créer une activité ou de quoi que ce soit.

Je suis vraiment perplexe.

Quelqu'un a une idée ce que d'autre à essayer?

Merci!

mise à jour

Si j'exécute la boucle vers l'arrière (for(int i=nodes.getLength()-1;i>=0;i--)), puis les premiers nœuds de prendre les 500ms-600ms, et les derniers à aller vite 10ms-20ms. Donc, cela semble comme il n'a rien à voir avec le nombre d'appels, mais plutôt que les expressions dont le contexte est proche de la fin du document, de prendre plus de temps que les expressions dont le contexte est près du début du document.

Quelqu'un avez des idées sur ce que je peux faire à ce sujet?

  • Shelansky: Avez-vous essayez d'exécuter une seule requête de ussing | de l'union de l'ensemble de nœuds oparator? Résultat de l'ensemble de nœuds serait dans l'ordre du document.
  • Shelansky: j'imagine que la NodeList être de retour par l'expression XPath est évaluée paresseusement. Donc, chaque fois que vous faites des nœuds.le point(i) il est d'avoir à compter, je les éléments de trouver le nœud. Essayez de stocker le nœud de la variable au début de la boucle et voir si cela aide.
  • Jones. Dans mon code de test, je suis en train de faire paresseux eval pour les nœuds.le point(i). Dans mon code de production, je suis en fait une itération à travers les nœuds immédiatement après l'appel de la première xp.évaluer. Les nœuds qui en résultent sont stockés dans une table de hachage de l'UUID de Nœud, et de l'évaluation de cette façon. Le code de production présente le même problème. Bien pensé, bien.
  • Je ne peux pas aider, mais je voulais témoigner leur sympathie qu' '"absurde" décrit également mon expérience avec le fait d'essayer d'utiliser la référence javax.xml.xpath dans la production. La seule véritable solution pour nous a été de commutation tout à Jaxen. Ne sais pas si c'est encore possible sur android 🙁
  • non, je ne l'ai pas fait. Je n'ai pas vraiment savoir à l'avance ce que l'ordre du document va être pour les éléments que je veux. Aussi loin que je peux dire, cependant, la seule considération importante pour combien de temps il faudra pour exécuter est de savoir comment loin dans le document, le nœud de contexte est.
  • Je n'ai toujours pas compris pourquoi ce qui se passe, sauf pour être certain que c'est purement à propos la distance entre le haut du document, le nœud de contexte est. Pour ma part, depuis que je suis toujours en train de travailler avec des documents plutôt gros, et jamais à l'aide de Xpath qui se soucient de parents ou ancêtres, je suis juste appeler cloneNode() avant d'appeler xp.évaluer. Il fonctionne environ 800% plus rapide. C'est un terrible "solution" parce que je sais qu'un jour je vais avoir une expression qui se soucie de la mère, mais pour l'instant...