Comment écrire un micro-benchmark correct en Java?

Feb 03 2009

Comment écrire (et exécuter) un micro-benchmark correct en Java?

Je recherche des exemples de code et des commentaires illustrant diverses choses à penser.

Exemple: le benchmark doit-il mesurer le temps / l'itération ou les itérations / le temps, et pourquoi?

Connexes: l' analyse comparative du chronomètre est-elle acceptable?

Réponses

802 12revs,12users61%EugeneKuleshov Feb 05 2009 at 03:49

Conseils sur l'écriture de micro-benchmarks des créateurs de Java HotSpot :

Règle 0: Lisez un article de bonne réputation sur les machines virtuelles Java et le micro-benchmarking. Un bon est Brian Goetz, 2005 . N'attendez pas trop des micro-benchmarks; ils ne mesurent qu'une gamme limitée de caractéristiques de performance JVM.

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 compilations avant la ou les phases de chronométrage. (Moins d'itérations sont acceptables lors de la phase de préchauffage. La règle de base est de plusieurs dizaines de milliers d'itérations de boucle interne.)

Règle 2: Toujours exécuter avec -XX:+PrintCompilation, -verbose:gcetc., de sorte que vous pouvez vérifier que le compilateur et d' autres parties de la machine virtuelle Java ne font pas le 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 d'échauffement, afin de pouvoir 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 -clientet -server, et OSR et les compilations régulières. Le -XX:+PrintCompilationdrapeau des rapports compilations OSR avec un arobase 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 régulier à l'OSR, si vous recherchez les meilleures performances.

Règle 4: Soyez conscient des effets d'initialisation. N'imprimez pas pour la première fois pendant 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 le chargement de classe spécifiquement (et dans ce cas, chargez uniquement 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ésoptimisation et de recompilation. Ne prenez aucun chemin de code pour la première fois dans la phase de minutage, car le compilateur risque de rejeter et de recompiler le code, sur la base d'une hypothèse optimiste antérieure selon laquelle le chemin n'allait pas être 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 produit. 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 benchmark sur une machine silencieuse et exécutez-le plusieurs fois, en éliminant les valeurs aberrantes. Utilisez -Xbatchpour sérialiser le compilateur avec l'application et envisagez de définir -XX:CICompilerCount=1pour 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) une valeur égale Xmset utilisez-la UseEpsilonGCsi elle est disponible.

Règle 8: Utilisez une bibliothèque pour votre benchmark car elle est probablement plus efficace et a déjà été déboguée dans ce seul but. Tels que JMH , Caliper ou Bill et les excellents benchmarks UCSD de Paul pour Java .

244 AravindYarram Dec 19 2010 at 06:35

Je sais que cette question a été marquée comme réponse mais je voulais mentionner deux bibliothèques qui nous aident à écrire des micro-benchmarks

Caliper de Google

Tutoriels de mise en route

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

JMH de OpenJDK

Tutoriels de mise en route

  1. Éviter les pièges de l'analyse comparative sur la machine virtuelle Java
  2. Utilisation de JMH pour le microbenchmarking Java
  3. Introduction à JMH
88 JonSkeet Feb 03 2009 at 00:46

Les éléments importants pour les benchmarks Java sont:

  • Faire chauffer le premier JIT en exécutant le code plusieurs fois avant de synchronisation , 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, c'est une bonne idée de l'exécuter entre les tests, afin que chaque test obtienne, espérons-le, un espace mémoire "propre" pour travailler. (Oui, gc()c'est plus un indice qu'une garantie, mais il est très probable que cela va vraiment ramasser les ordures selon mon expérience.)
  • J'aime afficher les itérations et le temps, et un score de temps / itération qui peut être mis à l'échelle de sorte que le "meilleur" algorithme obtienne un score de 1.0 et les autres sont notés de manière relative. Cela signifie que vous pouvez exécuter tous les algorithmes pendant une longue période, en variant à la fois le nombre d'itérations et le temps, tout en obtenant des résultats comparables.

Je suis juste en train de bloguer sur la conception d'un cadre d'analyse comparative en .NET. J'ai un deux des postes précédents qui peuvent être en mesure de vous donner quelques idées - pas tout sera approprié, bien sûr, mais certaines d' entre elles peut - être.

48 assylias Apr 03 2013 at 19:32

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

Le jmh est un harnais Java permettant de créer, d'exécuter et d'analyser des benchmarks nano / micro / macro écrits en Java et dans d'autres langages ciblant la JVM.

Des informations très intéressantes enfouies dans les commentaires des tests d'échantillons .

Voir également:

  • Éviter les pièges de l'analyse comparative sur la machine virtuelle Java
  • Discussion sur les principaux atouts de jmh .
23 PeterLawrey Feb 03 2009 at 02:54

Le benchmark doit-il mesurer le temps / l'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 temps / itération et si vous êtes intéressé par le débit , utilisez itérations / temps.

16 Kip Feb 03 2009 at 00:57

Si vous essayez de comparer deux algorithmes, effectuez au moins deux tests de performance 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 l'exécution du même algorithme dans différentes passes.

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

15 PeterŠtibraný Feb 03 2009 at 01:00

Assurez-vous que vous utilisez d'une manière ou d'une autre les résultats qui sont calculés dans un code de référence. Sinon, votre code peut être optimisé.

13 Mnementh Feb 03 2009 at 00:46

Il existe de nombreux pièges possibles pour l'écriture de micro-benchmarks en Java.

Premièrement: il faut calculer avec toutes sortes d'événements qui prennent du temps plus ou moins au hasard: ramasse-miettes, effets de mise en cache (d'OS pour les fichiers et de CPU pour la mémoire), IO etc.

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

