web-dev-qa-db-fra.com

Comment écrire un micro-benchmark correct en Java?

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

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

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

Connexes: L’évaluation du chronométrage est-elle acceptable?

821
John Nilsson

Conseils sur la rédaction de micro-points de repère des créateurs de Java HotSpot :

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

Règle 1: Incluez toujours 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: Toujours exécuter avec -XX:+PrintCompilation, -verbose:gc, etc., de sorte que vous puissiez vérifier que le compilateur et les autres la machine virtuelle Java ne fait pas un travail inattendu pendant votre phase de synchronisation.

Règle 2.1: Imprimez les messages au début et à la fin des phases de chronométrage et de préchauffage afin que vous puissiez vérifier qu'il n'y a pas de sortie de la règle 2 pendant la phase de chronométrage. .

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

Règle 4: Soyez conscient 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 est 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 dans la phase de minutage, car le compilateur risque de déverser du code et de recompiler le code, en partant de l'hypothèse optimiste antérieure selon laquelle le chemin ne serait pas utilisé du tout. La règle 2 est 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 le code vous-même avant de formuler des théories sur ce qui rend quelque chose plus rapide ou plus lent.

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 -Xbatch pour sérialiser le compilateur avec l'application et envisagez de définir -XX:CICompilerCount=1 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 Xmx (suffisamment grand) sur Xms et utilisez UseEpsilonGC s'il est disponible.

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

744
Eugene Kuleshov

Je sais que cette question a été marquée comme une réponse, mais je voulais mentionner deux bibliothèques qui nous aident à écrire des micro-points de repère.

Calibre de Google

Didacticiels de prise en main

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

JMH d'OpenJDK

Didacticiels de prise en main

  1. éviter les pièges d'analyse comparative sur la machine virtuelle
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://Java-performance.info/jmh/
234
Aravind R. Yarram

Les points importants pour les benchmarks Java sont les suivants:

  • Réchauffez d'abord le JIT en exécutant le code plusieurs fois avant de le chronométrer
  • 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 deux itérations, il est judicieux de l'exécuter entre les tests afin que chaque test obtienne, espérons-le, un espace mémoire "propre". (Oui, gc() est plus un indice qu'une garantie, mais il est très probable que cela ramasse vraiment les ordures selon mon expérience.)
  • J'aime afficher les itérations et l'heure, ainsi qu'un score temps/itération qui peut être mis à l'échelle de manière à ce que le "meilleur" algorithme obtienne une note de 1,0 et que les autres soient notés de manière relative. Cela signifie que vous pouvez exécuter tous les algorithmes pendant une période assez longue, en faisant varier le nombre d'itérations et la durée, mais en obtenant des résultats comparables.

Je suis en train de bloguer à propos de la conception d'un framework de benchmarking dans .NET. J'ai un couple sur posts précédents qui pourra peut-être vous donner quelques idées - tout ne conviendra pas, bien sûr, mais certaines d'entre elles le seront peut-être.

83
Jon Skeet

jmh est un ajout récent à OpenJDK et a été écrit par certains ingénieurs en performance d'Oracle. Ça vaut vraiment le coup d'oeil.

Jmh est un harnais Java permettant de créer, d'exécuter et d'analyser des points de repère nano/micro/macro écrits en Java et dans d'autres langages destinés à la machine virtuelle Java.

Des informations très intéressantes enfouies dans les commentaires des exemples de tests .

Voir également:

47
assylias

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

Cela dépend de quoi vous essayez de tester.

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

21
Peter Lawrey

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

15
Peter Štibraný

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 trouvé des différences notables (5-10% parfois) dans le temps d'exécution du même algorithme dans différentes passes.

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 ces données sont fiables.

14
Kip

L'écriture de micro-points de repère en Java présente de nombreux pièges.

Premièrement: vous devez calculer avec toutes sortes d'événements qui prennent du temps plus ou moins au hasard: garbage collection, effets de cache (de système d'exploitation pour les fichiers et de 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 en sorte que votre point de repère s'exécute en 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'éléments qui ne sont pas exécutés dans la phase de préchauffage (car cela pourrait déclencher le chargement de classes et la recompilation).

13
Mnementh

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 pourrait être plus rapide que la mise en oeuvre B pendant la plupart des exécutions du test de performance. Cependant, A pourrait également avoir un spread plus élevé, de sorte que le bénéfice de performance mesuré de A n'aura aucune importance par rapport à B.

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

8
SpaceTrucker

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 utilisez 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. Gardez à l'esprit que la température et les volatilités affectent également la durée de maintien 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 analyser un bit de code particulier, placez les appels de l'affectation à des emplacements appropriés pour éviter de mesurer des éléments qui ne vous intéressent pas. faire:

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");
7
Sina Madani

http://opt.sourceforge.net/ Java Micro Benchmark - tâches de contrôle nécessaires pour déterminer les caractéristiques de performances comparatives du système informatique sur différentes plateformes. Peut être utilisé pour guider les décisions d'optimisation et pour comparer différentes implémentations de Java.

7
Yuriy