Je me demande cela depuis un certain temps. Comme le titre l'indique, qu'est-ce qui est le plus rapide, la fonction réelle ou simplement monter à mi-puissance?
MISE À JOUR
Ce n'est pas une question d'optimisation prématurée. Il s'agit simplement de savoir comment fonctionne réellement le code sous-jacent. Quelle est la théorie du fonctionnement du code Python?
J'ai envoyé un e-mail à Guido van Rossum car je voulais vraiment connaître les différences entre ces méthodes.
Mon email:
Il y a au moins 3 façons de faire une racine carrée en Python: math.sqrt, l'opérateur '**' et pow (x, .5). Je suis simplement curieux de connaître les différences dans la mise en œuvre de chacun d'entre eux. En matière d'efficacité, quel est le meilleur?
Sa réponse:
pow et ** sont équivalents; math.sqrt ne fonctionne pas pour les nombres complexes et les liens vers la fonction C sqrt (). Quant à savoir lequel est le plus rapide, je n'ai aucune idée ...
math.sqrt
s'agit d'une routine plus optimisée (telle quelle) et qui exprime l'intention plus clairement, elle doit toujours être préférée àx**.5
. Il n'est pas prématuré de savoir ce que vous écrivez et de choisir l'alternative la plus rapide et offrant plus de clarté au code. Si tel est le cas, vous devez également expliquer pourquoi vous auriez choisi les autres alternatives.Réponses:
math.sqrt(x)
est nettement plus rapide quex**0.5
.import math N = 1000000
%%timeit for i in range(N): z=i**.5
%%timeit for i in range(N): z=math.sqrt(i)
Utilisation de Python 3.6.9 ( notebook ).
la source
Voici quelques horaires (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
c'est légèrement plus rapide quesqrt(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 quex**.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
la source
Dans ces micro-benchmarks, ce
math.sqrt
sera plus lent, en raison du peu de temps nécessaire pour rechercher lesqrt
dans l'espace de noms mathématique. Vous pouvez l'améliorer légèrement avecfrom math import sqrt
Même dans ce cas, en exécutant quelques variations dans le temps, cela montre un léger avantage (4-5%) en termes de performances pour
x**.5
Fait intéressant, faire
import math sqrt = math.sqrt
l'a accéléré encore plus, à moins de 1% de différence de vitesse, avec très peu de signification statistique.
Je vais répéter Kibbee et dire que c'est probablement une optimisation prématurée.
la source
sqrt
dans l'espace de noms local du programme l'accélère davantage est probablement à cause de l'ordre de résolution de la méthode: le compilateur vérifie d'abord si la fonction a été définie dans votre code, puis dans toutes les importations, donc si elle a été définie localement, cela prend moins de temps. rechercheCombien de racines carrées jouez-vous vraiment? Essayez-vous d'écrire un moteur graphique 3D en Python? Sinon, pourquoi choisir un code cryptique plutôt qu'un code facile à lire? Le décalage horaire serait inférieur à ce que quiconque pourrait remarquer dans à peu près n'importe quelle application que je pourrais 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.
la source
En python 2.6, la
(float).__pow__()
fonction utilise lapow()
fonction C et lesmath.sqrt()
fonctions utilisent lasqrt()
fonction C.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 Cpow(x,0.5)
appelle simplement lasqrt()
fonction.La différence de vitesse d'utilisation
.**
oumath.sqrt
est causée par les wrappers utilisés autour des fonctions C et la vitesse dépend fortement des indicateurs d'optimisation / compilateur C utilisés sur le système.Éditer:
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
la source
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
la source
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%
la source
Très probablement math.sqrt (x), car il est optimisé pour l'enracinement carré.
Benchmarks vous fournira la réponse que vous recherchez.
la source
Quelqu'un a commenté la "racine carrée rapide de Newton-Raphson" de Quake 3 ... Je l'ai implémentée avec des ctypes, mais c'est super lent par rapport aux versions natives. Je vais essayer quelques optimisations et 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, sort environ 3,6 fois plus vite que la version ctypes, mais toujours 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
la source
Les résultats de Claudiu diffèrent des miens. J'utilise Python 2.6 sur Ubuntu sur une ancienne 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 NOW semble convenir que sqrt, dans le contexte local, est plus rapide ( http://codepad.org/6trzcM3j ). Codepad semble exécuter actuellement Python 2.5. Peut-être utilisaient-ils 2.4 ou plus quand 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 secondes sur ma machine, ce qui est toujours mieux que les chiffres de 0,56-0,60 de timeit1.
Je dirais que sur Python moderne, utilisez math.sqrt et mettez-le définitivement dans le contexte local, soit avec somevar = math.sqrt, soit avec from math import sqrt.
la source
La chose pythonique à optimiser est la lisibilité. Pour cela, je pense que l'utilisation explicite de la
sqrt
fonction est la meilleure. 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 (ce 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 un exemple de sortie est:
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-le vous-même.
la source
Bien sûr, si l'on a affaire à des littéraux et que l'on a besoin d'une valeur constante, le runtime Python peut pré-calculer la valeur au moment de la compilation, si elle est écrite avec des opérateurs - pas besoin de profiler chaque version dans ce cas:
In [77]: dis.dis(a) 2 0 LOAD_CONST 1 (1.4142135623730951) 2 RETURN_VALUE In [78]: def a(): ...: return 2 ** 0.5 ...: In [79]: import dis In [80]: dis.dis(a) 2 0 LOAD_CONST 1 (1.4142135623730951) 2 RETURN_VALUE
la source
Le problème SQRMINSUM que j'ai résolu récemment nécessite le calcul de la racine carrée à plusieurs reprises sur un grand ensemble de données. Les 2 plus anciennes soumissions de mon histoire , avant que j'aie fait d'autres optimisations, diffèrent uniquement en remplaçant ** 0.5 par sqrt (), réduisant ainsi le temps d'exécution de 3.74s à 0.51s dans PyPy. C'est presque le double de l'amélioration déjà massive de 400% que Claudiu a mesurée.
la source
Ce serait encore plus rapide si vous alliez dans math.py et copiiez la fonction «sqrt» dans votre programme. Il faut du temps à votre programme pour trouver math.py, puis l'ouvrir, trouver la fonction que vous recherchez, puis la ramener dans votre programme. Si cette fonction est plus rapide même avec les étapes de «recherche», alors la fonction elle-même doit être terriblement rapide. Cela réduira probablement votre temps de moitié. En résumé:
la source
from math import sqrt
?