이 두 코드 시퀀스는 거의 동일하며 성능이 매우 비슷해야합니다. 그것이 내 "직감"입니다. 그러나 정적 분석을 수행하고 부분 벤치 마크를 실행하여 확인했습니다.
더 명확한 옵션을 구현하려면 4 바이트 이상의 바이트 코드가 필요하므로 약간 느려질 수 있습니다. 그러나 여분의 작업은 LOAD_FAST
과 STORE_FAST
으로 제한되어 있으며 스택 맨 위에서 (TOS
) 변수로 /에서 이동합니다. 추가 작업은 겸손하므로 성능에 영향을 주어야합니다.
더 많은 양적 정확성을 위해 대상 장비에서 두 가지 접근 방식을 벤치마킹 할 수 있지만 내 3 년 된 랩톱에서는 LOAD_FAST
/STORE_FAST
쌍이 표준 CPython 2.7.5에서 3 초 이상 걸립니다. 따라서이 명확성은 100M 항목 당 약 6 초의 비용이 소요될 것으로 추정합니다. PyPy just-in-time 파이썬 컴파일러는 동일한 바이트 코드를 사용하지 않지만, 절반 정도, 즉 100M 당 3 초의 클리어 버전에 대한 오버 헤드를 계산했습니다. 항목을 처리하기 위해 수행하는 다른 작업과 비교할 때 명확한 버전은 아마도 중요한 대결이 아닙니다.
는 TL; DR 뒷이야기
첫인상 코드 시퀀스, 가독성과 명확성 다른 반면, 기술적으로 매우 유사하다는 것을, 그리고 유사한 성능 특성이 안된다. 파이썬 디스어셈블러를 사용하여 좀 더 분석해 봅시다.
def one(valid, data):
valid[ data[ index[valid[:,0],0] ] == 0, 1] = False
def two(valid, data):
valid_index = index[valid[:,0],0]
invalid_index = (data[ valid_index ] == 0)
valid[ invalid_index, 1 ] = False
그런 Python's bytecode dissassember를 사용하여 :
import dis
dis.dis(one)
print "---"
dis.dis(two)
가 제공합니다 : 나는 함수로 각 코드를 떨어
15 0 LOAD_GLOBAL 0 (False)
3 LOAD_FAST 0 (valid)
6 LOAD_FAST 1 (data)
9 LOAD_GLOBAL 1 (index)
12 LOAD_FAST 0 (valid)
15 LOAD_CONST 0 (None)
18 LOAD_CONST 0 (None)
21 BUILD_SLICE 2
24 LOAD_CONST 1 (0)
27 BUILD_TUPLE 2
30 BINARY_SUBSCR
31 LOAD_CONST 1 (0)
34 BUILD_TUPLE 2
37 BINARY_SUBSCR
38 BINARY_SUBSCR
39 LOAD_CONST 1 (0)
42 COMPARE_OP 2 (==)
45 LOAD_CONST 2 (1)
48 BUILD_TUPLE 2
51 STORE_SUBSCR
52 LOAD_CONST 0 (None)
55 RETURN_VALUE
18 0 LOAD_GLOBAL 0 (index)
3 LOAD_FAST 0 (valid)
6 LOAD_CONST 0 (None)
9 LOAD_CONST 0 (None)
12 BUILD_SLICE 2
15 LOAD_CONST 1 (0)
18 BUILD_TUPLE 2
21 BINARY_SUBSCR
22 LOAD_CONST 1 (0)
25 BUILD_TUPLE 2
28 BINARY_SUBSCR
29 STORE_FAST 2 (valid_index)
19 32 LOAD_FAST 1 (data)
35 LOAD_FAST 2 (valid_index)
38 BINARY_SUBSCR
39 LOAD_CONST 1 (0)
42 COMPARE_OP 2 (==)
45 STORE_FAST 3 (invalid_index)
20 48 LOAD_GLOBAL 1 (False)
51 LOAD_FAST 0 (valid)
54 LOAD_FAST 3 (invalid_index)
57 LOAD_CONST 2 (1)
60 BUILD_TUPLE 2
63 STORE_SUBSCR
64 LOAD_CONST 0 (None)
67 RETURN_VALUE
유사하지만 동일하지,하지에 같은 순서. diff of the two은 똑같은 플러스 기능을 사용하면 더 많은 바이트 코드가 필요합니다.
나는, 각 기능의 디스어셈블러 목록 중 바이트 코드의 옵 코드를 구문 분석 collections.Counter
로 하락하고, 카운트 비교 : 그것은 분명해진다 곳
다음
Bytecode Count(s)
======== ========
BINARY_SUBSCR 3
BUILD_SLICE 1
BUILD_TUPLE 3
COMPARE_OP 1
LOAD_CONST 7
LOAD_FAST 3, 5 *** differs ***
LOAD_GLOBAL 2
RETURN_VALUE 1
STORE_FAST 0, 2 *** differs ***
STORE_SUBSCR 1
두 번째는, 명확 방법은 네 가지 이상을 사용하는 것이 바이트 코드 및 간단하고 빠른 LOAD_FAST
/STORE_FAST
다양성을 제공합니다. 따라서 정적 분석은 추가 메모리 할당이나 성능을 저하시키는 기타 부작용을 두려워 할 특별한 이유가 없음을 보여줍니다.
그런 다음 두 개의 함수가 매우 비슷하여 디스어셈블러의 차이점은 두 번째 함수가 다른 경우에만 LOAD_FAST
/STORE_FAST
쌍이 있다는 점입니다. 나는 그것들을 1 억 번 실행하고 그들의 런타임을 비교했다. 그들은 CPython 2.7.5에서 3 초 이상 차이가 났고 PyPy 2.2.1 (Python 2.7.3 기준)에서는 약 1.5 초가 달랐습니다. 그 두 배가 되더라도 (두 쌍이 있기 때문에) 추가로드/저장소 쌍이 당신을 많이 늦추지는 않을 것이라는 것이 분명합니다.
어쨌든 여분의 복사 작업을 수행하지 않으므로 그렇게하십시오. (내가 괄호 (PEP8)로 공백을 건너 뛰길 원합니다. – seberg
@seberg 실제로 논리적으로는 아닌 두 개의 새 (큰) 배열을 만듭니다 기능에 필요한 --- 그래서 여분의 메모리, 마치'valid_data = data [valid_index]' – DilithiumMatrix
Jeesh ... 그냥 말도 안되는 것을 제안했다면 어쨌든 그 배열을 만들고 있습니다. 3 행은 ** 정확히 ** 동일한 것. – seberg