[dis] dis 모듈을 활용해서 바이트코드 분석하기
dis 모듈을 활용해서 바이트코드 분석하기
파이썬 코드 리뷰 꿀팁, 김동현 - PyCon Korea 2022 [https://youtu.be/dzp0-5lInw0]
[파이썬] dis모듈로 바이트 코드 확인 - dis - [https://aia1235.tistory.com/60]
def func():
a = 1
b = 2
c = a + b
return c
- 바이트코드에 익숙하지 않다면 이해하기 어렵다
print(func.__code__.co_code)
print(list(func.__code__.co_code))
- output -
b'\x97\x00d\x01}\x00d\x02}\x01|\x00|\x01z\x00\x00\x00}\x02|\x02S\x00'
[151, 0, 100, 1, 125, 0, 100, 2, 125, 1, 124, 0, 124, 1, 122, 0, 0, 0, 125, 2, 124, 2, 83, 0]
- 이럴 때 사용할 수 있는 모듈이 dis
import dis
import timeit
dis.dis(func)
- output -
1 0 RESUME 0
2 2 LOAD_CONST 1 (1)
4 STORE_FAST 0 (a)
3 6 LOAD_CONST 2 (2)
8 STORE_FAST 1 (b)
4 10 LOAD_FAST 0 (a)
12 LOAD_FAST 1 (b)
14 BINARY_OP 0 (+)
18 STORE_FAST 2 (c)
5 20 LOAD_FAST 2 (c)
22 RETURN_VALUE
- 리스트 값 확인
[151, 0, 100, 1, 125, 0, 100, 2, 125, 1, 124, 0, 124, 1, 122, 0, 0, 0, 125, 2, 124, 2, 83, 0]
print(f'151: {dis.opname[125]}')
print(f'100: {dis.opname[100]}')
print(f'125: {dis.opname[125]}')
- output -
151: STORE_FAST
100: LOAD_CONST
125: STORE_FAST
- 더 쉽게 보고 싶다면
dis.show_code(func)
- output -
Name: func
Filename: /tmp/ipykernel_30725/21688586.py
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 3
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: 1
2: 2
Variable names:
0: a
1: b
2: c
사용 예시
어떤게 더 효율적인지 비교
ee={}
ee['3'] = 32
ee
ew={}
ew.update({'3': 32})
ew
둘 다 {‘3’: 32} 반환
# 서브스크립션
dis.dis('d[k] = v')
- output -
0 0 RESUME 0
1 2 LOAD_NAME 0 (v)
4 LOAD_NAME 1 (d)
6 LOAD_NAME 2 (k)
8 STORE_SUBSCR
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
0 RESUME 0: 이는 코드의 첫 번째 라인을 나타냅니다. RESUME은 실행을 재개하는 지점을 나타내는 명령입니다. 여기서는 실행을 계속 진행합니다.
2 LOAD_NAME 0 (v): 변수 v를 로드합니다.
4 LOAD_NAME 1 (d): 변수 d를 로드합니다.
6 LOAD_NAME 2 (k): 변수 k를 로드합니다.
8 STORE_SUBSCR: 변수 d의 k번째 요소에 변수 v를 할당합니다.
12 LOAD_CONST 0 (None): 상수 None을 로드합니다.
14 RETURN_VALUE: None을 반환하고 코드 실행을 종료합니다.
dis.dis('d.update({k: v})')
- output -
0 0 RESUME 0
1 2 LOAD_NAME 0 (d)
4 LOAD_METHOD 1 (update)
26 LOAD_NAME 2 (k)
28 LOAD_NAME 3 (v)
30 BUILD_MAP 1
32 PRECALL 1
36 CALL 1
46 RETURN_VALUE
0 RESUME 0: 이는 코드의 첫 번째 라인을 나타냅니다. RESUME은 실행을 재개하는 지점을 나타내는 명령입니다. 여기서는 실행을 계속 진행합니다.
2 LOAD_NAME 0 (d): 변수 d를 로드합니다.
4 LOAD_METHOD 1 (update): update라는 메서드를 로드합니다. 이는 변수 d의 값을 업데이트하는 메서드일 수 있습니다.
26 LOAD_NAME 2 (k): 변수 k를 로드합니다.
28 LOAD_NAME 3 (v): 변수 v를 로드합니다.
30 BUILD_MAP 1: 로드한 변수 k와 v를 사용하여 사전 형태의 매핑을 만듭니다.
32 PRECALL 1: 메서드 호출을 준비합니다.
36 CALL 1: update 메서드를 호출하고, 변수 d에 매개변수로 생성한 매핑을 전달합니다. 이를 통해 변수 d를 업데이트합니다.
46 RETURN_VALUE: 결과값을 반환하고 코드 실행을 종료합니다.
하지만, 각각의 상황에 맞추어서 사용하는 것이 중요
비교 해보기
-
Func_else
def Func_else(n): if n % 2 == 0: return True else: return False dis.dis(Func_else) ## -------------------- output -------------------- ## 1 0 RESUME 0 2 2 LOAD_FAST 0 (n) 4 LOAD_CONST 1 (2) 6 BINARY_OP 6 (%) 10 LOAD_CONST 2 (0) 12 COMPARE_OP 2 (==) 18 POP_JUMP_FORWARD_IF_FALSE 2 (to 24) 3 20 LOAD_CONST 3 (True) 22 RETURN_VALUE 5 >> 24 LOAD_CONST 4 (False) 26 RETURN_VALUE ## ------------------------------------------------ ##
-
Func_elif
def Func_elif(n): if n % 2 == 0: return True elif n % 2 == 1: return False dis.dis(Func_elif) ## -------------------- output -------------------- ## 1 0 RESUME 0 2 2 LOAD_FAST 0 (n) 4 LOAD_CONST 1 (2) 6 BINARY_OP 6 (%) 10 LOAD_CONST 2 (0) 12 COMPARE_OP 2 (==) 18 POP_JUMP_FORWARD_IF_FALSE 2 (to 24) 3 20 LOAD_CONST 3 (True) 22 RETURN_VALUE 4 >> 24 LOAD_FAST 0 (n) 26 LOAD_CONST 1 (2) 28 BINARY_OP 6 (%) 32 LOAD_CONST 4 (1) 34 COMPARE_OP 2 (==) 40 POP_JUMP_FORWARD_IF_FALSE 2 (to 46) 5 42 LOAD_CONST 5 (False) 44 RETURN_VALUE 4 >> 46 LOAD_CONST 0 (None) 48 RETURN_VALUE ## ------------------------------------------------ ##
-
Func_if
def Func_if(n): if n % 2 == 0: return True return False dis.dis(Func_if) ## -------------------- output -------------------- ## 1 0 RESUME 0 2 2 LOAD_FAST 0 (n) 4 LOAD_CONST 1 (2) 6 BINARY_OP 6 (%) 10 LOAD_CONST 2 (0) 12 COMPARE_OP 2 (==) 18 POP_JUMP_FORWARD_IF_FALSE 2 (to 24) 3 20 LOAD_CONST 3 (True) 22 RETURN_VALUE 4 >> 24 LOAD_CONST 4 (False) 26 RETURN_VALUE ## ------------------------------------------------ ##
-
전체 실행 시간 측정
# 실행 시간 측정 함수 def measure_execution_time(func, n): start_time = timeit.default_timer() result = func(n) end_time = timeit.default_timer() execution_time = end_time - start_time return execution_time, result # 실행 시간 측정 n = 1 # 테스트할 값 # Func_else(n)의 실행 시간 측정 execution_time_else, result_else = measure_execution_time(Func_else, n) print(f"Func_else 실행 시간: {execution_time_else} 초") print(f"Func_else 결과: {result_else}") # Func_elif(n)의 실행 시간 측정 execution_time_elif, result_elif = measure_execution_time(Func_elif, n) print(f"Func_elif 실행 시간: {execution_time_elif} 초") print(f"Func_elif 결과: {result_elif}") # Func_if(n)의 실행 시간 측정 execution_time_if, result_if = measure_execution_time(Func_if, n) print(f"Func_if 실행 시간: {execution_time_if} 초") print(f"Func_if 결과: {result_if}")
- result -
Func_else 실행 시간: 1.989305019378662e-06 초 Func_else 결과: False Func_elif 실행 시간: 1.5348196029663086e-06 초 Func_elif 결과: False Func_if 실행 시간: 1.4528632164001465e-06 초 Func_if 결과: False
댓글남기기