PyPy의 목표와 아키텍처를 개괄적으로 설명하고, 인터프리터·번역 도구체인·JIT 컴파일러·가비지 컬렉터의 주요 구성 요소를 소개합니다.
이 문서는 PyPy의 목표와 아키텍처를 개괄적으로 설명합니다. PyPy 사용에 관심이 있거나 이를 해킹하고 싶다면, 시작하기 섹션을 살펴보세요.
우리의 목표는 저수준 세부 사항을 인코딩하지 않고도 새로운 고급 고수준 기능을 가능하게 하기 위해 RPython 도구체인을 사용하는, 규격을 준수하고 유연하며 빠른 Python 언어 구현을 제공하는 것입니다. 우리는 이것을 PyPy라고 부릅니다.
번역 프레임워크를 개발하는 우리의 주된 동기는, 완전한 기능을 갖추고 사용자 정의 가능하며, 빠르고 매우 높은 호환성을 갖춘 Python 구현을 제공하는 것입니다. 이 구현은 매우 다양한 플랫폼에서 동작하고 상호작용하며, 새로운 고급 언어 기능을 빠르게 도입할 수 있게 합니다.
이 Python 구현은 비교적 단순한 인터프리터로서 RPython으로 작성되어 있으며, 몇몇 측면에서는 Python의 C 참조 구현인 CPython보다 이해하기 쉽습니다. 우리는 그 고수준성과 유연성을 이용해 기능이나 구현 기법을 빠르게 실험합니다. 전통적인 접근 방식이라면 이는 소스 코드 전반에 걸친 변경을 필요로 했을 것입니다. 예를 들어, PyPy의 Python 인터프리터는 선택적으로 지연 계산 객체를 제공할 수 있는데, 이는 작은 확장이지만 CPython에서는 전역적인 변경이 필요합니다. 또 다른 예는 가비지 수집 기법입니다. 참조 카운팅에 기반하지 않는 가비지 컬렉터를 사용하도록 CPython을 변경하는 일은 대규모 작업이겠지만, PyPy에서는 이것이 번역 프레임워크에 국한된 문제이며 인터프리터 소스 코드와 완전히 직교적입니다.
PyPy의 Python 인터프리터 는 RPython으로 작성되었으며 전체 Python 언어를 구현합니다. 이 인터프리터는 CPython의 동작을 매우 가깝게 에뮬레이션합니다. 여기에는 다음과 같은 핵심 구성 요소가 포함됩니다:
바이트코드 컴파일러 는 유연한 여러 패스(tokenizer, lexer, parser, abstract syntax tree builder, bytecode generator)를 거쳐 압축된 바이트코드 형식을 생성하는 전처리 단계입니다. 바이트코드 평가기 는 이 바이트코드를 해석합니다. 대부분의 작업은 사용자 객체에 대한 실제 조작을 모두 객체 공간 에 위임함으로써 수행됩니다. 후자는 내장 타입의 라이브러리로 생각할 수 있습니다. 이는 정수와 리스트 같은 사용자 객체의 구현뿐 아니라, 덧셈이나 truth-value-testing 같은 객체 간 연산도 정의합니다.
바이트코드 평가기와 객체 공간 사이의 이러한 분리는 큰 유연성을 제공합니다. 서로 다른 객체 공간을 끼워 넣어 Python 객체의 서로 다른 동작이나 확장된 동작을 얻을 수 있습니다.
RPython은 우리가 인터프리터를 작성하는 언어입니다. PyPy 프로젝트 전체가 RPython으로 작성된 것은 아니며, 번역 과정에서 컴파일되는 부분만 그렇습니다. 흥미로운 점은 RPython에는 파서가 없고, 살아 있는 Python 객체로부터 컴파일된다는 것입니다. 이 때문에 import 시점에 온갖 종류의 메타프로그래밍을 수행할 수 있습니다. 짧게 말해, Python은 RPython을 위한 메타프로그래밍 언어입니다.
RPython 표준 라이브러리는 rlib 하위 디렉터리에서 찾을 수 있습니다.
더 읽어보려면 Getting Started with RPython을 참고하고, 인터프리터를 그 위에 작성하지 않고도 RPython으로 무엇을 할 수 있는지에 대한 또 다른 관점은 RPython By Example을 참고하세요.
번역 도구체인 - 이것은 RPython을 플로우 그래프로 번역한 다음 C로 변환하는 일을 담당하는 부분입니다. 이에 대해서는 architecture 문서에 더 많은 설명이 있습니다.
이는 rpython 디렉터리에 있으며, flowspace, annotator, rtyper가 포함됩니다.
이것은 pypy 디렉터리에 있습니다. pypy/interpreter는 RPython으로 작성된 Python용 표준 인터프리터입니다. 이것이 RPython이라는 사실은 처음에는 분명하게 드러나지 않습니다. 내장 모듈은 pypy/module/*에 작성되어 있습니다. CPython이 C로 구현하는 일부 모듈은 단순히 순수 Python으로 작성되어 있으며, 최상위 lib_pypy 디렉터리에 있습니다. Python의 표준 라이브러리(PyPy에 맞추기 위한 약간의 변경 포함)는 lib-python에 있습니다.
Just-in-Time Compiler (JIT): 우리는 사용자가 작성한 프로그램이 아니라, 그 프로그램을 해석하는 RPython으로 작성된 인터프리터를 추적하는 tracing JIT를 가지고 있습니다. 그 결과 이는 어떤 인터프리터, 즉 어떤 언어에도 적용됩니다. 하지만 이것이 올바르게 동작하도록 만드는 일은 간단하지 않습니다. 소수의 정확한 “힌트”와, 경우에 따라 인터프리터의 약간의 리팩터링이 필요합니다. JIT 자체도 거의 독립적인 여러 부분으로 나뉩니다: rpython/jit/metainterp의 tracer 자체, 잔여 연산 목록을 최적화하는 rpython/jit/metainterp/optimizer의 optimizer, 그리고 이를 머신 코드로 바꾸는 rpython/jit/backend/<machine-name>의 backend입니다. 새로운 backend를 작성하는 것은 프로젝트에 입문하는 전통적인 방법입니다.
가비지 컬렉터(GC): CPython의 C 코드에 익숙하다면 눈치챌 수 있듯이, RPython 코드에는 Py_INCREF/Py_DECREF에 해당하는 것이 없습니다. Garbage Collection in RPython은 번역 과정에서 삽입됩니다. 게다가 이것은 참조 카운팅이 아니라, 더 많은 RPython 코드로 작성된 진짜 GC입니다. 지금까지 우리가 가진 것 중 가장 좋은 것은 rpython/memory/gc/incminimark.py에 있습니다.