18.2. Utilisation du module timeit
La chose la plus importante que vous devez savoir à propos de l'optimisation de code Python est que vous ne devez pas écrire vos propres fonctions de chronométrage.
Le chronométrage de petits segments de code est incroyablement complexe. Combien de temps processeur est consacré par votre
ordinateur à l'exécution de code ? Y-a-t-il des choses tournant en tâche de fond ? En êtes vous sûr ? Tous les ordinateurs
récents ont des processus s'exécutant en tâche de fond, certains tout le temps, d'autres de manière intermittente. Des tâches
cron s'exécutent à intervalles réguliers, des services en tâche de fond se «réveillent» pour accomplir des tâches comme relever votre courrier électronique, se connecter à des serveurs de messagerie instantanée,
vérifier les mises à jour disponibles, rechercher des virus, regarder si un disque a été inséré dans votre lecteur CD dans
les 100 dernières nanosecondes etc. Avant de démarrer vos tests de chronométrage, fermez tous ces services et déconnectez-vous
du réseau. Ensuite, fermez tout ce que vous avez oublié de fermer la première fois, puis fermez le service qui vérifie de
manière incessante si vous êtes connecté au réseau, puis...
Il y a aussi la question des variations introduites par le framework de chronométrage lui-même. L'interpréteur Python a-t-il un cache des tables de méthodes ? Un cache des blocs de code compilés ? Des expressions régulières ? Votre code produit-il
des effets de bord si il est exécuté plus d'une fois ? N'oubliez pas qu'il s'agit de fractions de secondes et que des petites
erreurs dans votre framework de chronométrage produirons des distorsions irréparables des résultats
La communauté Python a un proverbe : «Python est livré avec les piles.» N'écrivez pas votre propre framework de chronométrage. Python 2.3 est livré avec un très bon framework appelé timeit.
Exemple 18.2. Présentation de timeit
Si vous ne l’avez pas déjà fait, vous pouvez télécharger cet exemple ainsi que les autres exemples du livre.
>>> import timeit
>>> t = timeit.Timer("soundex.soundex('Pilgrim')",
... "import soundex")
>>> t.timeit()
8.21683733547
>>> t.repeat(3, 2000000)
[16.48319309109, 16.46128984923, 16.44203948912]
|
Le module timeit définit une classe, Timer, qui prend deux arguments. Les deux arguments sont des chaînes. Le premier argument est l'instruction que nous voulons chronométrer,
dans le cas présent nous chronométrons un appel à la fonction Soundex du module soundex avec pour argument 'Pilgrim'. Le deuxième argument de la classe Timer est l'instruction d'importation qui met en place l'environnement de l'instruction. En interne, timeit met en place un environnement virtuel isolé, exécute l'instruction de mise en place (importation du module soundex), puis compile et exécute l'instruction à chronométrer (appel de la fonction Soundex).
|
|
Une fois que nous avons l'objet Timer, la chose la plus simple à faire est d'appeler timeit(), qui appelle notre fonction 1 million de fois et retourne le nombre de secondes que cela a pris.
|
|
L'autre méthode importante de l'objet Timer est repeat(), qui prend deux arguments optionnels. Le premier argument est le nombre de répétition du test et le second est le nombre
de fois que l'instruction sera exécutée pour chaque test. Les deux arguments sont optionnels, leurs valeurs par défaut sont
respectivement 3 et 1000000. La méthode repeat() retourne une liste du temps que chaque cycle de test a pris, en secondes.
|
 |
| Vous pouvez utiliser le module timeit en ligne de commande pour tester un programme Python existant sans en modifier le code. Voir http://docs.python.org/lib/node396.html pour la documentation des paramètres de ligne de commande.
|
Notez que repeat() retourne une liste de temps. Les temps ne seront pratiquement jamais identiques, à cause des variations du temps processeur
alloué à l'interpréteur Python (et de toutes les tâches de fond dont on ne peut pas se débarrasser). Il est tentant de se dire «Prenons la moyenne et considérons que c'est le nombre correct.»
En fait, c'est presque certainement faux. Les tests qui ont pris plus de temps ne l'ont pas fait à cause de variations dans
notre code ou dans l'interpréteur Python, ils ont pris plus de temps à cause des tâches de fond, ou d'autres facteurs externes à l'interpréteur Python que nous ne pouvons pas entièrement éliminer. Si les différents résultats varient en pourcentage de plus de quelques points,
il y a trop de variation pour que nous puissions nous y fier. Sinon, c'est le temps minimum qu'il faut prendre en compte et
ne pas tenir compte du reste..
Python a une fonction min très utile qui prend une liste et retourne la valeur la plus petite de la liste :
 |
| Le module timeit ne fonctionne que si vous savez déjà quelle partie de votre code optimiser. Si vous avez un programme Python plus grand et que vous ne savez pas où se trouve le problème de performances, allez voir le module hotshot. |