Python: * 및 **가 / 및 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]
Python이 C에서 구현되는 방식과 관련이 있다고 생각합니다만, 그 이유를 설명해 주실 수 있을까요?
결과가 나오는 이유(예상치 못한)는 Python이 부동소수점 곱셈과 지수를 수반하는 상수 식을 접는 것처럼 보이지만 나눗셈은 아닌 것처럼 보이기 때문입니다. 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)
보시다시피 곱셈과 지수는 코드 컴파일 시 완료되기 때문에 전혀 시간이 걸리지 않습니다.분할은 런타임에 발생하기 때문에 시간이 오래 걸립니다.제곱근은 4개의 연산 중 가장 비용이 많이 드는 연산일 뿐만 아니라 다른 연산에서는 발생하지 않는 다양한 오버헤드(속성 룩업, 함수 호출 등)를 발생시킵니다.
연속 폴딩의 효과를 없애면 증배와 나눗셈을 분리할 필요가 거의 없습니다.
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
아마도 후자의 특수한 경우이기 때문에 오버헤드에 관계없이 보다 효율적으로 실행할 수 있기 때문일 것입니다.
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
edit 2011-11-16 : Python의 peephole optimizer에 의해 지속적인 표현 폴딩이 이루어집니다.소스 코드(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
flag를 지정하면 PEP 238에서 정의된 "true division"이 활성화됩니다.
언급URL : https://stackoverflow.com/questions/8068019/python-why-are-and-faster-than-and-sqrt
'programing' 카테고리의 다른 글
v-on 핸들러 오류: "ReferenceError: i18n이 정의되지 않았습니다." (0) | 2022.07.13 |
---|---|
Vue JS: 데이터 반환 내부의 값을 업데이트하는 방법 (0) | 2022.07.13 |
JAX-RS 및 Jersey를 사용한 REST 토큰 기반 인증 구현 방법 (0) | 2022.07.13 |
C 함수는 왜 네임 매니지먼트가 안 되나요? (0) | 2022.07.13 |
Vue Router가 루트로 이동하지만 잘못된 컴포넌트를 로드함 (0) | 2022.07.13 |