web-dev-qa-db-fra.com

Ce qui est plus rapide en Python: x **. 5 ou math.sqrt (x)?

Je me le demande depuis quelque temps. Comme le titre le dit, quelle est la plus rapide, la fonction réelle ou simplement augmenter à la moitié de la puissance?

METTRE À JOUR

Ce n’est pas une question d’optimisation prématurée. Il s’agit simplement de savoir comment fonctionne le code sous-jacent. Quelle est la théorie du fonctionnement du code Python?

J'ai envoyé un email à Guido van Rossum parce que je voulais vraiment connaître les différences entre ces méthodes.

Mon email:

Il existe au moins 3 façons de créer une racine carrée en Python: math.sqrt, le fichier Opérateur '**' et pow (x, .5). Je suis juste curieux de connaître les différences de la mise en œuvre de chacun de ces. En ce qui concerne l'efficacité qui est mieux?

Sa réponse:

pow et ** sont équivalents; math.sqrt ne fonctionne pas pour les nombres complexes, et des liens vers la fonction C sqrt (). Quant à savoir lequel est plus vite, je n'en ai aucune idée ...

154
Nope

Selon les commentaires, j'ai mis à jour le code:

import time
import math

def timeit1():
    s = time.time()
    for i in xrange(750000):
        z=i**.5
    print "Took %f seconds" % (time.time() - s)

def timeit2(arg=math.sqrt):
    s = time.time()
    for i in xrange(750000):
        z=arg(i)
    print "Took %f seconds" % (time.time() - s)

timeit1()
timeit2()

Maintenant, la fonction math.sqrt est directement dans un argument local, ce qui signifie qu’elle a la recherche la plus rapide possible. 

UPDATE: La version python semble avoir de l'importance ici. J'avais l'habitude de penser que timeit1 serait plus rapide, car lorsque python analyse "i **. 5", il sait syntaxiquement quelle méthode appeler (__pow__ ou une variante), il n'a donc pas besoin de passer par le temps de la recherche. que la variante math.sqrt fait. Mais je peux me tromper:

Python 2.5: 0.191000 vs. 0.224000

Python 2.6: 0.195000 vs. 0.139000

De plus, psyco semble mieux gérer math.sqrt:

Python 2.5 + Psyco 2.0: 0.109000 vs. 0.043000

Python 2.6 + Psyco 2.0: 0.128000 vs. 0.067000


| Interpreter    |  x**.5, |   sqrt, | sqrt faster, % |
|                | seconds | seconds |                |
|----------------+---------+---------+----------------|
| Python 3.2rc1+ |    0.32 |    0.27 |             19 |
| Python 3.1.2   |   0.136 |   0.088 |             55 |
| Python 3.0.1   |   0.155 |   0.102 |             52 |
| Python 2.7     |   0.132 |   0.079 |             67 |
| Python 2.6.6   |   0.121 |   0.075 |             61 |
| PyPy 1.4.1     |   0.083 |  0.0159 |            422 |
| Jython 2.5.1   |   0.132 |    0.22 |            -40 |
| Python 2.5.5   |   0.129 |   0.125 |              3 |
| Python 2.4.6   |   0.131 |   0.123 |              7 |
#+TBLFM: $4=100*($2-$3)/$3;%.0f

Résultats du tableau produits sur machine:

$ uname -vms
Linux #42-Ubuntu SMP Thu Dec 2 02:41:37 UTC 2010 x86_64
$ cat /proc/cpuinfo | grep 'model name' | head -1
model name      : Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz

Pour reproduire les résultats:

73
Claudiu
  • première règle d'optimisation: ne le faites pas
  • deuxième règle: ne le fais pas encore

Voici quelques timings (Python 2.5.2, Windows):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.445 usec per loop

$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.574 usec per loop

$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.727 usec per loop

Ce test montre que x**.5 est légèrement plus rapide que sqrt(x).

Pour le Python 3.0, le résultat est le contraire:

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
1000000 loops, best of 3: 0.803 usec per loop

$ \Python30\python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
1000000 loops, best of 3: 0.695 usec per loop

$ \Python30\python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
1000000 loops, best of 3: 0.761 usec per loop

