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 | stdout과 stderr를 버퍼링하지 않는 바이너리 모드로 사용합니다. |
| -h, --help | 도움말 메시지를 표시하고 종료합니다. |
| -m MOD | 스크립트로 실행할 라이브러리 모듈입니다(옵션 목록을 종료합니다). |
| -W ARG | 경고를 제어합니다(arg는 action: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 형식을 사용할 수 있습니다. :fnamedebug_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) 컬렉션의 마지막 단계였는지 여부를 나타내는 불리언입니다.
oldstate와 newstate의 값은 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)된 객체의 개수입니다.
GcCollectStats는 duration 필드를 가지고 있지 않다는 점에 유의하십시오. 이는 모든 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)