O que é mais rápido em Python: x**.5 ou matemática.sqrt (x)?

Tenho pensado nisto há algum tempo. Como diz O título, que é mais rápido, a função real ou simplesmente elevar para a metade do poder?

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...

Author: GEOCHET, 2008-11-29

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:

 69
Author: Claudiu, 2011-01-17 16:29:59
  • primeira regra de optimização: não o faças
  • segunda regra: não o faças , ainda
Aqui estão alguns horários (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

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
 15
Author: jfs, 2010-04-23 09:34:52
Quantas raízes quadradas estás a representar? Estás a tentar escrever um motor gráfico 3D em Python? Se não, então por que ir com o código que é críptico sobre o código que é fácil de ler? A diferença de tempo é que seria menos do que qualquer pessoa poderia notar em apenas sobre qualquer aplicação que eu poderia forsee. Eu realmente não quero colocar a sua pergunta, mas parece que você está indo um pouco longe demais com otimização prematura.
 12
Author: Kibbee, 2008-11-29 01:28:24

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.
 8
Author: JimB, 2008-11-29 01:53:04

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
 6
Author: zoli2k, 2010-04-23 04:37:34
Usando o código de Cláudio, na minha máquina, mesmo com "from math import sqrt" x*.5 é mais rápido, mas usando psyco.full() sqrt (x) torna-se muito mais rápido, pelo menos em 200%
 4
Author: Nope, 2008-11-29 02:28:52
Muito provavelmente matemática.sqrt (x), porque é otimizado para enraizamento quadrado. Os Benchmarks vão dar-lhe a resposta que procura.
 3
Author: strager, 2008-11-29 01:25:16
Pelo que vale (veja a resposta de Jim). Na minha máquina, a rodar o 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
Author: zdan, 2008-11-29 02:17:09
Alguém comentou sobre a "raiz quadrada rápida de Newton-Raphson" do Quake 3... Implementei-o com ctypes, mas é muito lento em comparação com as versões nativas. Vou tentar algumas otimizações e implementações alternativas.
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
 3
Author: lunixbochs, 2010-04-23 06:03:59
Os resultados de Cláudio diferem dos meus. Estou a usar o Python 2.6 no Ubuntu numa velha máquina P4 2.4 Ghz... Aqui estão os meus resultados:
>>> 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.
 1
Author: bobpaul, 2010-04-23 03:14:26

É melhor referenciar a raiz quadrada rápida de Newton-Raphson também. Não deve ser preciso muito para converter ao Python.

 0
Author: Mitch Wheat, 2008-11-29 01:36:19

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.

 0
Author: Nadstratosfer Gonczy, 2017-11-23 14:37:13
O que seria ainda mais rápido se entrasses math.py e copiou a função "sqrt" para o seu programa. Leva tempo para o seu programa encontrar math.py, então abra, encontre a função que você está procurando, e então traga isso de volta ao seu programa. Se essa função é mais rápida mesmo com os passos de" pesquisa", então a função em si tem que ser muito rápida. Provavelmente vai cortar o teu tempo ao meio. Em resumo:
  1. Vá para math.py
  2. Encontre a função "sqrt"
  3. entendido. it
  4. colar a função no seu programa como o localizador sqrt.
  5. Tempo.
 -3
Author: PyGuy, 2015-03-24 11:37:06