26.6. timeit — Mesurer le temps d’exécution de fragments de code

Nouveau dans la version 2.3.

Code source : Lib/timeit.py


Ce module fournit une façon simple de mesurer le temps d’exécution de fragments de code Python. Il expose une Interface en ligne de commande ainsi qu’une interface Python. Ce module permet d’éviter un certain nombre de problèmes classiques liés à la mesure des temps d’exécution. Voir par exemple à ce sujet l’introduction par Tim Peters du chapitre « Algorithmes » dans le livre Python Cookbook, aux éditions O’Reilly.

26.6.1. Exemples simples

L’exemple suivant illustre l’utilisation de l”Interface en ligne de commande afin de comparer trois expressions différentes :

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop

L”Interface Python peut être utilisée aux mêmes fins avec :

>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.8187260627746582
>>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
0.7288308143615723
>>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)
0.5858950614929199

Note however that timeit will automatically determine the number of repetitions only when the command-line interface is used. In the Exemples section you can find more advanced examples.

26.6.2. Interface Python

Ce module définit une classe publique ainsi que trois fonctions destinées à simplifier son usage :

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000)

Create a Timer instance with the given statement, setup code and timer function and run its timeit() method with number executions.

Nouveau dans la version 2.6.

timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=3, number=1000000)

Create a Timer instance with the given statement, setup code and timer function and run its repeat() method with the given repeat count and number executions.

Nouveau dans la version 2.6.

timeit.default_timer()

Define a default timer, in a platform-specific manner. On Windows, time.clock() has microsecond granularity, but time.time()”s granularity is 1/60th of a second. On Unix, time.clock() has 1/100th of a second granularity, and time.time() is much more precise. On either platform, default_timer() measures wall clock time, not the CPU time. This means that other processes running on the same computer may interfere with the timing.

class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>)

Classe permettant de mesurer le temps d’exécution de fragments de code.

The constructor takes a statement to be timed, an additional statement used for setup, and a timer function. Both statements default to 'pass'; the timer function is platform-dependent (see the module doc string). stmt and setup may also contain multiple statements separated by ; or newlines, as long as they don’t contain multi-line string literals.

To measure the execution time of the first statement, use the timeit() method. The repeat() method is a convenience to call timeit() multiple times and return a list of results.

Modifié dans la version 2.6: The stmt and setup parameters can now also take objects that are callable without arguments. This will embed calls to them in a timer function that will then be executed by timeit(). Note that the timing overhead is a little larger in this case because of the extra function calls.

timeit(number=1000000)

Mesure le temps number exécution de l’instruction principale. Ceci exécute l’instruction de mise en place une seule fois puis renvoie un flottant correspondant au temps nécessaire à l’exécution de l’instruction principale à plusieurs reprises, mesuré en secondes. L’argument correspond au nombre d’itérations dans la boucle, par défaut un million. L’instruction principale, l’instruction de mise en place et la fonction de chronométrage utilisée sont passées au constructeur.

Note

By default, timeit() temporarily turns off garbage collection during the timing. The advantage of this approach is that it makes independent timings more comparable. This disadvantage is that GC may be an important component of the performance of the function being measured. If so, GC can be re-enabled as the first statement in the setup string. For example:

timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()
repeat(repeat=3, number=1000000)

Appelle timeit() plusieurs fois.

Cette fonction d’agrément appelle timeit() à plusieurs reprises et renvoie une liste de résultats. Le premier argument spécifie le nombre d’appels à timeit(). Le second argument spécifie l’argument number de timeit().

Note

Il est tentant de vouloir calculer la moyenne et l’écart-type des résultats et notifier ces valeurs. Ce n’est cependant pas très utile. En pratique, la valeur la plus basse donne une estimation basse de la vitesse maximale à laquelle votre machine peut exécuter le fragment de code spécifié ; les valeurs hautes de la liste sont typiquement provoquées non pas par une variabilité de la vitesse d’exécution de Python, mais par d’autres processus interférant avec la précision du chronométrage. Le min() du résultat est probablement la seule valeur à laquelle vous devriez vous intéresser. Pour aller plus loin, vous devriez regarder l’intégralité des résultats et utiliser le bon sens plutôt que les statistiques.

print_exc(file=None)

Helper to print a traceback from the timed code.

Usage typique :

t = Timer(...)       # outside the try/except
try:
    t.timeit(...)    # or t.repeat(...)
except:
    t.print_exc()

