Что быстрее в 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.
Ответ 11
Возможно, вы захотите сравнить быстрый квадрат корня Ньютона-Рафсона. Не нужно много переводить на Python.
Ответ 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.
- Время.