math.sqrt(x) est toujours plus rapide que x**.5 sur une autre machine (Ubuntu, Python 2.6 et 3.1):

$ python -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.173 usec per loop
$ python -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.115 usec per loop
$ python -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.158 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "x**.5"
10000000 loops, best of 3: 0.194 usec per loop
$ python3.1 -mtimeit -s"from math import sqrt; x = 123" "sqrt(x)"
10000000 loops, best of 3: 0.123 usec per loop
$ python3.1 -mtimeit -s"import math; x = 123" "math.sqrt(x)"
10000000 loops, best of 3: 0.157 usec per loop
16
jfs

Combien de racines carrées effectuez-vous réellement? Êtes-vous en train d'essayer d'écrire un moteur graphique 3D en Python? Sinon, pourquoi choisir un code qui est crypté plutôt que facile à lire? La différence de temps est inférieure à ce que quiconque pourrait remarquer dans pratiquement toutes les applications que je pouvais prévoir. Je ne veux vraiment pas poser votre question, mais il semble que vous allez un peu trop loin avec une optimisation prématurée.

11
Kibbee

Dans ces micro-points de repère, math.sqrt sera plus lent, en raison du peu de temps nécessaire pour rechercher le carré dans l'espace de noms math. Vous pouvez l'améliorer légèrement avec 

 from math import sqrt

Malgré tout, quelques variations dans le temps montrent un léger avantage de performance (4-5%) pour "x **. 5".

fait intéressant, faire

 import math
 sqrt = math.sqrt

accéléré encore plus, à 1% de différence de vitesse, avec très peu de signification statistique.

Je vais répéter Kibbee et dire qu'il s'agit probablement d'une optimisation prématurée.

9
JimB

En python 2.6, la fonction (float).__pow__() utilise la fonction C pow() et les fonctions math.sqrt() utilisent la fonction C sqrt().

Dans le compilateur glibc, l’implémentation de pow(x,y) est assez complexe et elle est bien optimisée pour divers cas exceptionnels. Par exemple, appeler C pow(x,0.5) appelle simplement la fonction sqrt().

La différence de vitesse d'utilisation de .** ou math.sqrt est due aux wrappers utilisés autour des fonctions C et la vitesse dépend fortement des indicateurs d'optimisation/du compilateur C utilisé sur le système. 

Modifier:

Voici les résultats de l'algorithme de Claudiu sur ma machine. J'ai eu des résultats différents:

zoltan@Host:~$ python2.4 p.py 
Took 0.173994 seconds
Took 0.158991 seconds
zoltan@Host:~$ python2.5 p.py 
Took 0.182321 seconds
Took 0.155394 seconds
zoltan@Host:~$ python2.6 p.py 
Took 0.166766 seconds
Took 0.097018 seconds
6
zoli2k

en utilisant le code de Claudiu, sur ma machine, même avec "from math import sqrt" x **. 5 est plus rapide, mais utiliser psyco.full () sqrt (x) devient beaucoup plus rapide, au moins de 200%

4
Nope

Math.sqrt (x), probablement, car il est optimisé pour l’enracinement carré.

Les repères vous fourniront la réponse que vous recherchez.

3
strager

Pour ce que ça vaut (voir la réponse de Jim). Sur ma machine, exécutant Python 2.5:

PS C:\> python -m timeit -n 100000 10000**.5
100000 loops, best of 3: 0.0543 usec per loop
PS C:\> python -m timeit -n 100000 -s "import math" math.sqrt(10000)
100000 loops, best of 3: 0.162 usec per loop
PS C:\> python -m timeit -n 100000 -s "from math import sqrt" sqrt(10000)
100000 loops, best of 3: 0.0541 usec per loop
3
zdan

Quelqu'un a commenté la "racine carrée Newton-Raphson rapide" de Quake 3 ... Je l'ai implémentée avec des ctypes, mais c'est très lent par rapport aux versions natives. Je vais essayer quelques optimisations et des implémentations alternatives.

from ctypes import c_float, c_long, byref, POINTER, cast

def sqrt(num):
 xhalf = 0.5*num
 x = c_float(num)
 i = cast(byref(x), POINTER(c_long)).contents.value
 i = c_long(0x5f375a86 - (i>>1))
 x = cast(byref(i), POINTER(c_float)).contents.value

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num

