파이썬에서 x **. 5 또는 math.sqrt (x) 중 어느 것이 더 빠릅니까?


187

나는 이것을 오랫동안 궁금해했다. 제목에서 알 수 있듯이 실제 기능이 더 빠르거나 단순히 절반의 힘을 올리는 것입니까?

최신 정보

이것은 조기 최적화 문제가 아닙니다. 이것은 단순히 기본 코드가 실제로 어떻게 작동하는지에 대한 질문입니다. 파이썬 코드의 작동 원리는 무엇입니까?

귀도 반 로섬 (Guido van Rossum)에게이 방법의 차이점을 알고 싶었던 이메일을 보냈습니다.

내 이메일:

파이썬에서 제곱근을 수행하는 방법은 적어도 3 가지가 있습니다 : math.sqrt, '**'연산자 및 pow (x, .5). 각 구현의 차이점이 궁금합니다. 효율성이 더 좋을 때?

그의 답변 :

pow와 **는 동일합니다. math.sqrt는 복소수에서는 작동하지 않으며 C sqrt () 함수에 연결됩니다. 어느 쪽이 더 빠른지 모르겠다.


81
Guido가 이메일에 응답하는 것이 좋습니다.
Evan Fosmark

3
에반, 나는 응답을 받았다는 것에 놀랐다
Nope

11
나는 이것이 나쁜 질문이라고 생각하지 않습니다. 예를 들어, x * x는 x ** 2보다 10 배 빠릅니다.이 상황에서는 가독성이 떨어지기 때문에 왜 그렇게 빨리하지 않습니까?
TM.

12
케이시, 난 당신과 함께 "조기 최적화"에 있습니다. :) 귀하의 질문은 나에게 조기 최적화처럼 보이지 않습니다 : 변형이 코드를 손상시킬 위험이 없습니다. math.sqrt () 대신 pow ()를 선택할 때 수행 시간 측면에서 더 잘 아는 것이 더 중요합니다.
Eric O Lebigot

8
이것은 조기 최적화가 아니라 조기 비관 화를 피합니다 (참조 28, C ++ 코딩 표준, A.Alexandrescu). 만약math.sqrt (그대로)보다 최적화 루틴이고 명확하게 의도 이상을 표현, 항상보다 선호한다 x**.5. 작성하는 내용을 아는 것은 조기 최적화가 아니며 더 빠르고 더 명확한 코드를 제공하는 대안을 선택했습니다. 그렇다면 왜 다른 대안을 선택해야하는지 똑같이 논쟁해야합니다.
swalog

답변:


89

math.sqrt(x)보다 훨씬 빠릅니다 x**0.5.

import math
N = 1000000
%%timeit
for i in range(N):
    z=i**.5

10 루프, 루프 당 3 : 156ms 최고

%%timeit
for i in range(N):
    z=math.sqrt(i)

10 루프, 루프 당 3 : 91.1 ms

파이썬 3.6.9 사용하기 ( 노트북 ).


이제 codepad.org에서 3 번 실행했으며 세 번 모두 a ()가 b ()보다 훨씬 빠릅니다.
Jeremy Ruten

10
표준 timeit 모듈은 당신의 친구입니다. 실행 시간을 측정 할 때 일반적인 함정을 피합니다!
Eric O Lebigot

1
스크립트 결과는 다음과 같습니다. zoltan @ host : ~ $ python2.5 p.py 0.183226 초를 가져 왔습니다 0.155829 초를 가져 왔습니다 zoltan @ host : ~ $ python2.4 p.py를 0.181142 초 가져 왔습니다 0.153742 초를 가져 왔습니다 zoltan @ host : ~ $ python2.6 p.py Took 0.157436 초 Took 0.093905 초 대상 시스템 : Ubuntu Linux CPU : Intel (R) Core (TM) 2 Duo CPU T9600 @ 2.80GHz 다른 결과가 나왔습니다. 이것에 따르면 당신의 대답은 일반적이지 않습니다.
zoli2k

2
코드 패드는 훌륭한 서비스이지만 타이밍 성능이 끔찍합니다. 누가 서버가 주어진 순간에 얼마나 바쁜지 알고 있습니다. 각 실행은 잠재적으로 매우 다른 결과를 줄 수 있습니다
adamJLev에게

