56 votes

Performances de Java XPath (implémentation Apache JAXP)

REMARQUE : si vous rencontrez également ce problème, merci de le soumettre à la validation sur Apache JIRA :

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

J'en suis arrivé à la conclusion étonnante que ceci :

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

Il semble être incroyablement 100x plus rapide que ça :

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

J'utilise l'implémentation par défaut de JAXP de la JVM :

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

Je suis vraiment confus, parce qu'il est facile de voir comment JAXP pourrait optimiser la requête XPath ci-dessus pour exécuter en fait un simple getElementsByTagName() à la place. Mais il ne semble pas le faire. Ce problème est limité à environ 5-6 appels XPath fréquemment utilisés, qui sont abstraits et cachés par une API. Ces requêtes impliquent des chemins simples (par ex. /a/b/c sans variables, ni conditions) par rapport à un document DOM toujours disponible uniquement. Donc, si une optimisation peut être faite, elle sera assez facile à réaliser.

Ma question : La lenteur de XPath est-elle un fait reconnu, ou est-ce que je néglige quelque chose ? Existe-t-il une meilleure implémentation (plus rapide) ? Ou devrais-je simplement éviter XPath pour les requêtes simples ?

4 votes

Avez-vous essayé de comparer vos résultats avec ceux d'un compilé réutilisable XPathExpression ?

0 votes

La lenteur est-elle problématique ? Une possibilité est d'évaluer une autre bibliothèque, telle que jaxen .

0 votes

@McDowell, non. Je vais essayer, merci.

63voto

Lukas Eder Points 48046

J'ai débogué et profilé mon cas de test et Xalan/JAXP en général. J'ai réussi à identifier le gros problème majeur dans

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

On peut voir que chacune des 10 000 évaluations XPath du test a conduit le chargeur de classes à essayer de rechercher le fichier DTMManager dans une sorte de configuration par défaut. Cette configuration n'est pas chargée en mémoire, mais on y accède à chaque fois. De plus, cet accès semble être protégé par un verrou sur l'objet ObjectFactory.class même. Lorsque l'accès échoue (par défaut), la configuration est alors chargée à partir du fichier xalan.jar du fichier

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

le fichier de configuration. A chaque fois ! :

JProfiler profiling results

Heureusement, ce comportement peut être modifié en spécifiant un paramètre JVM comme ceci :

-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

Ce qui précède fonctionne, car cela permettra de contourner le travail coûteux en lookUpFactoryClassName() si le nom de la classe d'usine est de toute façon la valeur par défaut :

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

Voici donc un aperçu de l'amélioration des performances pour 10 000 évaluations XPath consécutives de //SomeNodeName contre un fichier XML de 90k (mesuré avec 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   |

notez que le benchmark était très primitif. il se peut que votre propre benchmark montre que saxon surpasse xalan

J'ai signalé ce problème aux responsables de Xalan chez Apache :

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

0 votes

Très intéressant. Je serais fasciné de voir quels chiffres vous obtenez si vous remplacez l'implémentation Saxon de XPathFactory. Je sais qu'il y a un gros surcoût pour la recherche de la fabrique XPath dans le classpath, et je soupçonne qu'il y a un surcoût significatif pour l'analyse de l'expression XPath, mais je pense que l'exécution réelle devrait être assez rapide.

0 votes

@Michael : Vous êtes affilié à Saxon, je vois ? J'ai téléchargé l'édition familiale et j'ai fait mes benchmarks avec. Là où Xalan avait 5800ms, Saxon a mis 16000ms...

0 votes

Une excellente optimisation à faible coût - Merci Lukas ! Je note que la réutilisation de XpathFactory, Xpath, XpathExpression n'est pas possible sur plusieurs threads, les trois ne sont malheureusement pas thread-safe. Avez-vous trouvé d'autres optimisations de bibliothèques alternatives plus rapides ???

6voto

Robbie Matthews Points 191

Ce n'est pas une solution, mais une indication du problème principal : Le site le plus lent Une partie du processus d'évaluation d'un xpath par rapport à un nœud arbitraire est le temps que prend le gestionnaire de MNT pour trouver le handle du nœud :

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

Si le nœud en question se trouve à la fin du document, il peut finir par parcourir l'arbre entier pour trouver le nœud en question, pour chaque requête.

Cela explique pourquoi le hack pour rendre orphelin le noeud cible fonctionne. Il y a devrait être un moyen de mettre en cache ces recherches, mais pour l'instant je ne vois pas comment.

0 votes

Hmm, c'est une contribution intéressante. Mais je dois à nouveau me profiler. Il ne m'est pas venu à l'esprit jusqu'à présent que cette méthode avait un impact pertinent sur les performances globales.

1 votes

J'ai rencontré ce problème récemment, et j'ai découvert la recherche ridiculement inefficace de la poignée DTM en la parcourant dans le débogueur. Qui aurait pensé que passer un nœud de contexte et une expression qui sélectionne un enfant direct du nœud de contexte impliquerait de parcourir l'ensemble de l'arbre DOM ? J'ai réécrit mon code de chargement en utilisant StAX au lieu de XPath et mon temps d'exécution est passé de 5 heures à une demi-seconde.

0voto

vtd-xml-author Points 891

Pour répondre à votre question, vtd-xml est bien plus rapide que Jaxen ou Xalan) (je dirais en moyenne 10x, et 60x a été rapporté...

0 votes

Intéressant. Pouvez-vous fournir un peu plus de détails pour étayer vos affirmations ? Bien que cela ne s'applique plus à mon problème initial, je vais certainement suivre votre outil !

0 votes

Eh bien, vtd-xml est un outil simple à télécharger, il prend 10 minutes à tester, allez sur le site web de vtd-xml et il y a beaucoup d'informations pertinentes, y compris des tests de référence comparant à Jaxen, et xalan. Nos résultats internes montrent en fait que les performances de Jaxen se sont considérablement détériorées au cours des dernières versions

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X