RPython 시작하기

RPython은 정적으로 컴파일할 수 있는 Python2의 부분집합입니다. PyPy 인터프리터는 대부분 RPython으로 작성되어 있으며(일부는 Python으로 작성됨), RPython 컴파일러는 Python으로 작성되어 있습니다. 이해하기 어려운 부분은 Python이 RPython을 위한 메타프로그래밍 언어라는 점입니다. 즉, “유효한 RPython인지”는 임포트가 완료된 이후의 실제 객체에 대해서만 의미가 있는 질문입니다. 이 부분은 좀 더 설명이 필요할 수 있습니다. entry_point부터 RPython 작성을 시작하며, 좋은 시작점은 rpython/translator/goal/targetnopstandalone.py입니다. 이것이 대단한 일을 하지는 않지만, 시작점은 됩니다. 이제 분석 대상 코드(이 경우 entry_point)가 어떤 함수들을 호출하면, 그 호출들은 추적됩니다. 그렇게 추적된 호출들은 그 자체로 RPython이어야 하지만(그리고 그것들이 호출하는 모든 것 등도 마찬가지), 모듈 파일 전체가 그럴 필요는 없습니다. 메타프로그래밍을 어떻게 사용할 수 있는지 보여주기 위해, 간단한 예제를 하나 살펴보겠습니다(클로저는 RPython이 아니라는 점에 유의하십시오).:

def generator(operation):
    if operation == 'add':
       def f(a, b):
           return a + b
    else:
       def f(a, b):
           return a - b
    return f

add = generator('add')
sub = generator('sub')

def entry_point(argv):
    print add(sub(int(argv[1]), 3) 4)
    return 0

이 예시에서 entry_point는 RPython이고, addsub도 RPython이지만, generator는 그렇지 않습니다.

다음과 같은 입문 수준의 문서를 이용할 수 있습니다:

번역기(translator) 사용해 보기

번역기(translator)는 PyPy 인터프리터를 기반으로 한 도구로서, 충분히 정적인 RPython 프로그램을 저수준 코드로 번역(translation)할 수 있습니다(특히 full Python interpreter를 번역하는 데 사용할 수 있습니다). 이를 실험해 보려면 다음의 일반적인 (CPython2) 버전을 다운로드하여 설치해야 합니다:

대화형 번역기(translator) 셸을 시작하려면 다음을 수행하십시오:

cd rpython
python2 bin/translatorshell.py

번역 가능한 코드의 테스트 스니펫은 rpython/translator/test/snippet.py 파일에 제공되며, 이 파일은 snippet이라는 이름으로 임포트됩니다. 예를 들어:

>>> t = Translation(snippet.is_perfect_number, [int])
>>> t.view()

그 후에, 그래프 뷰어가 나타나며, 흐름 그래프를 대화식으로 살펴볼 수 있게 해줍니다. 이동하려면, 살펴보고 싶은 대상을 클릭하십시오. 사용법에 대한 도움말을 보려면, ‘H’를 누르십시오. 다시 닫으려면, ‘Q’를 누르십시오.

타입 애노테이터 사용해보기

저희는 is_perfect_number와 같은 함수(및 훨씬 더 큰 예제)에 대한 타입을 완전히 추론할 수 있는 타입 애노테이터를 보유하고 있습니다:

>>> t.annotate()
>>> t.view()

변수 이름(빨간색으로 표시됨) 위에 마우스를 올리면 추론된 타입을 확인할 수 있습니다.

흐름 그래프를 C 코드로 번역(translation)하기

그래프는 C 코드로 변환될 수 있습니다:

>>> t.rtype()
>>> lib = t.compile_c()

첫 번째 명령은 연산을 C에서 사용 가능한 저수준 타입(예: int)만을 사용하는 다른 저수준 버전으로 대체합니다. 컴파일된 버전은 이제 .so 라이브러리에 있습니다. 예를 들어 ctypes를 사용하여 실행할 수 있습니다:

>>> f = get_c_function(lib, snippet.is_perfect_number)
>>> f(5)
0
>>> f(6)
1

조금 더 큰 예제

번역기(translator)와 어노테이터(annotator)를 보여주는 중소 규모의 데모가 있습니다.:

python2 bin/rpython --view --annotate translator/goal/bpnn.py

이것은 bpnn.py가 자신을 호출 그래프와 클래스 계층 구조로 표시하게 만듭니다. 함수를 클릭하면 해당 함수의 흐름 그래프가 표시됩니다. 클래스를 클릭하면 해당 클래스 인스턴스의 속성이 표시됩니다. 이 모든 정보(호출 그래프, 지역 변수의 타입, 인스턴스의 속성)는 애노테이터가 계산합니다.

이 예제를 C 코드(bpnn-c라는 실행 파일로 컴파일됨)로 변환하려면, 다음과 같이 입력하면 됩니다.:

python2 bin/rpython translator/goal/bpnn.py

전체 프로그램 번역(translation)하기

완전한 RPython 프로그램을 번역(translation)하기 위한 rpython/bin/rpython 스크립트가 있습니다. 이에 대한 예시로는 약간 수정된 버전의 Pystone이 있습니다.:

python2 bin/rpython translator/goal/targetrpystonedalone

이렇게 하면 실행 파일 “targetrpystonedalone-c”가 생성됩니다.

이 과정의 가장 큰 예는 full Python interpreter를 번역(translation)하는 것입니다. 또한 직접 만든 인터프리터를 위해 이 과정을 설정하는 방법에 대한 FAQ도 있습니다.

RPython을 다루면서 유용하게 쓸 수 있는 몇 가지 환경 변수가 있습니다:

PYPY_USESSION_DIR
RPython은 번역(translation) 과정에서 생성되는 파일(예: 번역된 C 파일)을 저장하기 위해 임시 세션 디렉터리를 사용합니다. PYPY_USESSION_DIR은 이러한 세션 디렉터리의 기본 디렉터리 역할을 합니다. 이 변수의 기본값은 시스템의 임시 디렉터리입니다.
PYPY_USESSION_KEEP
RPython은 기본적으로 PYPY_USESSION_DIR 내부의 세션 디렉터리 중 최근 PYPY_USESSION_KEEP개(기본값 3)만 유지합니다. C 파일을 더 오래 보존하려면 이 값을 늘리십시오(lldebug 빌드를 많이 생성할 때 유용합니다).

출처

  • rpython/translator는 코드 분석과 생성 관련 내용을 담고 있습니다. translator.py부터 읽기 시작하면, 다양한 번역(translation) 단계에 관련된 코드 조각들을 쉽게 따라갈 수 있을 것입니다.
  • rpython/annotator는 그래프에 대해 추론할 수 있는 타입 어노테이션의 데이터 모델을 담고 있습니다. 이를 사용하는 그래프 “워커(walker)”는 rpython/annotator/annrpython.py에 있습니다.
  • rpython/rtyper는 RPython 타이퍼(typer)의 코드를 포함합니다. 타이퍼(typer)는 주석이 달린 흐름 그래프(flow graph)를 C 코드와 매우 비슷하게 만드는 방식으로 변환하여, 쉽게 번역될 수 있도록 합니다. 그래프 변환은 rpython/rtyper/rtyper.py의 코드에 의해 제어됩니다. 사용되는 객체 모델은 rpython/rtyper/lltypesystem/lltype.py에서 찾을 수 있습니다. 각 RPython 타입마다 해당 타입에 필요한 저수준 함수를 포함하는 rxxxx.py 파일이 있습니다.
  • rpython/rlib은 rpython에서 사용할 수 있는 것들인 RPython 표준 라이브러리를 포함합니다.