pypy3

개요

pypy3 [options] [-c cmd|-m mod|file.py|-] [arg…]

옵션

-i 스크립트를 실행한 후 대화형으로 검사합니다.
-O assert 문은 건너뜁니다.
-OO -O에 더해, 모듈을 임포트할 때 docstring도 제거합니다.
-c CMD CMD로 전달된 프로그램입니다(옵션 목록을 종료합니다).
-S 초기화 시 import site를 수행하지 않습니다.
-s 사용자 사이트 디렉터리를 sys.path에 추가하지 않습니다.
-u stdoutstderr를 버퍼링하지 않는 바이너리 모드로 사용합니다.
-h, --help 도움말 메시지를 표시하고 종료합니다.
-m MOD 스크립트로 실행할 라이브러리 모듈입니다(옵션 목록을 종료합니다).
-W ARG 경고를 제어합니다(argaction:message:category:module:lineno 형식입니다).
-E 환경 변수(PYTHONPATH 등)를 무시합니다.
-B 바이트코드(.pyc) 파일 작성을 비활성화합니다.
-X track-resources
 파일이나 소켓이 가비지 컬렉터에 의해 닫힐 때마다 ResourceWarning을 발생시킵니다.
--version PyPy 버전을 출력합니다.
--info 이 PyPy 실행 파일에 관한 변환 정보를 출력합니다.
--jit ARG 저수준 JIT 매개변수입니다. 대부분 내부용입니다. 더 자세한 정보를 보려면 --jit help를 실행하십시오.

환경

PYTHONPATH
pypy3의 모듈 검색 경로에 디렉터리를 추가합니다. 형식은 셸의 PATH와 동일합니다.
PYTHONSTARTUP
이 변수가 참조하는 스크립트는 대화형 모드에서 첫 프롬프트가 표시되기 전에 실행됩니다.
PYTHONDONTWRITEBYTECODE
비어 있지 않은 값으로 설정하면 -B 옵션과 동일합니다. .pyc 파일 작성을 비활성화합니다.
PYTHONINSPECT
비어 있지 않은 값으로 설정하면 -i 옵션과 동일합니다. 지정된 스크립트를 실행한 후 대화형으로 검사합니다.
PYTHONIOENCODING
이 값이 설정되면 stdin/stdout/stderr에 사용되는 인코딩을 재정의합니다. 구문은 encodingname:errorhandler입니다. errorhandler 부분은 선택 사항이며 str.encode와 동일한 의미를 가집니다.
PYTHONNOUSERSITE
비어 있지 않은 값으로 설정하면 -s 옵션과 동일합니다. 사용자 사이트 디렉터리를 sys.path에 추가하지 않습니다.
PYTHONWARNINGS
설정된 경우, -W 옵션(경고 제어)과 동일합니다. 값은 쉼표로 구분된 -W 매개변수 목록이어야 합니다.
PYPYLOG

비어 있지 않은 값으로 설정하면 로깅을 활성화하며, 형식은 다음과 같습니다:

fname 또는 +fname
프로파일링용 로깅: 모든 debug_start/debug_stop을 포함하지만 중첩된 debug_print는 포함하지 않습니다. stderr에 로그를 남기려면 fname-로 지정할 수 있습니다. fname에 :가 있는 경우 +fname 형식을 사용할 수 있습니다.
:fname
debug_print를 포함한 전체 로깅입니다.
prefix:fname
조건부 로깅입니다. 접두사는 쉼표로 구분하여 여러 개를 지정할 수 있습니다. 이름이 접두사와 일치하는 섹션만 로깅됩니다.

PYPYLOG=jit-log-opt,jit-backend:logfile는 PyPy에서 성능 문제를 디버깅하기 위한 도구인 jitviewer에 적합한 로그를 생성합니다.

PYPY_IRC_TOPIC
비어 있지 않은 값으로 설정하면 대화형 모드 시작 시 #pypy IRC 주제를 무작위로 출력합니다.

PyPy의 기본 가비지 컬렉터는 incminimark라고 불리며, 점진적(incremental)이고 세대별(generational)로 이동하는 컬렉터입니다. 여기서는 이것이 어떻게 동작하는지, 그리고 작업 부하에 맞게 어떻게 조정할 수 있는지 간단히 설명하고자 합니다.