1
Linux에서 py32, py31, py30, py27, py26, pypy, jython, py25, py24 인터프리터에 대해 x **. 5 대 sqrt (x)의 성능 비교를 추가했습니다. gist.github.com/783011
jfs

19
  • 최적화의 첫 번째 규칙 : 하지 마십시오
  • 두 번째 규칙 : 아직 하지 마십시오

타이밍은 다음과 같습니다 (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).

파이썬 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

10

당신은 실제로 얼마나 많은 제곱근을 수행합니까? 파이썬으로 3D 그래픽 엔진을 작성하려고합니까? 그렇지 않다면 왜 읽기 쉬운 코드보다 암호가 많은 코드를 사용해야합니까? 시차는 내가 볼 수있는 거의 모든 응용 프로그램에서 누구나 알 수있는 것보다 적습니다. 나는 정말로 귀하의 질문을 내려 놓을 의도는 아니지만 조기 최적화로 너무 멀리 가고있는 것 같습니다.


16
나는 내가 조기 최적화를하고 있다고 생각하지 않습니다. 평균적으로 더 빠른 두 가지 방법을 결정하는 것은 단순한 질문입니다.
Nope

2
Kibbee : 확실히 유효한 질문이지만, Stack Overflow에 관한 질문에 대한 불만을 공유하는 것은, asker가 모든 종류의 조기 최적화를 수행하고 있음을 의미합니다. 모든 언어에 대해 묻는 질문의 많은 부분이 있습니다.
Eli Courtwright

2
math.sqrt (x)가 x ** 0.5보다 읽기 쉬운가요? 나는 적어도 분명히 파이썬에 익숙하다면 둘 다 제곱근이라고 생각합니다. 파이썬에 익숙하지 않기 때문에 ** "cryptic"과 같은 표준 파이썬 연산자를 호출하지 마십시오.
TM.

5
** 연산자가 암호라고 생각하지 않습니다. 나는 제곱근을 수학에 맞지 않는 사람들에게 조금 비밀스럽게 만드는 방법으로 지수 0.5에 무언가를 올리는 것으로 생각합니다.
Kibbee

13
그가 파이썬으로 3D 엔진을 만들고 있다면 어떨까요?
Chris Burt-Brown

9

이러한 마이크로 벤치 마크에서는 수학 네임 스페이스에서 math.sqrt검색하는 데 약간의 시간이 걸리기 때문에 속도가 느려집니다 sqrt. 당신은 그것을 약간 향상시킬 수 있습니다

 from math import sqrt

그럼에도 불구하고 timeit을 통해 몇 가지 변형을 실행하면 약간의 (4-5 %) 성능 이점이 있습니다. x**.5

흥미롭게도

 import math
 sqrt = math.sqrt

통계적으로 유의미한 속도로 속도 차이가 1 % 이내로 훨씬 더 빨라졌습니다.


Kibbee를 반복하고 이것이 아마도 조기 최적화라고 말할 것입니다.


7

파이썬 2.6에서, (float).__pow__() 함수는 C에서 사용하는 pow()함수와 math.sqrt()함수는 C를 사용하여sqrt() 기능.

glibc 컴파일러에서 구현 pow(x,y)은 매우 복잡하며 다양한 예외적 인 경우에 최적화되어 있습니다. 예를 들어 C pow(x,0.5)를 호출하면 단순히 sqrt()함수 를 호출합니다 .

사용의 속도 차이 .**또는 math.sqrt래퍼는 C 기능을 사용 주변 속도 강하게 최적화 플래그의 시스템에서 사용 / C 컴파일러에 의존하여 발생된다.

편집하다:

내 컴퓨터의 Claudiu 알고리즘 결과는 다음과 같습니다. 나는 다른 결과를 얻었다 :

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

4

가치가있는 것에 대해서는 (Jim의 답변 참조). 내 컴퓨터에서 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

4

내 컴퓨터에서 "수학 가져 오기 sqrt"를 사용하더라도 Claudiu의 코드를 사용하면 x **. 5는 빠르지 만 psyco.full () sqrt (x)를 사용하면 적어도 200 % 더 빠릅니다.


