Question

Comment écrivez-vous (et exécutez-vous) un micro-critère correct en Java?

Je suis à la recherche d'échantillons de code et de commentaires illustrant divers points à prendre en compte.

Exemple: le repère doit-il mesurer le temps / itération ou les itérations / temps et pourquoi?

En relation: L'analyse comparative du chronomètre est-elle acceptable?

Était-ce utile?

La solution

Astuces pour la rédaction de micro-tests de performance des créateurs de Java HotSpot :

Règle 0: lisez un article réputé sur les machines virtuelles et le micro-benchmarking. Un bon exemple est Brian Goetz, 2005 . N'attendez pas trop des micro-repères; ils ne mesurent qu’une gamme limitée de caractéristiques de performance de la machine virtuelle Java.

Règle 1: toujours inclure une phase de préchauffage qui exécute votre noyau de test tout au long, suffisamment pour déclencher toutes les initialisations et les compilations avant la ou les phases de chronométrage. (Moins d'itérations sont acceptables lors de la phase d'échauffement. La règle générale est de plusieurs dizaines de milliers d'itérations de la boucle interne.)

Règle 2: exécutez-vous toujours avec -XX:+PrintCompilation, -verbose:gc, etc., afin de vérifier que le compilateur et les autres composants de la machine virtuelle ne font pas de travail inattendu pendant la phase de minutage.

Règle 2.1: imprimez les messages au début et à la fin des phases de chronométrage et de préchauffage afin de vérifier que la règle 2 ne produit aucun résultat au cours de la phase de chronométrage.

Règle 3: soyez conscient de la différence entre -client et -server et des compilations OSR et régulières. L’indicateur Trouble$1::run @ 2 (41 bytes) signale les compilations OSR avec un at-sign pour indiquer le point d’entrée non initial, par exemple: -Xbatch. Préférez le serveur au client et le standard OSR si vous recherchez les meilleures performances.

Règle 4: Tenez compte des effets d'initialisation. N'imprimez pas pour la première fois au cours de votre phase de chronométrage, car l'impression charge et initialise les classes. Ne chargez pas de nouvelles classes en dehors de la phase de préchauffage (ou de la phase de rapport final), sauf si vous testez spécifiquement le chargement de classe (et dans ce cas, ne chargez que les classes de test). La règle 2 constitue votre première ligne de défense contre de tels effets.

Règle 5: soyez conscient des effets de déoptimisation et de recompilation. Ne prenez aucun chemin de code pour la première fois au cours de la phase de synchronisation, car le compilateur risque de déverrouiller et de recompiler le code, en se basant sur une hypothèse optimiste antérieure selon laquelle le chemin ne serait pas utilisé du tout. La règle 2 constitue votre première ligne de défense contre de tels effets.

Règle 6: Utilisez les outils appropriés pour lire l'esprit du compilateur et attendez-vous à être surpris par le code qu'il génère. Inspectez vous-même le code avant de formuler des théories sur ce qui accélère ou ralentit quelque chose.

Règle 7: réduisez le bruit dans vos mesures. Exécutez votre test sur une machine silencieuse, puis exécutez-le plusieurs fois, en éliminant les valeurs aberrantes. Utilisez -XX:CICompilerCount=1 pour sérialiser le compilateur avec l'application et envisagez de définir Xmx pour empêcher le compilateur de s'exécuter en parallèle avec lui-même. Faites de votre mieux pour réduire les frais généraux du GC, définissez Xms (suffisamment grand) sur UseEpsilonGC et utilisez <=> s'il est disponible.

Règle 8: utilisez une bibliothèque pour votre critère, car elle est probablement plus efficace et a déjà été déboguée à cette seule fin. Tels que JMH , Caliper ou Bill et Les excellents tests UCSD de Paul pour Java .

Autres conseils

Je sais que cette question a été marquée comme ayant fait l'objet d'une réponse, mais je voulais mentionner deux bibliothèques qui nous aident à écrire des micro-repères

pied à coulisse de Google

Didacticiels de démarrage

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

JMH d'OpenJDK

Didacticiels de démarrage

  1. Éviter les pièges de l'analyse comparative sur la JVM
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://java-performance.info/jmh/

Les points importants pour les tests de performance Java sont les suivants:

  • Réchauffez d'abord le JIT en exécutant le code plusieurs fois avant de le chronométrer , il
  • Assurez-vous de l'exécuter suffisamment longtemps pour pouvoir mesurer les résultats en quelques secondes ou (mieux) des dizaines de secondes
  • Bien que vous ne puissiez pas appeler System.gc() entre les itérations, il est judicieux de l'exécuter entre les tests afin que chaque test obtienne, espérons-le, un & "clean &"; espace mémoire pour travailler avec. (Oui, gc() est plus une allusion qu'une garantie, mais il est très probable que cela va vraiment ramasser des ordures selon mon expérience.)
  • J'aime afficher les itérations et l'heure, ainsi qu'une vingtaine d'heures / itérations pouvant être mises à l'échelle de manière à ce que le & "meilleur &"; algorithme obtient un score de 1,0 et les autres sont évalués de manière relative. Cela signifie que vous pouvez exécuter tous tous les algorithmes pendant longtemps, en faisant varier le nombre d'itérations et l'heure, mais en obtenant des résultats comparables.