Incminimark은 먼저 객체를 이른바 nursery*(어린 객체를 위한 장소)에 할당하는데, 이곳에서는 할당이 단순히 포인터를 증가시키는 것에 지나지 않으므로 매우 저렴합니다. nursery 크기는 매우 중요한 변수입니다 - 작업 부하(프로세스가 하나인지 여럿인지)와 캐시 크기에 따라 *PYPY_GC_NURSERY 환경 변수로 이 값을 실험해보고 싶을 수 있습니다. nursery가 가득 차면 마이너 컬렉션(minor collection)이 수행됩니다. 해제된 객체는 더 이상 참조할 수 없게 되어, 단지 더 이상 참조되지 않음으로써 소멸합니다. 반면, 아직 살아 있는 것으로 확인된 객체는 살아남아야 하며 nursery에서 구세대(old generation)로 복사됩니다. 이때 동일한 크기의 객체들의 집합인 아레나(arena)로 복사되거나, 더 큰 경우에는 malloc으로 직접 할당됩니다. (세 번째 범주인 매우 큰 객체는 처음부터 nursery 밖에 할당되며 결코 이동하지 않습니다.)

Incminimark은 점진적 GC이므로 주요 수집(major collection)도 점진적으로 이루어집니다: 목표는 1ms보다 긴 일시 정지가 없도록 하는 것이지만, 실제로는 힙(heap)의 크기와 특성에 따라 달라지며, 간혹 10-100ms 사이의 일시 정지가 발생할 수 있습니다.

반수동 가비지 컬렉션 관리

프로그램에서 낮은 지연 시간이 중요한 부분이 있다면, 예상치 못한 멈춤을 피하기 위해 GC가 실행되는 시점을 정확히 제어하고 싶을 수 있습니다. 이는 메이저 컬렉션(major collection)에만 영향을 미치며, 마이너 컬렉션(minor collection)은 평소처럼 계속 동작한다는 점에 유의하시기 바랍니다.

위에서 설명했듯이, 전체 메이저 컬렉션은 N단계로 구성되며, N은 힙의 크기에 따라 달라집니다. 일반적으로 말해, 컬렉션을 완료하는 데 몇 단계가 필요할지 예측하는 것은 불가능합니다.

gc.enable()gc.disable()은 GC가 자동으로 수집 단계를 실행할지 여부를 제어합니다. GC가 비활성화되면, gc.collect()gc.collect_step()을 수동으로 호출하지 않는 한 메모리 사용량이 무한정 증가합니다.

gc.collect()는 전체 메이저 컬렉션을 실행합니다.

gc.collect_step()는 단일 수집 단계를 실행합니다. 이것은 해당 가비지 컬렉터 훅에 전달되는 것과 동일한 GcCollectStepStats타입의 객체를 반환합니다. 다음 코드는 대략 gc.collect()와 동등합니다:

while True:
    if gc.collect_step().major_is_done:
        break

이 API의 실제 사용 예시로, GC가 금지된 영역을 표시하기 위한 with customgc.nogc() 컨텍스트 관리자도 제공하는 서드파티 모듈 pypytools.gc.custom을 참고할 수 있습니다.

단편화

“파편화(fragmentation)” 문제를 논의하기 전에, 약간의 정확한 정의가 필요합니다. 서로 관련되어 있지만 구별되는 두 가지 종류의 문제가 있습니다:

  • 프로그램이 많은 메모리를 할당한 다음, 모든 참조를 제거하여 이를 전부 해제하면, RSS가 감소할 것이라고 예상할 수 있습니다. (RSS = 리눅스에서 “top”으로 볼 수 있는 Resident Set Size로, OS 관점에서 실제 메모리 사용량의 근사치입니다.) 이런 일이 일어나지 않을 수도 있습니다: RSS가 최고값에 머물러 있을 수 있습니다. 이 문제는 더 정확히는 프로세스가 “free” 메모리를 OS에 반환하지 않기 때문에 발생합니다. 우리는 이 경우를 “반환되지 않은 메모리(unreturned memory)”라고 부릅니다.
  • 위 작업을 수행한 후 RSS가 줄어들지 않았다면, 적어도 이후의 할당이 RSS를 더 증가시키지는 않아야 합니다. 즉, 프로세스는 반환되지 않은 메모리가 남아 있는 한 이를 재사용해야 합니다. 만약 이런 일이 일어나지 않는다면 RSS는 더욱 커지게 되고, 실질적인 단편화 문제가 발생합니다.

gc.get_stats

gc 모듈에는 get_stats(memory_pressure=False)라는 특별한 함수가 있습니다.

