함수 안에서 파이썬 코드가 더 빠른 이유
파이썬 코드를 모듈 수준에서 그대로 실행하는 것과 함수 안에서 실행하는 것을 비교해 보면, 후자가 눈에 띄게 빠르다는 사실을 알 수 있습니다. 왜 같은 코드가 함수 안에서 더 빨리 동작하는 걸까요? 핵심은 지역 변수 접근 속도와 CPython의 컴파일 방식에 있습니다.
지역 변수와 전역 변수의 속도 차이
일반적으로 파이썬 함수 안에서 지역 변수(local variable)를 저장하고 읽는 것이 전역 변수(global variable)보다 훨씬 빠릅니다. 이것이 함수 사용 시 성능이 향상되는 가장 큰 이유입니다.
또한 지역/전역 변수의 저장 시간 차이 외에도, CPython의 오프코드 예측(opcode prediction) 메커니즘이 함수 실행을 더욱 빠르게 만들어 줍니다.
CPython과 바이트코드 평가 루프
CPython은 python.org에서 내려받는 표준 파이썬 구현체입니다. 'CPython'이라는 이름은 이후에 등장한 다른 파이썬 구현체들과 구별하기 위해서이며, 프로그래밍 언어로서의 파이썬과 언어 엔진 구현 자체를 구분하기 위해서도 사용됩니다.
CPython은 C 언어로 작성되어 있으며, 우리가 작성한 파이썬 코드를 바이트코드(bytecode)로 컴파일한 뒤, 이를 평가 루프(evaluation loop)에서 해석하며 실행합니다.
함수 컴파일 시 지역 변수의 처리 방식
함수가 컴파일될 때 지역 변수들은 고정 크기 배열(딕셔너리가 아님)에 저장되고, 각 변수 이름은 인덱스에 할당됩니다. 함수 안에서는 지역 변수를 동적으로 추가할 수 없기 때문에 이런 최적화가 가능합니다.
결과적으로 지역 변수를 읽어오는 작업은 단순히 배열에서 포인터를 조회하고 해당 PyObject의 참조 카운트를 1 증가시키는 수준에 그칩니다. 이 비용은 사실상 무시할 수 있을 정도로 작습니다.
전역 변수 조회와의 비교
반면 전역 변수 조회는 실제 딕셔너리 검색 과정을 거치므로 해시 계산 등 추가적인 비용이 발생합니다.
참고로, 변수를 전역으로 만들려면 global 키워드를 명시해야 하는 이유도 여기에 있습니다. 스코프 내부에서 변수에 값을 할당하면 컴파일러는 기본적으로 해당 변수 접근에 STORE_FAST 명령을 사용하는데, global 선언이 없으면 이 동작이 유지되기 때문입니다.
전역 조회도 최적화되어 있다
덧붙여 말하자면, 전역 변수 조회 역시 상당히 잘 최적화되어 있습니다. 정말로 느린 것은 별도로, 속성(attribute) 조회입니다!