1.3.3 : Reproductibilité du benchmark



  • Privilégier l'étude de benchmarks de longue durée pour limiter l'impact des comportements transitoires (ex: "chauffe" des caches) et des perturbations extérieures du système (ex: tâches de fond qu'on ne peut arrêter) sur les résultats.
    • Pour se placer hors de la zone des comportements transitoires, il est utile d'étudier comment l'indicateur de performances utilisé (ex: temps d'exécution) varie avec la taille du problème. Dans le cas usuel où il suit une loi affine, on veut se placer à une taille de problème telle que l'impact de la composante affine (ex: temps d'exécution à taille de problème nulle) sur le résultat est négligeable.
    • Pour réduire l'impact des perturbations, on doit choisir une taille de problème telle que l'indicateur choisi fluctue suffisamment peu d'une exécution de benchmark à une autre. Par exemple, si on mesure des temps d'exécution qui varient de 0.1s entre exécutions et on veut une mesure précise à 10% près, alors l'exécution du benchmark doit durer au moins quelques secondes.
    • Attention cependant au fait que certaines caractéristiques du benchmark peuvent dépendre de la taille du problème de façon non-affine (algorithmes de tris, combinaisons quadratiques ou cubiques d'éléments du jeu de données d'entrée, "effets de seuil" liés au dépassement de la taille d'un cache...). Dans ce cas, l'extrapolation de données obtenues dans une configuration modifiée à la configuration réaliste n'est pas triviale, et il vaut mieux minuter le temps cumulé d'un grand nombre d'exécutions qu'augmenter la taille du problème.
    • On essaiera aussi souvent, pour des raisons pratiques, de rester sous une limite haute qui dépend de l'utilisation du benchmark. Par exemple, au-delà de quelques minutes d'exécution, un benchmark est difficile à utiliser itérativement pendant le processus d'optimisation de code, car le développeur perd le fil de son idée en attendant que l'exécution se termine. Dans des processus automatisés et rares (intégration continue, nightly builds, validation mensuelle...), le budget temps sera souvent plus ample, et on pourra donc se permettre d'exécuter des benchmarks plus réalistes, plus complexes, ou plus nombreux.
    • Quand l'ajustement du temps d'exécution n'est pas possible (ex: étude de comportements transitoires), mesurer le phénomène un grand nombre de fois en traitant les observations par un raisonnement statistique. Garder à l'esprit en effectuant ce traitement que les mesures de performances logicielles ne suivent pas forcément une loi normale...
  • Appliquer une graine de générateur de nombres aléatoires constante, et plus généralement éviter dans le benchmark toute pratique qui fait varier le comportement d'une exécution à l'autre (effets basés sur l'heure système, répartition du travail entre threads non déterministe...)
    • En particulier, n'activer l'exécution multi-thread que quand c'est ce qu'on cherche à étudier, puisqu'elle est intrinsèquement moins déterministe que l'exécution séquentielle.
  • Eviter toute requête réseau (y compris implicite, p.ex. système de fichiers distribué) quand ce n'est pas le sujet de l'étude.
  • Eviter tout accès disque (en effectuant si possible les entrées/sorties sur un ramdisk) quand ce n'est pas le sujet de l'étude.
    • Si il faut choisir entre les deux, le disque est préférable au réseau, puisqu'il n'est pas partagé entre plusieurs machines.
  • Limiter les appels système non essentiels (ex: logging) qui augmentent l'impact du système d'exploitation sur les performances.