Utiliser Python Timeit pour chronométrer votre code



Dans ce guide, nous allons explorer l’utilisation de la fonction timeit, un outil puissant intégré au module timeit de Python. Vous apprendrez à mesurer avec précision le temps d’exécution d’expressions et de fonctions Python simples.

L’analyse temporelle de votre code est essentielle pour évaluer l’efficacité de différentes sections et pour identifier les parties qui nécessitent une optimisation.

Nous débuterons par l’étude de la syntaxe de la fonction timeit, puis nous passerons à des exemples pratiques pour comprendre comment l’appliquer à des portions de code et à des fonctions dans votre projet Python. Plongeons-nous dans le sujet.

Comment utiliser la fonction timeit de Python

Le module timeit est inclus dans la bibliothèque standard de Python. Pour l’utiliser, commencez par l’importer :

import timeit

La syntaxe générale d’utilisation de la fonction timeit est la suivante :

timeit.timeit(stmt, setup, number)

Où :

  • stmt est le fragment de code dont vous voulez mesurer le temps d’exécution. Il peut s’agir d’une simple chaîne, d’une chaîne multiligne ou du nom d’une fonction.
  • setup, comme son nom l’indique, est un code qui n’est exécuté qu’une seule fois, généralement pour mettre en place l’environnement nécessaire à l’exécution de stmt. Par exemple, pour mesurer la création d’un tableau NumPy, l’importation de NumPy ferait partie de la configuration.
  • number indique le nombre de fois que stmt sera exécuté. Sa valeur par défaut est un million (1000000), mais vous pouvez la modifier à votre convenance.

Maintenant que vous êtes familiarisé avec la syntaxe, passons à des exemples concrets.

Mesure du temps d’expressions Python basiques

Dans cette section, nous allons nous concentrer sur la mesure du temps d’exécution d’expressions Python élémentaires avec timeit.

Ouvrez un interpréteur Python (REPL) et testez les exemples ci-dessous. Nous allons calculer le temps nécessaire pour effectuer des opérations d’exponentiation et de division euclidienne, en répétant l’exécution 10 000 et 100 000 fois.

Notez que l’instruction à évaluer est passée sous forme de chaîne de caractères, et que nous utilisons un point-virgule pour séparer les expressions.

>>> import timeit
>>> timeit.timeit('3**4;3//4',number=10000)
0.0004020999999738706

>>> timeit.timeit('3**4;3//4',number=100000)
0.0013780000000451764

Utilisation de timeit en ligne de commande

timeit peut également être utilisé directement depuis la ligne de commande. Voici la syntaxe pour l’équivalent de l’appel de la fonction timeit :

$ python -m timeit -n [nombre] -s [configuration] [instruction]
  • python -m timeit indique que nous utilisons timeit comme module principal.
  • -n est l’option spécifiant le nombre d’exécutions du code, équivalente à l’argument number de la fonction timeit().
  • L’option -s permet de définir le code de configuration.

Reprenons l’exemple précédent en utilisant la version en ligne de commande :

$ python -m timeit -n 100000 '3**4;3//4'
100000 loops, best of 5: 35.8 nsec per loop

Dans cet exemple, nous mesurons le temps d’exécution de la fonction intégrée len(). L’initialisation de la chaîne est le code de configuration, spécifié par l’option -s.

$ python -m timeit -n 100000 -s "string_1 = 'coding'" 'len(string_1)'
100000 loops, best of 5: 239 nsec per loop

Notez que la sortie indique le meilleur temps parmi 5 exécutions. En effet, lorsque timeit est utilisé en ligne de commande, l’option -r est par défaut fixée à 5, ce qui signifie que l’exécution de l’instruction (stmt) est répétée cinq fois et seul le meilleur temps est conservé.

Analyse de méthodes d’inversion de chaînes avec timeit

Lors de la manipulation de chaînes de caractères en Python, il est fréquent de devoir les inverser. Les deux techniques les plus courantes pour réaliser cette opération sont :

  • L’utilisation du découpage de chaînes.
  • L’emploi de la fonction reversed() combinée à la méthode join().

Inversion de chaînes via le découpage

Explorons le fonctionnement du découpage de chaînes et comment il peut être utilisé pour inverser une chaîne. L’expression une_chaîne[début:fin] renvoie une partie de la chaîne, allant de l’index début jusqu’à l’index fin-1. Prenons un exemple.

Considérons la chaîne ‘Python’. Elle est de longueur 6, et les indices vont de 0 à 5.

>>> string_1 = 'Python'

Si vous spécifiez les indices de début et de fin, la partie de la chaîne extraite commencera à l’indice de début et se terminera juste avant l’indice de fin. Par exemple, string_1[1:4] renvoie ‘yth’.

>>> string_1 = 'Python'
>>> string_1[1:4]
'yth'

Si l’indice de début est omis, la tranche commencera à l’index 0 et s’étendra jusqu’à fin-1.

Ici, l’indice de fin est 3, donc la tranche comprend les indices 0, 1 et 2.

>>> string_1[:3]
'Pyt'

Si l’indice de fin est omis, la tranche débutera à l’indice de début et s’étendra jusqu’à la fin de la chaîne.

>>> string_1[1:]
'ython'

Omettre les deux indices permet d’obtenir une copie complète de la chaîne.

>>> string_1[::]
'Python'