Je suis en train de bloguer à propos de la conception d'un framework d'analyse comparative en .NET. J'ai un couple . de plus tôt messages qui peuvent vous donner des idées - tout ne conviendra pas, bien sûr, mais certaines d'entre elles le seront peut-être.

jmh est un ajout récent à OpenJDK et a été écrit par certains ingénieurs de performance d'Oracle. Vaut le détour.

  

jmh est un harnais Java permettant de créer, d’exécuter et d’analyser des tests de performances nano / micro / macro écrits en Java et dans d’autres langages destinés à la machine virtuelle Java.

De très intéressantes informations cachées dans les exemples de commentaires testent .

Voir aussi:

  

Le repère doit-il mesurer le temps / itération ou les itérations / le temps et pourquoi?

Cela dépend de ce que vous essayez de tester.

Si vous êtes intéressé par la latence , utilisez le temps / itération et par le débit , utilisez les itérations / le temps.

Veillez à utiliser les résultats calculés dans du code référencé. Sinon, votre code peut être optimisé.

Si vous essayez de comparer deux algorithmes, effectuez au moins deux points de repère pour chacun, en alternant l'ordre. c'est-à-dire:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

J'ai constaté des différences notables (5 à 10% parfois) dans l'exécution d'un même algorithme à différentes étapes.

Assurez-vous également que n est très volumineux, de sorte que la durée d'exécution de chaque boucle soit d'au moins 10 secondes. Plus il y a d'itérations, plus les chiffres sont significatifs dans votre temps de référence et plus les données sont fiables.

Il existe de nombreux pièges pour l'écriture de micro-points de repère en Java.

Tout d’abord: vous devez calculer avec toutes sortes d’événements qui prennent du temps plus ou moins au hasard: ramassage des ordures, effets de mise en cache (du système d’exploitation pour les fichiers et du processeur pour la mémoire), IO, etc.

Deuxièmement: vous ne pouvez pas faire confiance à la précision des temps mesurés pour des intervalles très courts.

Troisièmement: la machine virtuelle Java optimise votre code lors de l’exécution. Des exécutions différentes dans la même instance JVM deviendront de plus en plus rapides.

Mes recommandations: Faites que votre point de référence soit exécuté quelques secondes, ce qui est plus fiable qu’une exécution de plusieurs millisecondes. Réchauffer la JVM (signifie exécuter le test de performance au moins une fois sans mesurer que la JVM peut exécuter des optimisations). Et exécutez votre point de repère plusieurs fois (peut-être 5 fois) et prenez la valeur médiane. Exécutez chaque micro-référence dans une nouvelle instance de machine virtuelle Java (appelez chaque nouvelle référence Java), sinon les effets d'optimisation de la machine virtuelle peuvent influer sur les tests en cours d'exécution. N'exécutez pas d'objets qui ne sont pas exécutés pendant la phase de préchauffage (cela pourrait déclencher le chargement et la recompilation des classes).

Il convient également de noter qu’il peut également être important d’analyser les résultats du micro-repère lors de la comparaison de différentes implémentations. Par conséquent, un test de signification doit être effectué.

En effet, la mise en œuvre A peut être plus rapide que la mise en œuvre lors de la plupart des exécutions du test de performance B. Mais <=> peut également avoir un spread plus élevé, de sorte que l’avantage en termes de performance mesuré de <=> ne sera pas significatif par rapport à <=>.

Il est donc également important d'écrire et d'exécuter un micro-test correctement, mais également de l'analyser correctement.

http://opt.sourceforge.net/ Java Micro Benchmark - tâches de contrôle requises pour déterminer la comparaison caractéristiques de performance du système informatique sur différentes plates-formes. Peut être utilisé pour guider les décisions d’optimisation et comparer différentes implémentations Java.

Pour ajouter aux autres excellents conseils, je tiens également compte des points suivants:

Pour certains processeurs (tels que la gamme Intel Core i5 avec TurboBoost), la température (et le nombre de cœurs actuellement utilisés, ainsi que leur pourcentage d'utilisation) affectent la vitesse d'horloge. Les processeurs étant synchronisés de manière dynamique, cela peut affecter vos résultats. Par exemple, si vous avez une application à un seul thread, la vitesse d'horloge maximale (avec TurboBoost) est supérieure à celle d'une application utilisant tous les cœurs. Cela peut donc interférer avec les comparaisons de performances mono et multi-thread sur certains systèmes. N'oubliez pas que la température et les volatilités affectent également la durée de conservation de la fréquence Turbo.

Peut-être un aspect plus fondamentalement important sur lequel vous avez un contrôle direct: assurez-vous de mesurer la bonne chose! Par exemple, si vous utilisez System.nanoTime() pour référencer un morceau de code particulier, placez les appels de l'affectation dans des emplacements appropriés pour éviter de mesurer des éléments qui ne vous intéressent pas. Par exemple, ne faites pas:

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

Le problème est que vous n'obtenez pas immédiatement l'heure de fin lorsque le code est terminé. Au lieu de cela, essayez ce qui suit:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top