목표와 아키텍처 개요¶
이 문서는 PyPy의 목표와 아키텍처를 개괄적으로 설명합니다. PyPy 사용하기나 PyPy를 직접 고쳐 보는 데 관심이 있으시다면 시작하기 섹션을 살펴보십시오.
미션 선언문¶
저희는 RPython 툴체인을 사용하여 저수준 세부 사항을 직접 작성하지 않고도 새롭고 진보된 고수준 기능을 구현할 수 있는, 규격을 준수하며 유연하고 빠른 Python 언어 구현체를 제공하는 것을 목표로 합니다. 저희는 이것을 PyPy라고 부릅니다.
상위 수준 목표¶
저희가 변환 프레임워크를 개발하는 주된 동기는 모든 기능을 갖추고 사용자 정의가 가능하며 빠르고 호환성이 매우 높은 Python 구현을 제공하는 것입니다. 이 구현은 매우 다양한 플랫폼에서 동작하고 상호작용하며, 새로운 고급 언어 기능을 빠르게 도입할 수 있게 해 줍니다.
이 Python 구현은 RPython으로 작성된 비교적 단순한 인터프리터이며, 어떤 면에서는 Python의 C 참조 구현인 CPython보다 이해하기 쉽습니다. 저희는 RPython의 높은 추상화 수준과 유연성을 활용하여, 전통적인 방식이라면 소스 코드 전반에 걸친 변경이 필요했을 기능이나 구현 기법을 빠르게 실험하고 있습니다. 예를 들어, PyPy의 Python 인터프리터는 지연 계산되는 객체를 선택적으로 제공할 수 있습니다. 이는 작은 확장이지만 CPython에서라면 전역적인 변경이 필요했을 것입니다. 또 다른 예는 가비지 컬렉션 기법입니다. CPython이 참조 카운팅에 기반하지 않은 가비지 컬렉터를 사용하도록 변경하는 것은 대규모 작업이 되겠지만, PyPy에서는 이것이 변환 프레임워크 안에 국한된 문제이며 인터프리터 소스 코드와는 완전히 독립적입니다.
PyPy Python 인터프리터¶
PyPy의 Python 인터프리터는 RPython으로 작성되었으며 Python 언어 전체를 구현합니다. 이 인터프리터는 CPython의 동작을 매우 충실하게 재현합니다. 이 인터프리터에는 다음과 같은 핵심 구성 요소가 있습니다.
- 사용자 애플리케이션의 소스 코드로부터 Python 코드 객체를 생성하는 역할을 담당하는 바이트코드 컴파일러;
- Python 코드 객체의 해석을 담당하는 바이트코드 평가기;
- 애플리케이션이 보는 Python 객체를 생성하고 조작하는 역할을 맡는 표준 객체 공간(object space)
바이트코드 컴파일러는 여러 유연한 패스(토크나이저, 렉서, 파서, 추상 구문 트리 빌더, 바이트코드 생성기)를 차례로 거쳐 간결한 바이트코드 형식을 만들어 내는 전처리 단계입니다. 바이트코드 평가기는 이 바이트코드를 해석합니다. 바이트코드 평가기는 사용자 객체에 대한 실제 조작을 모두 객체 공간(object space)에 위임하는 방식으로 대부분의 작업을 수행합니다. 객체 공간은 내장 타입의 라이브러리라고 생각할 수 있습니다. 객체 공간은 정수나 리스트 같은 사용자 객체의 구현뿐 아니라 덧셈이나 진릿값 검사 같은 객체 간 연산도 정의합니다.
바이트코드 평가기와 객체 공간(object space)을 이렇게 분리하면 유연성이 크게 높아집니다. 서로 다른 객체 공간을 끼워 넣으면 Python 객체가 다르게 동작하거나 더 풍부하게 동작하도록 만들 수 있습니다.
계층¶
RPython¶
RPython은 저희가 인터프리터를 작성하는 데 사용하는 언어입니다. PyPy 프로젝트 전체가 RPython으로 작성된 것은 아니며, 변환 과정에서 컴파일되는 부분만 RPython으로 작성되어 있습니다. 흥미로운 점은 RPython에 파서가 없다는 것입니다. RPython은 살아 있는 Python 객체로부터 컴파일되며, 덕분에 임포트 시점에 온갖 종류의 메타프로그래밍을 할 수 있습니다. 요컨대, 파이썬은 RPython을 위한 메타프로그래밍 언어입니다.
RPython 표준 라이브러리는 rlib 하위 디렉터리에 있습니다.
더 읽을거리가 필요하시면 Getting Started with RPython을 참고하십시오. RPython 위에 인터프리터를 작성하지 않고도 RPython으로 무엇을 할 수 있는지 다른 관점에서 살펴보시려면 RPython By Example을 참고하십시오.
변환¶
변환 툴체인 - RPython을 흐름 그래프로 변환한 다음 C로 변환하는 일을 담당하는 부분입니다. 이에 대한 자세한 내용은 architecture 문서에 나와 있습니다.
이는 rpython 디렉터리 안의 flowspace, annotator, rtyper에 있습니다.
PyPy 인터프리터¶
이 코드는 pypy 디렉터리에 있습니다. pypy/interpreter는 RPython으로 작성된 표준 Python 인터프리터입니다. 이 인터프리터가 RPython으로 작성되었다는 사실은 처음에는 잘 드러나지 않습니다. 내장 모듈은 pypy/module/*에 작성되어 있습니다. CPython이 C로 구현하는 일부 모듈은 PyPy에서 그냥 순수 Python으로 작성되어 있으며, 이 모듈들은 최상위 lib_pypy 디렉터리에 있습니다. Python 표준 라이브러리(PyPy에 맞게 몇 가지를 수정한 것)는 lib-python에 있습니다.
JIT 컴파일러¶
JIT(Just-in-Time) 컴파일러: 저희에게는 트레이싱 JIT가 있습니다. 이 JIT는 인터프리터가 해석하는 사용자 프로그램이 아니라 RPython으로 작성된 인터프리터 자체를 트레이싱합니다. 그 결과 이 JIT는 어떤 인터프리터에도, 즉 어떤 언어에도 적용됩니다. 하지만 이 JIT가 올바르게 동작하게 만드는 일은 간단하지 않습니다. 정확한 “힌트”가 몇 가지 필요하고, 경우에 따라 인터프리터를 조금 리팩터링해야 할 수도 있습니다. JIT 자체도 거의 독립적인 여러 부분으로 이루어져 있습니다. rpython/jit/metainterp에 있는 트레이서 자체, 잔여 연산 목록을 최적화하는 rpython/jit/metainterp/optimizer의 옵티마이저, 그리고 이를 기계어 코드로 변환하는 rpython/jit/backend/<machine-name>의 백엔드가 그것입니다. 새 백엔드를 작성하는 것은 이 프로젝트에 참여하는 전통적인 방법입니다.
가비지 컬렉터¶
가비지 컬렉터(GC): CPython의 C 코드에 익숙하시다면 눈치채셨겠지만, RPython 코드에는 Py_INCREF/Py_DECREF에 해당하는 것이 없습니다. Garbage Collection in RPython은 변환 과정에서 삽입됩니다. 게다가 이는 참조 카운팅이 아니라, 역시 RPython 코드로 작성된 실제 GC입니다. 지금까지 만든 것 중 가장 좋은 GC는 rpython/memory/gc/incminimark.py에 있습니다.