====== Différences ====== Ci-dessous, les différences entre deux révisions de la page.
|
python:benchmark:operators [2015/03/06 18:41] nliautaud créée |
python:benchmark:operators [2015/03/07 19:28] (Version actuelle) nliautaud |
||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| ====== Opérateurs ====== | ====== Opérateurs ====== | ||
| - | ===== Division et multiplication ===== | + | ===== Opérateurs mathématiques ===== |
| - | Il est bien connu que les multiplications sont plus rapides à calculer que les divisions, on est donc facilement tenté de convertir ses diviseurs en multiplicateurs. Vérifions. | + | Les opérateurs mathématiques en python se comptent au nombre de sept : addition, soustraction, multiplication, division, reste de la division (modulo), division entière et puissance. La logique voudrait que les trois premiers soient très rapides, mais que la division et les autres soient bien plus lents. Jettons-y un oeil, en testant des nombres entiers, des nombres flottants, et les deux mélangés. |
| + | |||
| + | <WRAP center info> | ||
| + | Lorsque le compilateur est capable de déterminer le résultat à l'avance, par exemple en notant directement ''4 / 4'', le résultat est précalculé à la compilation. La variable ''i'' sert ici à forcer le programme à interpréter l'opération. | ||
| + | </WRAP> | ||
| <Code> | <Code> | ||
| - | python -m timeit -n 1000000 -r 10 -s "i=.83" "i * .25" | + | # nombres entiers |
| - | 1000000 loops, best of 10: 0.0805 usec per loop | + | python -m timeit -n 1000000 -r 100 -s "i=4" "i + 4" |
| + | 1000000 loops, best of 100: 0.0327 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i - 4" | ||
| + | 1000000 loops, best of 100: 0.0318 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i * 4" | ||
| + | 1000000 loops, best of 100: 0.0318 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i / 4" | ||
| + | 1000000 loops, best of 100: 0.0381 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i % 4" | ||
| + | 1000000 loops, best of 100: 0.0692 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i // 4" | ||
| + | 1000000 loops, best of 100: 0.0697 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i ** 4" | ||
| + | 1000000 loops, best of 100: 0.291 usec per loop | ||
| + | |||
| + | # nombres flottants | ||
| + | python -m timeit -n 1000000 -r 100 -s "i=4.0" "i + 4.0" | ||
| + | 1000000 loops, best of 100: 0.0335 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4.0" "i - 4.0" | ||
| + | 1000000 loops, best of 100: 0.0335 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4.0" "i * 4.0" | ||
| + | 1000000 loops, best of 100: 0.0325 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4.0" "i / 4.0" | ||
| + | 1000000 loops, best of 100: 0.0335 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4.0" "i % 4.0" | ||
| + | 1000000 loops, best of 100: 0.0526 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4.0" "i // 4.0" | ||
| + | 1000000 loops, best of 100: 0.112 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4.0" "i ** 4.0" | ||
| + | 1000000 loops, best of 100: 0.102 usec per loop | ||
| + | |||
| + | # entiers et flottants | ||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i + 4.0" | ||
| + | 1000000 loops, best of 100: 0.0841 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i - 4.0" | ||
| + | 1000000 loops, best of 100: 0.0819 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i * 4.0" | ||
| + | 1000000 loops, best of 100: 0.0829 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i / 4.0" | ||
| + | 1000000 loops, best of 100: 0.0837 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i % 4.0" | ||
| + | 1000000 loops, best of 100: 0.106 usec per loop | ||
| + | |||
| + | python -m timeit -n 1000000 -r 100 -s "i=4" "i // 4.0" | ||
| + | 1000000 loops, best of 100: 0.166 usec per loop | ||
| - | python -m timeit -n 1000000 -r 10 -s "i=.83" "i / 4.0" | + | python -m timeit -n 1000000 -r 100 -s "i=4" "i ** 4.0" |
| - | 1000000 loops, best of 10: 0.0329 usec per loop | + | 1000000 loops, best of 100: 0.162 usec per loop |
| </Code> | </Code> | ||
| - | <gchart 270x130 hbar center "% vs /"> | + | <WRAP group> |
| - | division = 8.05 | + | <WRAP third column> |
| - | multiplication = 3.29 | + | <gchart 200x170 hbar center "Temps (int)"> |
| + | addition = 327 | ||
| + | soustraction = 318 | ||
| + | multiplication = 318 | ||
| + | division= 381 | ||
| + | reste = 692 | ||
| + | div. entière = 697 | ||
| + | puissance = 2910 | ||
| </gchart> | </gchart> | ||
| + | </WRAP> | ||
| + | <WRAP third column> | ||
| + | <gchart 200x170 hbar center "Temps (float)"> | ||
| + | addition = 335 | ||
| + | soustraction = 335 | ||
| + | multiplication = 325 | ||
| + | division = 335 | ||
| + | reste = 526 | ||
| + | div. entière = 1120 | ||
| + | puissance = 1020 | ||
| + | </gchart> | ||
| + | </WRAP> | ||
| + | <WRAP third column> | ||
| + | <gchart 200x170 hbar center "Temps (int et float)"> | ||
| + | addition = 841 | ||
| + | soustraction = 819 | ||
| + | multiplication = 829 | ||
| + | division = 837 | ||
| + | reste = 1060 | ||
| + | div. entière = 1660 | ||
| + | puissance = 1620 | ||
| + | </gchart> | ||
| + | </WRAP> | ||
| + | </WRAP> | ||
| - | La multiplication est bien environ deux fois plus rapide que la division. | + | Addition, soustraction, multiplication **et division** vont strictement à la même vitesse. |
| - | <WRAP center tip> | + | Le reste de la division (modulo) est à peine un peu plus lent, et la division entière est pareillement rapide sur des nombres entiers. Lorsqu'il y a des nombres flottants en jeu, la division entière et la puissance sont beaucoup plus longs. Et la puissance de nombres entiers crève littéralement le plafond. |
| - | Aux rares occasions où le compilateur est capable de déterminer le résultat, l'opération sera précalculée. | + | |
| - | </WRAP> | + | |
| + | Le mélange de types est plus de deux fois plus lent. | ||
| + | |||
| + | <WRAP center tip > | ||
| + | La vitesse d'exécution des opérateurs arithmétiques de base est entièrement liée aux optimisations que peut faire le compilateur, et, en bout de chaîne, au processeur qui les execute. Dans d'autres contextes (version de python plus ancienne, système différent, etc.), les résultats pourraient être différents et, par exemple, avaliser l'idée que la division soit beaucoup plus lente que la multiplication, mais ce n'est plus le cas lors de mes derniers essais. | ||
| + | </WRAP> | ||
| ====Incrémentation et addition==== | ====Incrémentation et addition==== | ||
| Ligne 40: | Ligne 138: | ||
| </gchart> | </gchart> | ||
| - | Incrémentation et addition sont tout aussi rapides. Il s'agit intrinsèquement de la même opération, seulement une différence de syntaxe. Le compilateur produit le même //bytecode// pour les deux : | + | Incrémentation et addition sont tout aussi rapides. |
| + | |||
| + | <WRAP center tip> | ||
| + | Python n'a pas de réelle incrémentation comme ''++'' : les nombres en python étant inalterables, le seul moyen de changer la valeur d'une variable est de lui assigner une autre valeur. Il s'agit donc intrinsèquement de la même opération, le ''+='' étant seulement un raccourci de syntaxe. | ||
| + | </WRAP> | ||
| + | |||
| + | |||
| + | Le compilateur produit le même //bytecode// pour les deux : | ||
| <Code python> | <Code python> | ||
| import dis | import dis | ||
| - | def add(i): | + | def add(i): i = i+1 |
| - | i = i + 1 | + | def inc(i): i += 1 |
| - | + | ||
| - | def inc(i): | + | |
| - | i += 1 | + | |
| - | + | ||
| dis.dis(add) | dis.dis(add) | ||
| + | dis.dis(inc) | ||
| + | |||
| 2 0 LOAD_FAST 0 (i) | 2 0 LOAD_FAST 0 (i) | ||
| 3 LOAD_CONST 1 (1) | 3 LOAD_CONST 1 (1) | ||
| Ligne 57: | Ligne 160: | ||
| 10 LOAD_CONST 0 (None) | 10 LOAD_CONST 0 (None) | ||
| 13 RETURN_VALUE | 13 RETURN_VALUE | ||
| - | dis.dis(inc) | + | 3 0 LOAD_FAST 0 (i) |
| - | 2 0 LOAD_FAST 0 (i) | + | |
| 3 LOAD_CONST 1 (1) | 3 LOAD_CONST 1 (1) | ||
| 6 INPLACE_ADD | 6 INPLACE_ADD | ||