Voici une autre méthode utilisant struct, qui sort environ 3.6x plus vite que la version ctypes, mais 1/10 de la vitesse de C.

from struct import pack, unpack

def sqrt_struct(num):
 xhalf = 0.5*num
 i = unpack('L', pack('f', 28.0))[0]
 i = 0x5f375a86 - (i>>1)
 x = unpack('f', pack('L', i))[0]

 x = x*(1.5-xhalf*x*x)
 x = x*(1.5-xhalf*x*x)
 return x * num
3
lunixbochs

Les résultats de Claudiu diffèrent des miens. J'utilise Python 2.6 sur Ubuntu sur une vieille machine P4 2.4Ghz ... Voici mes résultats:

>>> timeit1()
Took 0.564911 seconds
>>> timeit2()
Took 0.403087 seconds
>>> timeit1()
Took 0.604713 seconds
>>> timeit2()
Took 0.387749 seconds
>>> timeit1()
Took 0.587829 seconds
>>> timeit2()
Took 0.379381 seconds

sqrt est toujours plus rapide pour moi ... Même Codepad.org semble MAINTENANT d'accord pour dire que sqrt, dans le contexte local, est plus rapide ( http://codepad.org/6trzcM3j ). Codepad semble utiliser actuellement Python 2.5. Peut-être utilisaient-ils la version 2.4 ou une version plus ancienne lorsque Claudiu a répondu pour la première fois?

En fait, même en utilisant math.sqrt (i) à la place de arg (i), j'obtiens toujours de meilleurs temps pour sqrt. Dans ce cas, timeit2 () a pris entre 0,53 et 0,55 seconde sur ma machine, ce qui est toujours meilleur que les chiffres de 0,56 à 0,60 de timeit1.

Je dirais qu'en Python moderne, utilisez math.sqrt et amenez-le définitivement au contexte local, soit avec somevar = math.sqrt, soit avec math import SQL.

1
bobpaul

Vous voudrez peut-être aussi: racine carrée de Newton-Raphson rapide . Ne devrait pas prendre beaucoup pour convertir en Python.

0
Mitch Wheat

Le problème SQRMINSUM J'ai récemment résolu le problème, il est nécessaire de calculer la racine carrée à plusieurs reprises sur un grand jeu de données. Les 2 soumissions les plus anciennes de mon history , avant d’avoir effectué d’autres optimisations, diffèrent uniquement en remplaçant ** 0.5 par sqrt (), ce qui réduit le temps d’exécution de PyPy de 3,74 à 0,51 s. C'est presque le double de l'amélioration déjà massive de 400% mesurée par Claudiu.

0

L’optimisation de Pythonic est la lisibilité. Pour cela, je pense que l’utilisation explicite de la fonction sqrt est préférable. Cela dit, examinons quand même les performances.

J'ai mis à jour le code de Claudiu pour Python 3 et j'ai également rendu impossible l'optimisation des calculs (quelque chose qu'un bon compilateur Python pourrait faire à l'avenir):

from sys import version
from time import time
from math import sqrt, pi, e

print(version)

N = 1_000_000

def timeit1():
  z = N * e
  s = time()
  for n in range(N):
    z += (n * pi) ** .5 - z ** .5
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

def timeit2():
  z = N * e
  s = time()
  for n in range(N):
    z += sqrt(n * pi) - sqrt(z)
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

def timeit3(arg=sqrt):
  z = N * e
  s = time()
  for n in range(N):
    z += arg(n * pi) - arg(z)
  print (f"Took {(time() - s):.4f} seconds to calculate {z}")

timeit1()
timeit2()
timeit3()

Les résultats varient, mais voici un exemple de résultat:

3.6.6 (default, Jul 19 2018, 14:25:17) 
[GCC 8.1.1 20180712 (Red Hat 8.1.1-5)]
Took 0.3747 seconds to calculate 3130485.5713865166
Took 0.2899 seconds to calculate 3130485.5713865166
Took 0.2635 seconds to calculate 3130485.5713865166

Essayez vous-même.

0
hkBst