Что быстрее в Python: x**.5 или математике.sqrt (x)?



Мне вот интересно, это в течение некоторого времени. Как говорится в названии, что быстрее, фактическая функция или просто повышение до половины мощности?



обновление



Это не вопрос преждевременной оптимизации. Это просто вопрос о том, как на самом деле работает базовый код. Какова теория того, как работает код Python?



Я послал Guido van Rossum по электронной почте, потому что я действительно хотел знать различия в этих методах.



мой электронная почта:




есть по крайней мере 3 способа сделать квадратный корень в Python: math.sqrt, the
'**'оператор и pow(x,.5). Мне просто любопытно, в чем разница
реализация каждого из них. Когда дело доходит до эффективности, которая
так лучше?




его ответ:




pow и ** эквивалентны; математика.sqrt не работает для комплексных чисел,
и ссылки на функцию C sqrt (). О что
быстрее, у меня нет идея...


548   13  

13 ответов:

согласно комментариям, я обновил код:

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()

теперь math.sqrt функция находится непосредственно в локальном аргументе, что означает, что она имеет самый быстрый поиск.

обновление: версия python, кажется, имеет значение здесь. Я раньше думал, что timeit1 было бы быстрее, так как когда python анализирует "i**.5 " он знает, синтаксически, какой метод вызвать (__pow__ или какой-то вариант), поэтому он не должен идти через надземный поиска, что math.sqrt вариант есть. Но я могу ошибаться:

Python 2.5: 0.191000 против 0.224000

Python 2.6: 0.195000 против 0.139000

также psyco, кажется, имеет дело с math.sqrt лучше:

Python 2.5 + Psyco 2.0: 0,109000 против 0,043000

Python 2.6 + Psyco 2.0: 0.128000 против 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: =100*(-)/;%.0f

таблица результатов производится на машина:

$ 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

для воспроизведения результатов:

  • первое правило оптимизации: не надо
  • второе правило: не надо, но все же

вот некоторые тайминги (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

этот тест показывает, что x**.5 - это немного быстрее, чем sqrt(x).

для Python 3.0 результат противоположный:

$ \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) всегда быстрее, чем x**.5 на другой машине (Ubuntu, Python 2.6 и 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

сколько квадратных корней вы действительно выполняете? Вы пытаетесь написать какой-то 3D графический движок в Python? Если нет, то зачем идти с кодом, который является загадочным над кодом, который легко читать? Разница во времени будет меньше, чем кто-либо мог заметить в любом приложении, которое я мог бы предвидеть. Я действительно не хочу ставить ваш вопрос, но кажется, что вы заходите слишком далеко с преждевременной оптимизацией.

в этих микро-ориентиры, математика.sqrt будет медленнее из-за небольшого времени, необходимого для поиска sqrt в пространстве имен math. Вы можете немного улучшить его с

 from math import sqrt

даже тогда, хотя, запустив несколько вариаций через timeit, показывают небольшое (4-5%) преимущество производительности для "x**.5"

интересно, что делал

 import math
 sqrt = math.sqrt

ускорил его еще больше, в пределах 1% разницы в скорости, с очень небольшим статистическим значением.

I повторю Кибби, и скажу, что это, наверное, преждевременная оптимизация.

в python 2.6 the (float).__pow__() функция использует C

используя код Claudiu, на моей машине даже с "из math import sqrt" x**.5 быстрее, но с помощью psyco.full () sqrt (x) становится намного быстрее, по крайней мере на 200%

скорее всего математика.sqrt (x), потому что он оптимизирован для квадратного укоренения.

тесты предоставят вам ответ, который вы ищете.

для чего это стоит (см. ответ Джима). На моей машине работает 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

кто-то прокомментировал "быстрый квадратный корень Ньютона-Рафсона" из Quake 3... Я реализовал его с помощью ctypes, но он очень медленный по сравнению с родными версиями. Я собираюсь попробовать несколько оптимизаций и альтернативных реализаций.

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

вот еще один метод, использующий struct, выходит примерно на 3.6 x быстрее, чем версия ctypes, но все же 1/10 скорости 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

результаты Клаудиу отличаются от моих. Я использую Python 2.6 на Ubuntu на старой машине P4 2.4 Ghz... Вот мои результаты:

>>> 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 последовательно быстрее для меня... Даже Codepad.org теперь, кажется, согласен, что sqrt, в локальном контексте, быстрее (http://codepad.org/6trzcM3j). Codepad, кажется, работает на Python 2.5 в настоящее время. Возможно, они использовали 2.4 или старше, когда Клаудиу первый ответил?

в самом деле, даже с помощью математики.sqrt (i) вместо arg (i), я все еще получаю лучшие времена для sqrt. В этом случае timeit2() занял от 0,53 до 0,55 секунды на моей машине, что все еще лучше, чем цифры 0,56-0,60 от timeit1.

Я бы сказал, на современном Python, используйте математику.sqrt и определенно привести его к локальному контексту, либо с somevar=math.sqrt или с помощью math import sqrt.

вы, возможно, захотите, чтобы тест быстрый квадратный корень Ньютона-Рафсона Как хорошо. Не займет много, чтобы преобразовать в Python.

проблема SQRMINSUM Я решил недавно требует вычисления квадратного корня неоднократно на большом наборе данных. Самые старые 2 представления в моем история, прежде чем я сделал другие оптимизации, отличаются только заменой **0.5 на sqrt(), тем самым уменьшая время выполнения с 3,74 С до 0,51 С в PyPy. Это почти в два раза больше, чем уже массивное улучшение 400%, которое измерил Клаудиу.

Что было бы еще быстрее, если бы вы вошли math.py и скопировал функцию "sqrt" в вашу программу. Это занимает время для вашей программы, чтобы найти math.py, затем откройте его, найдите функцию, которую вы ищете, а затем верните ее в свою программу. Если эта функция быстрее даже с шагами "поиска", то сама функция должна быть ужасно быстрой. Вероятно, сократит ваше время в два раза. Вкратце:

  1. перейти к math.py
  2. найти функцию "корень"
  3. скопировать
  4. вставить функцию в вашу программу в качестве sqrt finder.
  5. времени.

Comments

    Ничего не найдено.