Troisièmement: la JVM optimise votre code lors de son exécution. Ainsi, différentes exécutions dans la même instance JVM deviendront de plus en plus rapides.

Mes recommandations: faites fonctionner votre benchmark quelques secondes, ce qui est plus fiable qu'un runtime sur des millisecondes. Préchauffer la JVM (signifie exécuter le benchmark au moins une fois sans mesurer, que la JVM peut exécuter des optimisations). Et exécutez votre benchmark plusieurs fois (peut-être 5 fois) et prenez la valeur médiane. Exécutez chaque micro-benchmark dans une nouvelle instance JVM (appelez pour chaque benchmark nouveau Java), sinon les effets d'optimisation de la JVM peuvent influencer les tests en cours d'exécution ultérieurs. N'exécutez pas de choses qui ne sont pas exécutées pendant la phase de préchauffage (car cela pourrait déclencher le chargement de classe et la recompilation).

8 SpaceTrucker Jan 21 2013 at 21:04

Il convient également de noter qu'il peut également être important d'analyser les résultats du micro-benchmark 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 Apeut être plus rapide pendant la plupart des exécutions du benchmark que la mise en œuvre B. Mais Apeut également avoir un spread plus élevé, de sorte que l'avantage de performance mesuré An'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 SinaMadani Mar 20 2017 at 02:21

Pour ajouter aux autres excellents conseils, je tiens également compte de ce qui suit:

Pour certains processeurs (par exemple 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) affecte la vitesse d'horloge. Étant donné que les processeurs sont synchronisés de manière dynamique, cela peut affecter vos résultats. Par exemple, si vous avez une application monothread, la vitesse d'horloge maximale (avec TurboBoost) est plus élevée que pour 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 fondamental sur lequel vous avez un contrôle direct: assurez-vous de mesurer la bonne chose! Par exemple, si vous utilisez System.nanoTime()pour comparer un morceau de code particulier, placez les appels à l'affectation à des endroits qui ont du sens pour éviter de mesurer des choses 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é. Essayez plutôt ce qui suit:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");
7 Yuriy Dec 19 2010 at 06:22

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