이전에 작성한 글
https://jspark33.tistory.com/114
컴파일러 VS 인터프리터
컴파일러란?컴파일러는 프로그래밍 언어로 작성된 코드를 기계어로 변환하는 프로그램이다. 컴파일 과정은 소스 코드를 한 번에 전체적으로 분석하고, 이를 실행 파일로 변환하여 이후 실행할
jspark33.tistory.com
에서 인터프리터에 대하여 알아보며
인터프리터 언어 중에 하나인 파이썬의 인터프리터는 어떻게 구성되어있을지 매우 궁금하였다.
파이썬 인터프리터에 대해 알아보려면
파이썬의 기본 구현체인 cPython을 살펴보아야 한다.
https://github.com/python/cpython
GitHub - python/cpython: The Python programming language
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
github.com
CPyton의 소스 코드는 여러 디렉터리로 구성되어 있다.
그중에서도 Python/ 안에 위치해있는
ceval.c가 파이썬 바이트코드를 실행하는 메인 루프로서, 인터프리터의 핵심이라 할 수 있다.
https://github.com/python/cpython/blob/main/Python/ceval.c
cpython/Python/ceval.c at main · python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
github.com
이 ceval.c 파일 내에 있는 PyEval_EvalFrameEx()함수는 바이트코드를 해석하고 실행하는 메인 루프이다.
이 함수는 바이트코드를 가져와 switch문을 사용해 각각의 명령을 처리한다.
PyEval_EvalFrameEx()함수에 대해 파고 들어보자면
무한 루프 형태로 구현된 함수로서
한 명령어씩 바이트코드를 읽고 실행한다.
바이트 코드의 명령어(opcode)를 가져오고, 이에 따라 실행할 동작을 결정하게된다.
이 때 각 명령어는 switch-case문으로 처리된다
switch (opcode) {
case LOAD_FAST:
// 로컬 변수를 스택에 로드
break;
case BINARY_ADD:
// 스택에서 두 값을 더함
break;
...
}
모든 바이트코드가 끝까지 실행되면 PyEval_EvalFrameEx는 루프를 종료한다.
잠깐 파이썬 코드와 그에 따른 바이트코드에 따른 실행과정을 한 번 살펴보자.
파이썬 코드
x = 1
y = 2
z = x + y
바이트 코드
1 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (x)
2 4 LOAD_CONST 2 (2)
6 STORE_FAST 1 (y)
3 8 LOAD_FAST 0 (x)
10 LOAD_FAST 1 (y)
12 BINARY_ADD
14 STORE_FAST 2 (z)
16 RETURN_VALUE
PyEval_EvalFrameEx 처리 흐름:
- LOAD_CONST 1: 상수 1을 스택에 푸시.
- STORE_FAST x: 스택에서 값을 팝하고 로컬 변수 x에 저장.
- LOAD_CONST 2: 상수 2를 스택에 푸시.
- STORE_FAST y: 스택에서 값을 팝하고 로컬 변수 y에 저장.
- LOAD_FAST x: 로컬 변수 x의 값을 스택에 푸시.
- LOAD_FAST y: 로컬 변수 y의 값을 스택에 푸시.
- BINARY_ADD: 스택의 두 값을 더하고 결과를 푸시.
- STORE_FAST z: 스택의 결과를 로컬 변수 z에 저장.
- RETURN_VALUE: 결과값을 반환하고 종료.
위와 같은 형식으로 파이썬 코드 -> 바이트 코드 -> 인터프리터가 실행되며 코드가 동작하는 것이다.
전에 작성한 게시글에서 말했듯
인터프리터는 결국 big while swtich문이라 할 수 있다.
예시로 wamr 을 직접 빌드하고
디버거로 코드의 실행과정을 따라가보면
switch 문 안에서 런타임이 계속 동작하는 것을 눈으로 확인할 수 있다.
과연 그렇다면 CPython에서도 wamr처험 while switch 코드를 눈으로 확인할 수 있을까?
답은 NO! 다.
왜냐하면 파이썬 인터프리터는 디스패치 테이블이라는 방식을 사용하여 switch 문을 구성하였기에
다른 런타임들과 달리 switch문이 눈에 보이지 않는다.
디스패치 테이블이란
static void* dispatch_table[] = {&&do_halt, &&do_inc, &&do_dec, ...};
#define DISPATCH()
goto *dispatch_table[code[pc++]]
간단하게 말하자면 위 코드와 같이 goto를 활용하여 해당 opcode의 코드로 직접 점프하는 방식이다.
일반적인 switch case문에 비해 15-25% 정도 성능이 향상되어
VM이나 인터프리터에서 자주 사용된다고 한다.
과정을 분석해보니
파이썬 코드가 바이트코드로 변환되고
변환된 바이트코드 명령어(op code)rk
파이썬 인터프리터 코드인 ceval.c에서
PyEval_EvalFrameEx에서는 _PyEvalFrameDefault를 호출하고
_PyEvalFrameDefault안에서 USE_COMPUTED_GOTO가 실행된다.
이 USE_COMPUTED_GOTO는
cpython/Python/ceval_macros.h at cef0a90d8f3a94aa534593f39b4abf98165675b9 · python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
github.com
ceval_macros.h에서
DISPATCH_GOTO()를 통해 디스패치 테이블 방식으로 실행되고
파이썬 바이트 코드의 명령어가 작동되는 코드로 goto한다.
generated_cases.c에서
해당 OPcode로 향해 바이트 코드 명령어가 실행되게 된다.
https://github.com/python/cpython/blob/main/Python/generated_cases.c.h
cpython/Python/generated_cases.c.h at main · python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
github.com
요약하자면
파이썬 인터프리터는 디스패치 테이블 방식을 이용하여 switch case문을 구성하여
더 효율적인 인터프리터를 구현했다!
'Python' 카테고리의 다른 글
터미널에서 한번에 여러개의 파일 만들기 (0) | 2021.06.29 |
---|---|
Python csv를 list로 변환 (0) | 2020.09.12 |
파이썬으로 문자를 보내보자-twilio (1) | 2020.04.21 |