La raison (quelque peu inattendue) de vos résultats est que Python semble plier les expressions constantes impliquant la multiplication et l'exponentiation en virgule flottante, mais pas la division. math.sqrt()
est tout à fait différent puisqu'il n'y a pas de bytecode pour lui et qu'il implique un appel de fonction.
Sur Python 2.6.5, le code suivant :
x1 = 1234567890.0 / 4.0
x2 = 1234567890.0 * 0.25
x3 = 1234567890.0 ** 0.5
x4 = math.sqrt(1234567890.0)
se compile avec les bytecodes suivants :
# x1 = 1234567890.0 / 4.0
4 0 LOAD_CONST 1 (1234567890.0)
3 LOAD_CONST 2 (4.0)
6 BINARY_DIVIDE
7 STORE_FAST 0 (x1)
# x2 = 1234567890.0 * 0.25
5 10 LOAD_CONST 5 (308641972.5)
13 STORE_FAST 1 (x2)
# x3 = 1234567890.0 ** 0.5
6 16 LOAD_CONST 6 (35136.418286444619)
19 STORE_FAST 2 (x3)
# x4 = math.sqrt(1234567890.0)
7 22 LOAD_GLOBAL 0 (math)
25 LOAD_ATTR 1 (sqrt)
28 LOAD_CONST 1 (1234567890.0)
31 CALL_FUNCTION 1
34 STORE_FAST 3 (x4)
Comme vous pouvez le constater, la multiplication et l'exponentiation ne prennent pas de temps puisqu'elles sont effectuées lors de la compilation du code. La division prend plus de temps puisqu'elle se fait au moment de l'exécution. La racine carrée n'est pas seulement l'opération la plus coûteuse des quatre sur le plan du calcul, elle entraîne également divers frais généraux que les autres n'ont pas à supporter (recherche d'attributs, appel de fonctions, etc.).
Si l'on élimine l'effet du pliage constant, il n'y a pas grand-chose qui sépare la multiplication de la division :
In [16]: x = 1234567890.0
In [17]: %timeit x / 4.0
10000000 loops, best of 3: 87.8 ns per loop
In [18]: %timeit x * 0.25
10000000 loops, best of 3: 91.6 ns per loop
math.sqrt(x)
est en fait un peu plus rapide que x ** 0.5
probablement parce qu'il s'agit d'un cas particulier de ce dernier et qu'il peut donc être réalisé plus efficacement, malgré les frais généraux :
In [19]: %timeit x ** 0.5
1000000 loops, best of 3: 211 ns per loop
In [20]: %timeit math.sqrt(x)
10000000 loops, best of 3: 181 ns per loop
modifier 2011-11-16 : Le pliage des expressions constantes est effectué par l'optimiseur peephole de Python. Le code source ( peephole.c
) contient le commentaire suivant qui explique pourquoi la division constante n'est pas pliée :
case BINARY_DIVIDE:
/* Cannot fold this operation statically since
the result can depend on the run-time presence
of the -Qnew flag */
return 0;
En -Qnew
permet d'activer la "vraie division" définie dans PEP 238 .