python:benchmark:operators

====== Opérateurs ====== ===== Opérateurs mathématiques ===== 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> # nombres entiers 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 100 -s "i=4" "i ** 4.0" 1000000 loops, best of 100: 0.162 usec per loop </Code> <WRAP group> <WRAP third column> <gchart 200x170 hbar center "Temps (int)"> addition = 327 soustraction = 318 multiplication = 318 division= 381 reste = 692 div. entière = 697 puissance = 2910 </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> Addition, soustraction, multiplication **et division** vont strictement à la même vitesse. 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. 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==== <Code> python -m timeit -n 1000000 -r 10 -s "i=0" "i = i+1" 1000000 loops, best of 10: 0.0958 usec per loop python -m timeit -n 1000000 -r 10 -s "i=0" "i += 1" 1000000 loops, best of 10: 0.0958 usec per loop </Code> <gchart 270x130 hbar center "Addition"> addition = 1 incrémentation = 1 </gchart> 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> import dis def add(i): i = i+1 def inc(i): i += 1 dis.dis(add) dis.dis(inc) 2 0 LOAD_FAST 0 (i) 3 LOAD_CONST 1 (1) 6 BINARY_ADD 7 STORE_FAST 0 (i) 10 LOAD_CONST 0 (None) 13 RETURN_VALUE 3 0 LOAD_FAST 0 (i) 3 LOAD_CONST 1 (1) 6 INPLACE_ADD 7 STORE_FAST 0 (i) 10 LOAD_CONST 0 (None) 13 RETURN_VALUE </Code>