The advantage over the standard traceback is that source lines in the compiled template will be displayed. The optional file argument directs where the traceback is sent; it defaults to sys.stderr.

26.6.3. Interface en ligne de commande

Lorsque le module est appelé comme un programme en ligne de commande, la syntaxe suivante est utilisée :

python -m timeit [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement ...]

Les options suivantes sont gérées :

-n N, --number=N

nombre d’exécutions de l’instruction statement

-r N, --repeat=N

how many times to repeat the timer (default 3)

-s S, --setup=S

instruction exécutée une seule fois à l’initialisation (pass par défaut)

-t, --time

use time.time() (default on all platforms but Windows)

-c, --clock

use time.clock() (default on Windows)

-v, --verbose

print raw timing results; repeat for more digits precision

-h, --help

affiche un court message d’aide puis quitte

Une instruction sur plusieurs lignes peut être donnée en entrée en spécifiant chaque ligne comme un argument séparé. Indenter une ligne est possible en encadrant l’argument de guillemets et en le préfixant par des espaces. Plusieurs -s sont gérées de la même façon.

Si -n n’est pas donnée, le nombre de boucles adapté est déterminé automatiquement en essayant les puissances de 10 successives jusqu’à ce que le temps total d’exécution dépasse 0,2 secondes.

default_timer() measurations can be affected by other programs running on the same machine, so the best thing to do when accurate timing is necessary is to repeat the timing a few times and use the best time. The -r option is good for this; the default of 3 repetitions is probably enough in most cases. On Unix, you can use time.clock() to measure CPU time.

Note

There is a certain baseline overhead associated with executing a pass statement. The code here doesn’t try to hide it, but you should be aware of it. The baseline overhead can be measured by invoking the program without arguments, and it might differ between Python versions. Also, to fairly compare older Python versions to Python 2.3, you may want to use Python’s -O option (see Optimizations) for the older versions to avoid timing SET_LINENO instructions.

26.6.4. Exemples

Il est possible de fournir une instruction de mise en place exécutée une seule fois au début du chronométrage :

$ python -m timeit -s 'text = "sample string"; char = "g"'  'char in text'
10000000 loops, best of 3: 0.0877 usec per loop
$ python -m timeit -s 'text = "sample string"; char = "g"'  'text.find(char)'
1000000 loops, best of 3: 0.342 usec per loop
>>> import timeit
>>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')
0.41440500499993504
>>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')
1.7246671520006203

La même chose peut être réalisée en utilisant la classe Timer et ses méthodes :

>>> import timeit
>>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')
>>> t.timeit()
0.3955516149999312
>>> t.repeat()
[0.40193588800002544, 0.3960157959998014, 0.39594301399984033]

Les exemples qui suivent montrent comment chronométrer des expressions sur plusieurs lignes. Nous comparons ici le coût d’utilisation de hasattr() par rapport à try/except pour tester la présence ou l’absence d’attributs d’un objet :

$ python -m timeit 'try:' '  str.__nonzero__' 'except AttributeError:' '  pass'
100000 loops, best of 3: 15.7 usec per loop
$ python -m timeit 'if hasattr(str, "__nonzero__"): pass'
100000 loops, best of 3: 4.26 usec per loop

$ python -m timeit 'try:' '  int.__nonzero__' 'except AttributeError:' '  pass'
1000000 loops, best of 3: 1.43 usec per loop
$ python -m timeit 'if hasattr(int, "__nonzero__"): pass'
100000 loops, best of 3: 2.23 usec per loop
>>> import timeit
>>> # attribute is missing
>>> s = """\
... try:
...     str.__nonzero__
... except AttributeError:
...     pass
... """
>>> timeit.timeit(stmt=s, number=100000)
0.9138244460009446
>>> s = "if hasattr(str, '__bool__'): pass"
>>> timeit.timeit(stmt=s, number=100000)
0.5829014980008651
>>>
>>> # attribute is present
>>> s = """\
... try:
...     int.__nonzero__
... except AttributeError:
...     pass
... """
>>> timeit.timeit(stmt=s, number=100000)
0.04215312199994514
>>> s = "if hasattr(int, '__bool__'): pass"
>>> timeit.timeit(stmt=s, number=100000)
0.08588060699912603

Afin de permettre à timeit d’accéder aux fonctions que vous avez définies, vous pouvez passer au paramètre setup une instruction d’importation:

def test():
    """Stupid test function"""
    L = []
    for i in range(100):
        L.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))