Il est également possible d’ajouter un pas. Par exemple, en définissant les valeurs de début, de fin et de pas à 1, 5 et 2 respectivement, on obtient une tranche commençant à 1, allant jusqu’à 4 (non inclus) en prenant un caractère sur deux.

>>> string_1[1:5:2]
'yh'

Un pas négatif permet de parcourir la chaîne en sens inverse. Par exemple, string_1[5:2:-2] donne la tranche :

>>> string_1[5:2:-2]
'nh'

Pour inverser une chaîne, il suffit donc de ne pas spécifier d’indices de début et de fin et de définir le pas à -1 :

>>> string_1[::-1]
'nohtyP'

En résumé, chaîne[::-1] renvoie une copie inversée de la chaîne.

Inversion de chaînes avec des fonctions intégrées et méthodes

La fonction intégrée reversed() de Python retourne un itérateur qui parcourt la chaîne en sens inverse.

>>> string_1 = 'Python'
>>> reversed(string_1)
<reversed object at 0x00BEAF70>

Vous pouvez alors utiliser une boucle for pour parcourir cet itérateur et accéder aux éléments de la chaîne dans l’ordre inverse.

for char in reversed(string_1):
    print(char)

Voici la sortie :

# Output
n
o
h
t
y
P

Ensuite, vous pouvez faire appel à la méthode join() sur l’itérateur inverse, selon la syntaxe : <séparateur>.join(reversed(chaîne)).

L’exemple ci-dessous montre des exemples où les séparateurs sont respectivement un tiret et un espace.

>>> '-'.join(reversed(string1))
'n-o-h-t-y-P'
>>> ' '.join(reversed(string1))
'n o h t y P'

Dans notre cas, nous ne voulons aucun séparateur, donc nous définissons le séparateur à une chaîne vide pour obtenir une copie inversée de la chaîne :

>>> ''.join(reversed(string1))
'nohtyP'

L’expression ''.join(reversed(chaîne)) renvoie ainsi une copie inversée de la chaîne.

Comparaison des temps d’exécution avec timeit

Nous avons maintenant deux méthodes pour inverser une chaîne. Mais laquelle est la plus rapide ? Vérifions-le.

Dans un exemple précédent, nous avons mesuré le temps d’expressions simples sans configurer de code spécifique. Ici, nous inversons des chaînes Python. L’opération d’inversion est répétée le nombre de fois spécifié par l’argument number, tandis que le code de configuration, qui consiste à l’initialisation de la chaîne, n’est exécuté qu’une seule fois.

>>> import timeit
>>> timeit.timeit(stmt="string_1[::-1]", setup = "string_1 = 'Python'", number = 100000)
0.04951830000001678
>>> timeit.timeit(stmt = "''.join(reversed(string_1))", setup = "string_1 = 'Python'", number = 100000)
0.12858760000000302

Pour le même nombre d’exécutions, l’approche par découpage est plus rapide que celle utilisant join() et reversed().

Mesure du temps d’exécution de fonctions Python avec timeit

Dans cette section, nous allons apprendre à mesurer le temps d’exécution de fonctions Python avec timeit. Prenons l’exemple d’une fonction nommée hasDigit qui prend en argument une liste de chaînes et retourne la liste des chaînes contenant au moins un chiffre.

def hasDigit(somelist):
     str_with_digit = []
     for string in somelist:
         check_char = [char.isdigit() for char in string]
         if any(check_char):
            str_with_digit.append(string)
     return str_with_digit

Nous souhaitons évaluer le temps d’exécution de cette fonction hasDigit() à l’aide de timeit.

Identifions d’abord l’instruction à évaluer (stmt). Il s’agit de l’appel de la fonction hasDigit() avec une liste de chaînes en argument. Ensuite, nous devons définir le code de configuration. Qu’est-ce qu’il devrait comprendre ?

Pour que l’appel de fonction fonctionne correctement, le code de configuration doit inclure :

  • La définition de la fonction hasDigit().
  • L’initialisation de la liste de chaînes à passer en argument.

Définissons le code de configuration sous forme de chaîne :

setup = """
def hasDigit(somelist):
    str_with_digit = []
    for string in somelist:
      check_char = [char.isdigit() for char in string]
      if any(check_char):
        str_with_digit.append(string)
    return str_with_digit
thislist=['puffin3','7frost','blue']
     """

Maintenant, nous pouvons utiliser timeit pour mesurer le temps d’exécution de hasDigit() pour 100 000 exécutions.

import timeit
timeit.timeit('hasDigit(thislist)',setup=setup,number=100000)
# Output
0.2810094920000097

Conclusion

Vous avez appris à utiliser la fonction timeit de Python pour mesurer le temps d’expressions, de fonctions et d’autres objets appelables. Cet outil est précieux pour comparer différents algorithmes, évaluer l’impact de modifications de code et pour optimiser les performances de vos projets.

Récapitulons ce que nous avons vu dans ce tutoriel. La fonction timeit() s’utilise avec la syntaxe timeit.timeit(stmt=...,setup=...,number=...). Alternativement, timeit peut être utilisé en ligne de commande pour évaluer des portions de code courtes.

Pour aller plus loin, vous pouvez explorer d’autres outils de profilage Python, comme line-profiler pour analyser le temps d’exécution ligne par ligne, et memprofiler pour étudier l’utilisation de la mémoire.

Ensuite, vous pouvez apprendre à calculer le décalage horaire en Python.