IT

어느 정도 : 왜 *와 **가 /와 sqrt ()보다 빠릅니까?

lottoking 2020. 10. 10. 10:27

어느 정도 : 왜 *와 **가 /와 sqrt ()보다 빠릅니까?


코드를 최적화하는 동안 다음을 깨달았습니다.

>>> from timeit import Timer as T
>>> T(lambda : 1234567890 / 4.0).repeat()
[0.22256922721862793, 0.20560789108276367, 0.20530295372009277]
>>> from __future__ import division
>>> T(lambda : 1234567890 / 4).repeat()
[0.14969301223754883, 0.14155197143554688, 0.14141488075256348]
>>> T(lambda : 1234567890 * 0.25).repeat()
[0.13619112968444824, 0.1281130313873291, 0.12830305099487305]

그리고 또한 :

>>> from math import sqrt
>>> T(lambda : sqrt(1234567890)).repeat()
[0.2597470283508301, 0.2498021125793457, 0.24994492530822754]
>>> T(lambda : 1234567890 ** 0.5).repeat()
[0.15409398078918457, 0.14059877395629883, 0.14049601554870605]

나는 그것이 존재하는 C로 구현되는 방식과 관련이 있다고 생각하지만, 왜 그렇게 할 것이 있는지 설명합니다.


결과에 대한 (다소 예상치 못한) 이유는 그리고 지수가 부동 소수점 곱셈과 지수를 포함하는 상수를 접는 것처럼 보이지만 나누는 것은 아닙니다. math.sqrt()바이트 코드가없고 함수 호출을 포함하기 때문에 다른 짐승입니다.

Python 2.6.5에서 다음 코드 :

x1 = 1234567890.0 / 4.0
x2 = 1234567890.0 * 0.25
x3 = 1234567890.0 ** 0.5
x4 = math.sqrt(1234567890.0)

다음 바이트 코드로 실행됩니다.

  # x1 = 1234567890.0 / 4.0
  4           0 LOAD_CONST               1 (1234567890.0)
              3 LOAD_CONST               2 (4.0)
              6 BINARY_DIVIDE       
              7 STORE_FAST               0 (x1)

  # x2 = 1234567890.0 * 0.25
  5          10 LOAD_CONST               5 (308641972.5)
             13 STORE_FAST               1 (x2)

  # x3 = 1234567890.0 ** 0.5
  6          16 LOAD_CONST               6 (35136.418286444619)
             19 STORE_FAST               2 (x3)

  # x4 = math.sqrt(1234567890.0)
  7          22 LOAD_GLOBAL              0 (math)
             25 LOAD_ATTR                1 (sqrt)
             28 LOAD_CONST               1 (1234567890.0)
             31 CALL_FUNCTION            1
             34 STORE_FAST               3 (x4)

보시다시피 곱셈과 지수는 코드가 준비 될 때 완료되기 때문에 시간이 전혀 걸리지 않습니다. 더 오래 행사에 발생합니다. 제곱근은 네 가지 중 가장 많은 계산 비용이 많이 사용되는 작업 일 가장 속 아니라 다른 작업이 수행하지 않는 다양한 오버 헤드 (발생한 조회, 함수 호출 등).

상수 접기의 효과를 제거하면 곱셈과 나눗셈을 분리 할 수 ​​없습니다.

In [16]: x = 1234567890.0

In [17]: %timeit x / 4.0
10000000 loops, best of 3: 87.8 ns per loop

In [18]: %timeit x * 0.25
10000000 loops, best of 3: 91.6 ns per loop

math.sqrt(x)실제로 x ** 0.5후자의 특수한 경우이므로 over-헤드에도 불구하고보다 효율적으로 수행 할 수 있기 때문에 실제로보다 약간 빠릅니다 .

In [19]: %timeit x ** 0.5
1000000 loops, best of 3: 211 ns per loop

In [20]: %timeit math.sqrt(x)
10000000 loops, best of 3: 181 ns per loop

편집 2011-11-16 : 상수 표현 접기는 파이썬의 구멍 최적화 프로그램에 의해 수행됩니다. 소스 코드 ( peephole.c)에는 상수 나눗셈이 접히지 않는 이유를 설명하는 다음 주석이 포함되어 있습니다.

    case BINARY_DIVIDE:
        /* Cannot fold this operation statically since
           the result can depend on the run-time presence
           of the -Qnew flag */
        return 0;

-Qnew플래그는 PEP 238에 정의 된 "진정한 분할"을 활성화합니다 .

참고 URL : https://stackoverflow.com/questions/8068019/python-why-are-and-faster-than-and-sqrt