Chapter 9.1 : Introduction (WIP)
L'évaluation de performance se fait par le biai d'un test qui permet, dans une certaine mesure d'évaluer la performance d'un programme ou d'une fonction lorsqu'il s'agit d'un test de performance réduit (ou micro-benchmark).
Cette méthode consiste simplement à chronométrer le temps d'exécution d'une fonction ou d'un programme.
Sauf que, et même si cela paraît raisonnable au premier abord, le temps d'exécution d'une fonction, ou pire, d'un programme complet dépend de nombreux facteurs.
Il y a bien sur, ceux qui dépendent directement de la fonction :
- l'algorithme utilisé : quelle est sa complexité ?
- l'implémentation :
- utilise-t-elle des tableaux alignés ?
- des variables globales ?
- de nombreux accès mémoire ?
- beaucoup d'allocation ?
- utilise-t-elle plusieurs threads ?
- etc
Ensuite, il y a la chaîne de compilation :
- quel compilateur utilise-t-on ?
- quel est la version de ce compilateur ?
- a-t-on utilisé des options d'optimisation ? si oui, lesquelles ?
Si il n'y avait que ces paramètres là, ce serait déjà pas mal, et on aurait exactement que ce l'on veut. Mais malheureusement, le test de performance lui-même peut changer les performances mesurée de notre fonction :
- quelles sont les valeurs données à la fonction ?
- elles doivent bien sur être initialisées mais avec quelles valeurs .
- est-ce que certaines valeurs produisent des comportements différents (temps de convergence, valeurs exotiques comme des NaN ou des nombres dénormalisés, voir cours Performance with Nan and other exotic values)
- quelle est la quantité de données utilisé pour tester la fonction ? des ko, Mo, Go ou plus ?
- combien d'appels à cette fonction faisons nous pour évaluer sa performance ? il n'est bien entendu important d'avoir plusieurs résultats pour en tirer une conclusion statistique, mais combien ?
- qu'utilise-t-on comme chronomètre ? clock ? rdtsc ? std::chrono ?
- quelles sont les options de compilation à utiliser pour compiler notre test de performance ? et celles à ne pas utiliser ?
- quelle doit être la durée de ce test de performance ? une seconde ? une minute ? plus ? moins ?
Pour ne rien arranger, la machine que l'on utilise pour nos tests influencera les résulats. D'une manière assez attendue :
- type de processeur :
- nombre de coeurs
- taille des caches
- avec de l'hyperthreading ?
- une architecture superscalaire ?
- type et quantité de RAM (DDR4, DDR5, etc), nombre de slots et comment ils sont banchés
- type de stocakage (disque dur classique, SSD, etc)
- type de GPU (si la fonction en a besoin)
Et pour finir, notre programme de test n'est certainement pas seul sur la machine de test :
- y-a-t il des programmes plus prioritaires que le notre ?
- est-ce que d'autres programmes utilisent les ressources intensément ?
- est-ce que l'OS à tendence à faire migrer des threads souvent ?
- est-ce que la machine est partagée entre plusieurs utilisateurs ?
Si on fait le compte, on constate que la majorité des paramètres qui influenceront notre test de performance ne sont pas du fait de notre fonction.
C'est pourquoi, il est important de toujours se rappeler, qu'un test de performance, évalue :
- d'une certaine manière (avec un chrono plus ou moins mal fichu)
- l'implémentation (plus ou moins bonne)
- d'un algo (plus ou moins pertinent)
- compilé (plus ou moins bien)
- sur une machine (plus ou moins fiable)
- avec un ensemble de données définit (plus ou moins représentatives du problème posé)
- pendant un certain temps (plus ou moins judicieux)
- sur un OS (plus ou moins pénible)
Bref, nous devons insister sur le fait que développer un test de performance, c'est faire des choix (pas toujours judicieux voire préjudiciable), et si la conclusion ne tient pas compte de ces choix l'étude n'est pas sérieuse.
Ce n'est qu'un premier jet. On peut certainement faire mieux. Et il faudra aller plus en détail avec des exemples pour mettre en lumière les fourberies des uns et des autres. |