memory_pressure는 GC 외부에서 할당된 객체로 인한 메모리 압박을 보고할지 여부를 제어하는데, 이를 위해서는 전체 힙을 순회해야 하므로 비용 문제로 기본적으로 비활성화되어 있습니다. 원인을 알 수 없는 메모리 소실을 디버깅할 때는 이를 활성화하십시오.

예시 호출은 다음과 같습니다:

>>> gc.get_stats(True)
Total memory consumed:
GC used:            4.2MB (peak: 4.2MB)
   in arenas:            763.7kB
   rawmalloced:          383.1kB
   nursery:              3.1MB
raw assembler used: 0.0kB
memory pressure:    0.0kB
-----------------------------
Total:              4.2MB

Total memory allocated:
GC allocated:            4.5MB (peak: 4.5MB)
   in arenas:            763.7kB
   rawmalloced:          383.1kB
   nursery:              3.1MB
raw assembler allocated: 0.0kB
memory pressure:    0.0kB
-----------------------------
Total:                   4.5MB

시작 직후인 이 특정 경우, 가비지 컬렉터는 비교적 적은 메모리를 소비하며, 할당되었지만 사용되지 않은 메모리는 그보다 더욱 적습니다. 반환되지 않은 메모리가 많거나 실제 단편화가 있는 경우, “allocated”가 “used”보다 훨씬 높을 수 있습니다. 일반적으로 “peak”는 RSS로 보고되는 실제 소비 메모리에 더 가깝습니다. 실제로 메모리를 OS에 반환하는 것은 어렵고 아직 해결되지 않은 문제입니다. PyPy에서는 아레나(각각 4 또는 8 KB인 페이지 64개로 이루어진 연속 블록)가 완전히 비어 있을 때만 발생합니다. 이는 “rawmalloced” 카테고리에서도 마찬가지로 드문데, 적어도 일반적인 시스템 malloc()구현에서는 그렇습니다.

다양한 필드의 세부 사항:

  • 아레나(arena) 내 GC - 작은 오래된 객체들이 아레나에 보관됩니다. “할당된(allocated)” 양이 “사용된(used)” 양보다 훨씬 많다면, 반환되지 않은 메모리가 있는 것입니다. 여기서 내부 단편화가 발생할 가능성이 있지만 흔하지는 않습니다. 하지만 이 반환되지 않은 메모리는 “rawmalloced” 섹션의 메모리를 포함하여 어떤 malloc()에도 재사용될 수 없습니다.
  • GC rawmalloced - malloc으로 할당된 대형 객체입니다. 이는 malloc()으로 할당된 현재(첫 번째 텍스트 블록)와 최대(두 번째 텍스트 블록) 메모리를 나타냅니다. malloc()으로 인해 발생한 미반환 메모리 또는 단편화의 양은 쉽게 보고될 수 없습니다. 보통 RSS가 “GC allocated”로 보고된 총 메모리보다 훨씬 크다면 이런 메모리가 있다고 추측할 수 있지만, 이 총량에는 PyPy의 가비지 컬렉터가 전혀 알지 못하는 malloc된 메모리는 포함되지 않는다는 점을 유념하십시오. 그런 것이 있다고 추측된다면, 시스템 malloc 대신 jemalloc을 사용하는 것을 고려하십시오.
  • nursery - nursery를 위해 할당된 메모리 양으로, 시작 시 고정되며 환경 변수를 통해 제어됩니다
  • raw assembler allocated - JIT가 책임지고 있다고 인식하는 어셈블러 메모리의 양
  • 메모리 압박(memory pressure), 요청 시 - GC 객체에 의해 살아있게 유지되지만 GC에서는 계산되지 않는, 외부 malloc(예: SSL 컨텍스트에서 인증서 저장소 로딩)을 통해 할당되었다고 생각되는 메모리의 양

가비지 컬렉터 훅

GC 훅은 특정 GC 이벤트가 발생할 때마다 호출되는 사용자 정의 함수이며, GC 활동과 일시 정지를 모니터링하는 데 사용할 수 있습니다. 다음 속성들을 설정하여 훅을 설치할 수 있습니다:

