비교할 문제
multiprocessing 직렬화 비용에서 확인할 질문은 process에 전달하는 payload 크기가 pickle·IPC 왕복 비용에 얼마나 영향을 주는가입니다. thread와 process 후보가 같은 bytes 길이와 오류를 반환하게 하고 executor startup과 payload 전송 측정을 구분합니다. 결과값, 순서, 오류 처리와 부수 효과가 다르면 같은 성능 비교가 아닙니다. 먼저 두 후보가 같은 일을 하는지 작은 입력으로 확인합니다.
원인 분석과 재현
process 사이 argument와 result는 대개 pickle·IPC를 거치므로 큰 object는 계산보다 복사·직렬화가 더 비쌀 수 있습니다.
payload 1KB·10MB, 작업 20개에서 pool을 미리 만든 뒤 wall time과 parent·worker RSS를 기록합니다. runtime version, 운영체제, CPU와 memory 제한도 결과와 함께 기록합니다.
비교 질문은 process에 전달하는 payload 크기가 pickle·IPC 왕복 비용에 얼마나 영향을 주는가입니다.
| 후보 | 유리한 조건 | 함께 치르는 비용 |
|---|---|---|
| thread pool | 같은 process 안에서 객체를 공유할 수 있는 작업 | pure Python CPU 병렬성은 GIL 영향을 받음 |
| process pool | 격리와 CPU core 사용이 필요한 작업 | argument·result pickle, IPC와 process memory 비용 |
| shared memory·file | 복사하기 큰 read-only data | 수명주기, 동기화와 cleanup을 직접 관리 |
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from time import perf_counter
def payload_size(payload: bytes) -> int:
return len(payload)
if __name__ == '__main__':
for size in (1_024, 10 * 1024 * 1024):
payload = b'x' * size
for executor_type in (ThreadPoolExecutor, ProcessPoolExecutor):
with executor_type(max_workers=2) as executor:
list(executor.map(payload_size, [b'warm-up'] * 4))
started = perf_counter()
result = list(executor.map(payload_size, [payload] * 20))
print(size, executor_type.__name__, perf_counter() - started, sum(result))
해결 방안과 판정
timeit.repeat() 결과 전체를 보관하고, 작은 코드 조각의 하한을 볼 때는 공식 문서가 안내하는 최솟값도 함께 확인합니다. 서비스 지연을 재는 경우에는 median과 p95를 별도로 기록합니다. CPU time, allocation, RSS·heap, GC, I/O 중 이 작업에 직접 관련된 지표만 선택합니다. 작은 계산에 큰 payload를 보내면 process 병렬화 이득보다 직렬화·복사 비용이 클 수 있습니다. 실제 작업의 계산량과 payload 비율로 다시 측정합니다.
차이가 반복 실행의 흔들림보다 작다면 더 단순하고 읽기 쉬운 구현을 선택합니다. 차이가 충분히 크더라도 실제 요청 전체의 지연과 자원 사용이 개선되는지 확인한 뒤 적용합니다.
공식 문서와 적용 범위
문서의 기본값과 동작은 version에 따라 달라질 수 있습니다. 현재 Python 구현과 version, dependency, 입력 크기와 실행 환경에서 다시 확인합니다.