2014-10-19 3 views
2

나는 꽤 추한 색인 작업을 진행하고 있습니다. 예를 들어,효율성과 가독성 : 중첩 된 부울 인덱스 배열을 사용할 때 난독 화

valid[ data[ index[valid[:,0],0] ] == 0, 1] = False 

valid과 같은 것들을 index 각각 {} NX2 bool 어레이 또는 S와 S int이고, data 긴 {N}이다.

정말 열심히 집중하면, 나는 이것이 내가 원하는 것을하고 있다는 것을 확신 할 수있다. 그러나 그것의 믿어지지 않는 정도로 난독 화된다. 이렇게 효율적으로 어떻게 불투명해질 수 있습니까?

valid_index = index[valid[:,0],0] 
invalid_index = (data[ valid_index ] == 0) 
valid[ invalid_index, 1 ] = False 

을하지만 내 배열은 그래서 메모리를 복제하지 않으려는 수백만 개의 항목 100의 최대해야합니다;

나는 예를 들어, 그것을 깰 수 가능한 한 효율적으로 속도를 유지해야합니다.

+1

어쨌든 여분의 복사 작업을 수행하지 않으므로 그렇게하십시오. (내가 괄호 (PEP8)로 공백을 건너 뛰길 원합니다. – seberg

+1

@seberg 실제로 논리적으로는 아닌 두 개의 새 (큰) 배열을 만듭니다 기능에 필요한 --- 그래서 여분의 메모리, 마치'valid_data = data [valid_index]' – DilithiumMatrix

+0

Jeesh ... 그냥 말도 안되는 것을 제안했다면 어쨌든 그 배열을 만들고 있습니다. 3 행은 ** 정확히 ** 동일한 것. – seberg

답변

2

이 두 코드 시퀀스는 거의 동일하며 성능이 매우 비슷해야합니다. 그것이 내 "직감"입니다. 그러나 정적 분석을 수행하고 부분 벤치 마크를 실행하여 확인했습니다.

더 명확한 옵션을 구현하려면 4 바이트 이상의 바이트 코드가 필요하므로 약간 느려질 수 있습니다. 그러나 여분의 작업은 LOAD_FASTSTORE_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 초가 달랐습니다. 그 두 배가 되더라도 (두 쌍이 있기 때문에) 추가로드/저장소 쌍이 당신을 많이 늦추지는 않을 것이라는 것이 분명합니다.

+0

감사합니다! 평신도의 접근법을 사용하십시오 (% timeit 및 % mement) 시간이나 메모리 사용량에는 차이가 없다는 것을 확인할 수 있습니다. – DilithiumMatrix

+0

@zhermes'% timeit'과'% memit' ar e는이 질문에 답할 수있는 완벽하고 유효하고 즐거운 경험적 방법입니다. 전체 코드와 데이터 세트에 대한 액세스 권한이 있다면 다른 옵션을 비교했을 것입니다. 정적 분석 및 부분 벤치 마크는 유용하지만 간단한 벤치마킹 및/또는 프로파일 링을 수행 할 때 최종 방법입니다. –

+0

흥미 롭습니다. 이전에이 도구들 중 하나를 본적이 없었습니다 --- 그래서 이제는 그들에 대해 알게되어 정말 기쁩니다! – DilithiumMatrix