gc.hooks.on_gc_minor
마이너 컬렉션이 발생할 때마다 호출됩니다. 이는 PYPYLOG 내의 gc-minor 섹션에 해당합니다.
gc.hooks.on_gc_collect_step
메이저 컬렉션(major collection)의 증분 단계(incremental step)가 발생할 때마다 호출됩니다. 이는 PYPYLOG 내부의 gc-collect-step 섹션에 해당합니다.
gc.hooks.on_gc_collect
마지막 증분 단계 이후, 메이저 컬렉션이 완전히 끝났을 때 호출됩니다. 이는 PYPYLOG 내의 gc-collect-done 섹션에 해당합니다.

훅을 제거하려면 해당 속성을 None으로 설정하면 됩니다. 모든 훅을 한 번에 설치하려면 gc.hooks.set(obj)를 호출할 수 있으며, 이는 obj에서 on_gc_* 메서드를 찾습니다. 모든 훅을 한 번에 제거하려면 gc.hooks.reset()을 호출할 수 있습니다.

훅(hook)에 의해 호출되는 함수들은 이벤트에 대한 다양한 통계를 담고 있는 단일 stats 인자를 받습니다.

PyPy는 GC 이벤트 직후에는 훅(hook)을 호출할 수 없고, 인터프리터가 알려진 상태에 있으며 사용자 정의 코드를 호출해도 무해한 지점에 도달할 때까지 기다려야 한다는 점에 유의하십시오. 훅이 호출되기 전에 여러 이벤트가 발생할 수 있습니다. 이 경우 stats.count 값을 확인하면 마지막으로 훅이 호출된 이후 이벤트가 몇 번 발생했는지 알 수 있습니다. 마찬가지로, stats.duration은 마지막으로 훅이 호출된 이후 이 특정 이벤트에 대해 GC가 소비한 시간을 담고 있습니다.

반면에, stats 객체의 다른 모든 필드는 시리즈의 마지막 이벤트에만 상대적입니다.

on_gc_minor훅에서 GcMinorStats의 속성은 다음과 같습니다:

count
마지막 훅(hook) 호출 이후 발생한 마이너 컬렉션(minor collection)의 개수입니다.
duration
이전 후크 호출 이후 마이너 컬렉션 내부에서 소요된 총 시간(초 단위)입니다.
duration_min
마지막 훅 호출 이후 가장 빠른 마이너 컬렉션의 소요 시간입니다.
duration_max
마지막 훅(hook) 호출 이후 가장 느렸던 마이너 컬렉션(minor collection)의 소요 시간입니다.
total_memory_used
마이너 컬렉션이 끝날 때 사용된 메모리 양(바이트 단위)입니다. 여기에는 아레나에서 사용된 메모리(가비지 컬렉터가 관리하는 메모리용)와 raw-malloc으로 할당된 메모리(예: numpy 배열의 내용)가 포함됩니다.
pinned_objects
고정된(pinned) 객체의 수입니다.

on_gc_collect_step 훅의 GcCollectStepStats에 대한 속성은 다음과 같습니다:

count, duration, duration_min, duration_max
위 내용을 참조하십시오.
oldstate, newstate
단계 전후의 GC 상태를 나타내는 정수입니다.
major_is_done
이것이 메이저(major) 컬렉션의 마지막 단계였는지 여부를 나타내는 불리언입니다.

oldstatenewstate의 값은 gc.GcCollectStepStats 내부에 정의된 다음 상수 중 하나입니다: STATE_SCANNING, STATE_MARKING, STATE_SWEEPING, STATE_FINALIZING, STATE_USERDEL. GC_STATES 튜플의 인덱스를 사용해 그 문자열 표현을 얻을 수 있습니다.

on_gc_collect 훅에서 GcCollectStats의 속성은 다음과 같습니다:

count
위 내용을 참조하십시오.
num_major_collects
시작 이후 지금까지 수행된 주요 컬렉션의 총 횟수입니다. count와 달리, 이 값은 항상 증가하는 카운터이며 호출 사이에 초기화되지 않습니다.
arenas_count_before, arenas_count_after
메이저 컬렉션 전후에 사용된 아레나(arena)의 수입니다.
arenas_bytes
가비지 컬렉션이 관리하는 객체가 사용하는 총 바이트 수입니다.
rawmalloc_bytes_before, rawmalloc_bytes_after
메이저 컬렉션 전후에 raw-malloc으로 할당된 객체가 사용하는 총 바이트 수입니다.
pinned_objects
고정(pinned)된 객체의 개수입니다.

GcCollectStatsduration 필드를 가지고 있지 않다는 점에 유의하십시오. 이는 모든 GC 작업이 gc-collect-step 내부에서 수행되기 때문입니다: gc-collect-done은 추가적인 통계만 제공할 뿐, 실제 작업은 전혀 수행하지 않습니다.