3

math.sqrt (x)는 제곱근에 최적화되어 있기 때문에 가능성이 높습니다.

벤치 마크를 통해 원하는 답변을 얻을 수 있습니다.


3

누군가 Quake 3의 "빠른 Newton-Raphson 제곱근"에 대해 언급했습니다. 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를 사용하는 또 다른 방법은 ctypes 버전보다 약 3.6 배 빠르지 만 C 속도의 1/10입니다.

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

1

Claudiu의 결과는 내 것과 다릅니다. 오래된 P4 2.4Ghz 컴퓨터에서 우분투에서 Python 2.6을 사용하고 있습니다 ... 내 결과는 다음과 같습니다.

>>> 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 ). 현재 코드 패드가 Python 2.5를 실행중인 것 같습니다. Claudiu가 처음 대답했을 때 2.4 이상을 사용하고 있었습니까?

실제로 arg (i) 대신 math.sqrt (i)를 사용하더라도 sqrt에 대한 더 나은 시간을 얻습니다. 이 경우 timeit2 ()는 내 컴퓨터에서 0.53 ~ 0.55 초가 걸렸으며 timeit1의 0.56-0.60 수치보다 여전히 낫습니다.

현대 파이썬에서는 math.sqrt를 사용하고 somevar = math.sqrt 또는 math import sqrt에서 로컬 컨텍스트로 가져옵니다.


1

최적화 할 파이썬적인 것은 가독성입니다. 이를 위해 sqrt함수를 명시 적으로 사용하는 것이 가장 좋습니다. 어쨌든 성능을 조사해 봅시다.

Python 3에 대한 Claudiu의 코드를 업데이트하고 계산을 최적화하는 것이 불가능 해졌습니다 (향후 좋은 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

직접 해보십시오.


0

최근에 해결 한 SQRMINSUM 문제 는 큰 데이터 세트에서 반복적으로 제곱근을 계산해야합니다. 내 역사상 가장 오래된 2 건의 제출물 은 다른 최적화를하기 전에 ** 0.5를 sqrt ()로 대체하여 PyPy에서 런타임을 3.74에서 0.51로 줄임으로써 전적으로 다릅니다. 이것은 Claudiu가 측정 한 이미 400 %의 엄청난 개선의 두 배입니다.


0

물론 리터럴을 다루고 상수 값이 필요한 경우, 파이썬 런타임은 연산자로 작성된 경우 컴파일 타임에 값을 미리 계산할 수 있습니다.이 경우 각 버전을 프로파일 링 할 필요가 없습니다.

In [77]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

In [78]: def a(): 
    ...:     return 2 ** 0.5 
    ...:                                                                                                                                  

In [79]: import dis                                                                                                                       

In [80]: dis.dis(a)                                                                                                                       
  2           0 LOAD_CONST               1 (1.4142135623730951)
              2 RETURN_VALUE

-3

math.py에 들어가서 "sqrt"함수를 프로그램에 복사하면 더 빠를 것입니다. 프로그램이 math.py를 찾은 다음 열어서 찾고있는 기능을 찾은 다음 프로그램으로 다시 가져 오는 데 시간이 걸립니다. "조회"단계에서도 해당 기능이 더 빠르면 기능 자체가 엄청나게 빨라야합니다. 아마 당신의 시간을 반으로 줄입니다. 요약해서 말하자면:

  1. math.py로 이동
  2. 함수 "sqrt"를 찾으십시오
  3. 복사
  4. 함수를 sqrt 파인더로 프로그램에 붙여 넣으십시오.
  5. 그것을 시간.

1
작동하지 않습니다. stackoverflow.com/q/18857355/3004881을 참조하십시오 . 또한 원래 질문의 인용 부호가 C 함수에 대한 링크라고 언급하십시오. 또한 함수의 소스 코드를 복사하는 것이 어떻게 다를 수 from math import sqrt있습니까?
Dan Getz

나는 두 함수를 호출하는 것의 차이점을 정확히 밝히는 것이라고 말하지 않았습니다.
PyGuy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.