O que é mais rápido em Python: x**.5 ou matemática.sqrt (x)?
Actualizar
Isto não é uma questão de otimização prematura. Esta é simplesmente uma questão de como o código subjacente realmente funciona. Qual é a teoria de como o código Python funciona? Enviei um e-mail ao Guido van Rossum porque queria saber as diferenças destes métodos.o meu email:
Existem pelo menos 3 maneiras de fazer uma raiz quadrada em Python: math.sqrt, o operador "* * " e pow (x,.5). Só estou curioso quanto às diferenças em a implementação de cada um deles. Quando se trata de eficiência que está melhor?
a sua resposta:
Pow e* * são equivalentes; math.sqrt não funciona para números complexos, e ligações à função C sqrt (). Quanto a qual é mais rápido, não faço ideia...
13 answers
De acordo com os comentários, actualizei o código:
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()
Agora a função math.sqrt
está directamente num argumento local, o que significa que tem a pesquisa mais rápida possível.
Actualização: a versão em python parece importar aqui. Eu costumava pensar que timeit1
seria mais rápido, desde quando python parses " i**.5 " Ele sabe, sintaticamente, que método chamar (__pow__
ou alguma variante), então ele não tem que passar pela cabeça de pesquisa que a variante math.sqrt
faz. Mas posso estar errado:
Python 2. 5: 0. 191000 vs. 0. 224000
Python 2. 6: 0.195000 vs. 0. 139000
Também o psyco parece lidar com math.sqrt
Melhor:
O 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
Resultados das tabelas produzidas na máquina:
$ 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
Para reproduzir os resultados:
- obter Fonte:
git clone git://gist.github.com/783011.git gist-783011
- instalar
tox
:pip install tox
- executar
tox
a partir da pasta comtox.ini
ficheiro.
- primeira regra de optimização: não o faças
- segunda regra: não o faças , ainda
$ 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
Este teste mostra que x**.5
é ligeiramente mais rápido do que sqrt(x)
.
Para o Python 3.0 o resultado é o oposto:
$ \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)
é sempre mais rápido do que x**.5
noutra máquina (Ubuntu, Python 2.6 e 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
Nestes micro-benchmarks, matemática.o sqrt será mais lento, devido ao pouco tempo que leva para procurar o sqrt no espaço de nomes de matemática. Você pode melhorá-lo ligeiramente com
from math import sqrt
Mesmo assim, executando algumas variações através do tempo, mostrar uma pequena (4-5%) vantagem de desempenho para "x**.5"
Curiosamente, fazer ... import math
sqrt = math.sqrt
Acelerou ainda mais, para uma diferença de 1% na velocidade, com muito pouco significado estatístico.
Vou repetir o Kibbee e dizer: que esta é provavelmente uma otimização prematura.No python 2.6 a função (float).__pow__()
usa a função C pow()
e as funções math.sqrt()
usam a função C sqrt()
.
No compilador glibc, a implementação de pow(x,y)
é bastante complexa e está bem optimizada para vários casos excepcionais. Por exemplo, chamar C pow(x,0.5)
simplesmente chama a função sqrt()
.
A diferença na velocidade de utilização de .**
ou math.sqrt
é causada pelas embalagens usadas em torno das funções C e a velocidade depende fortemente das opções de optimização/compilador C usado no sistema.
Editar:
Aqui estão os resultados do algoritmo do Claudiu na minha máquina. Tenho resultados diferentes.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
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
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
Aqui está outro método usando struct, sai cerca de 3,6 x mais rápido do que a versão ctypes, mas ainda 1/10 a velocidade 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
>>> 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
O Sqrt é consistentemente mais rápido para mim... Mesmo Codepad.org agora parece concordar que o sqrt, no contexto local, é mais rápido ( http://codepad.org/6trzcM3j ). O Codepad parece estar a correr o Python 2.5 actualmente. Talvez estivessem a usar 2,4 ou mais quando Claudiu respondeu pela primeira vez?
Na verdade, até usando matemática.sqrt (i) no lugar de arg(i), eu ainda tenho melhores tempos para o sqrt. Neste caso, o timeit2 () levou entre 0,53 e 0,55 segundos na minha máquina, o que ainda é melhor do que os valores de 0,56-0,60 do timeit1.
Eu diria, em Python moderno, usa matemática.sqrt e definitivamente trazê-lo para o contexto local, ou com somevar=math.sqrt ou com a partir de importação matemática sqrt.
É melhor referenciar a raiz quadrada rápida de Newton-Raphson também. Não deve ser preciso muito para converter ao Python.
O problema SQRMINSUM que resolvi recentemente requer a computação de raiz quadrada repetidamente num conjunto de dados grande. As duas mais antigas submissões na minha história , antes de eu ter feito outras otimizações, diferem apenas por substituir **0.5 por sqrt(), reduzindo assim o tempo de execução de 3,74 s para 0,51 s em PyPy. Isso é quase o dobro da já massiva melhoria de 400% que Claudiu mediu.
- Vá para math.py
- Encontre a função "sqrt"
- entendido. it
- colar a função no seu programa como o localizador sqrt. Tempo.