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

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

ОБНОВЛЕНИЕ

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

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

Мой адрес электронной почты:

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

Его ответ:

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

Ответ 1

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

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: $4=100*($2-$3)/$3;%.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

Чтобы воспроизвести результаты:

Ответ 2

  • первое правило оптимизации: не делайте этого
  • второе правило: не делайте этого, тем не менее

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

Ответ 3

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

Ответ 4

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

 from math import sqrt

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

Интересно, занимаюсь

 import math
 sqrt = math.sqrt

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


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

Ответ 5

В python 2.6 функция (float).__pow__() использует функцию C pow(), а функции math.sqrt() используют функцию C sqrt().

В компиляторе glibc реализация pow(x,y) довольно сложна и хорошо оптимизирована для различных исключительных случаев. Например, вызов C pow(x,0.5) просто вызывает функцию sqrt().

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

Edit:

Вот результаты алгоритма Клаудиу на моей машине. У меня разные результаты:

[email protected]:~$ python2.4 p.py 
Took 0.173994 seconds
Took 0.158991 seconds
[email protected]:~$ python2.5 p.py 
Took 0.182321 seconds
Took 0.155394 seconds
[email protected]:~$ python2.6 p.py 
Took 0.166766 seconds
Took 0.097018 seconds

Ответ 6

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

Ответ 7

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

Контрольные показатели предоставят вам ответ, который вы ищете.

Ответ 8

За что это стоит (см. Джим). На моей машине запущен 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

Ответ 9

Кто-то прокомментировал "быстрый квадратный корень Ньютона-Рафсона" от 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 раза быстрее, чем версия 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

Ответ 10

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

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

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

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

Ответ 12

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

Ответ 13

Pythonic вещь для оптимизации это читабельность. Для этого я думаю, что явное использование функции sqrt лучше всего. Сказав это, позвольте исследовать производительность в любом случае.

Я обновил код Claudiu для Python 3, а также сделал невозможным оптимизировать вычисления (что может сделать хороший компилятор Python в будущем):

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

Результаты различаются, но пример выходных данных:

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

Попробуй сам.

Ответ 14

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

  • Перейти к math.py
  • Найти функцию "sqrt"
  • Скопировать его
  • Вставить функцию в вашу программу в качестве искателя sqrt.
  • Время.