다음은 GC 후크를 사용하는 예시입니다:

import sys
import gc

class MyHooks(object):
    done = False

    def on_gc_minor(self, stats):
        print 'gc-minor:        count = %02d, duration = %d' % (stats.count,
                                                                stats.duration)

    def on_gc_collect_step(self, stats):
        old = gc.GcCollectStepStats.GC_STATES[stats.oldstate]
        new = gc.GcCollectStepStats.GC_STATES[stats.newstate]
        print 'gc-collect-step: %s --> %s' % (old, new)
        print '                 count = %02d, duration = %d' % (stats.count,
                                                                stats.duration)

    def on_gc_collect(self, stats):
        print 'gc-collect-done: count = %02d' % stats.count
        self.done = True

hooks = MyHooks()
gc.hooks.set(hooks)

# simulate some GC activity
lst = []
while not hooks.done:
    lst = [lst, 1, 2, 3]

환경 변수

PyPy의 기본 incminimark 가비지 컬렉터는 여러 환경 변수를 통해 설정할 수 있습니다:

PYPY_GC_NURSERY
너서리(nursery) 크기입니다. 기본값은 최종 레벨 캐시의 1/2이며, 알 수 없는 경우 4M이고, 최종 레벨 캐시가 너무 작은 경우에도 4M입니다. 작은 값(예: 1 또는 1KB)은 디버깅에 유용합니다.
PYPY_GC_NURSERY_DEBUG
0이 아닌 값으로 설정하면, 디버깅에 도움이 되도록 너서리(nursery)를 가비지로 채웁니다.
PYPY_GC_INCREMENT_STEP
마킹 단계에서 마킹되는 메모리의 크기입니다. 기본값은 nursery 크기의 2배입니다. 너무 높게 설정하면 GC가 전혀 점진적(incremental)이지 않게 됩니다. 최솟값은 마이너 컬렉션에서 살아남는 크기의 1.5배로 설정되어 있어, 항상 무언가를 회수하게 됩니다.
PYPY_GC_MAJOR_COLLECT
메이저 컬렉션 메모리 계수. 기본값은 1.82이며, 이는 이전 메이저 컬렉션이 끝난 시점에 실제로 사용된 메모리의 1.82배에 해당하는 메모리가 소비되면 메이저 컬렉션을 트리거한다는 의미입니다.
PYPY_GC_GROWTH
메이저 컬렉션 임계값의 최대 증가율입니다. 기본값은 1.4입니다. 메모리 사용량이 일시적으로 급증하는 경우처럼 메모리가 갑자기 증가할 때 평소보다 더 자주 컬렉션을 수행하는 데 유용합니다.
PYPY_GC_MAX
최대 힙(heap) 크기입니다. 이 한계에 가까워지면 먼저 더 자주 수집하고, 그래도 부족하면 RPython MemoryError를 발생시키며, 그것마저 충분하지 않으면 치명적 오류로 프로그램을 종료합니다. 1.6GB와 같은 값을 시도해 보십시오.
PYPY_GC_MAX_DELTA
메이저 컬렉션 임계값은 컬렉션 이후 실제로 사용된 양보다 PYPY_GC_MAX_DELTA만큼 더 큰 값으로 설정되지 않습니다. 전체 RAM 크기의 1/8을 기본값으로 사용합니다(32비트 시스템에서는 최대 2/3/4GB로 제한됩니다). 200MB와 같은 값을 시도해 보십시오.
PYPY_GC_MIN
메모리 크기가 이 한계보다 작은 동안에는 수집하지 않습니다. 매우 작은 프로그램에서 모든 시간을 GC에 소비하지 않도록 하는 데 유용합니다. 기본값은 너서리(nursery)의 8배입니다.
PYPY_GC_DEBUG
일반적인 사용에는 너무 느린 수집(collection) 관련 추가 검사를 활성화합니다. 값은 0 (끄기), 1 (주요 수집(major collection) 시) 또는 2 (부수 수집(minor collection) 시에도)입니다.
PYPY_GC_MAX_PINNED
임의의 시점에 고정(pinned)된 객체의 최대 개수입니다. 기본값은 너서리(nursery) 크기와 너서리 내 최대 객체 크기에 따라 결정되는 보수적인 값입니다. 0으로 설정하면 디버깅에 유용합니다.

참고

python3(1)