Python 3.14 InterpreterPoolExecutor: 멀티코어 병렬 처리와 격리의 대가

CPU 계산이 많은 파이썬 코드를 병렬화할 때, 작업을 어떻게 나누고 결과를 어떻게 돌려받을지부터 정해야 합니다. Python 3.14의 InterpreterPoolExecutor는 같은 프로세스 안에서 여러 인터프리터를 사용하는 선택지입니다. 익숙한 concurrent.futures 인터페이스를 유지하면서도, 데이터 전달 방식은 별도로 설계해야 합니다.

기준일: 2026년 10월 6일. 이 글은 새 기능 발표가 아닌 활용 가이드입니다. Python 3.14.0은 2025년 10월 7일 정식 출시됐으며, 확인한 3.14 계열 유지보수 버전은 2026년 9월 30일 공개된 3.14.8입니다. 예제는 Python 3.14 이상을 대상으로 합니다. 3.14.0 릴리스 · 3.14.8 릴리스

1. 작업자마다 인터프리터가 하나씩

InterpreterPoolExecutor는 작업자 스레드마다 별도의 인터프리터를 둡니다. 각 인터프리터는 자체 GIL을 가지므로 여러 CPU 코어에서 파이썬 코드를 병렬로 실행할 수 있습니다. 여러 인터프리터를 표준 라이브러리에서 다루는 기능은 Python 3.14에 포함됐고, 관련 제안인 PEP 734의 상태는 Final입니다. PEP 734

이 구조에서는 가져온 모듈과 전역 상태도 인터프리터별로 나뉩니다. 주 인터프리터에서 만든 리스트를 작업자가 그대로 수정하고, 그 변경을 주 인터프리터가 즉시 보게 되는 방식을 기대하면 안 됩니다. 입력을 전달하고 반환값을 모으는 구조로 시작하는 편이 이해하기 쉽습니다. 인터프리터 격리와 데이터 전달

2. 표준 라이브러리만 사용하는 최소 예제

다음 코드를 interpreter_pool_demo.py로 저장합니다. 0부터 999,999까지의 정수를 네 구간으로 나눠 합산합니다. 작업 함수는 내장 함수 sum을 사용하므로 별도 패키지가 필요 없습니다.

from concurrent.futures import InterpreterPoolExecutor


def main():
    batches = [
        range(start, start + 250_000)
        for start in range(0, 1_000_000, 250_000)
    ]
    expected = list(map(sum, batches))

    with InterpreterPoolExecutor(max_workers=2) as executor:
        actual = list(executor.map(sum, batches))

    assert actual == expected
    print("결과 일치:", actual == expected)
    print("전체 합:", sum(actual))


if __name__ == "__main__":
    main()

Python 3.14 이상이 선택된 터미널에서 실행합니다.

python --version
python interpreter_pool_demo.py

코드상 기대 출력은 다음과 같습니다.

결과 일치: True
전체 합: 499999500000

검증 범위: 이 글의 작성 환경은 Python 3.12여서 실제 인터프리터 풀 실행은 검증하지 못했습니다. 위 결과는 순차 계산과 코드 구조를 기준으로 제시한 기대값이며, 성능 측정 결과가 아닙니다.

여기서 range를 전달한 이유는 모든 정수를 담은 큰 리스트를 만들 필요가 없기 때문입니다. 먼저 순차 계산의 정답을 구한 다음 병렬 결과와 비교하도록 구성했습니다. with 블록을 벗어날 때 작업 종료도 기다립니다.

3. 병렬 처리에도 전달 비용이 듭니다

submit()과 map()은 함수와 인수, 반환값을 인터프리터 사이에서 전달할 때 pickle 직렬화를 사용합니다. 계산이 아주 짧거나 전달할 데이터가 크면 준비와 전달 비용의 비중이 커질 수 있습니다. 따라서 작업자 수만 늘려 속도가 빨라진다고 가정하면 안 됩니다. InterpreterPoolExecutor 공식 문서

실제 업무에 적용할 때는 다음 순서로 확인해 보세요.

  1. 먼저 같은 입력으로 순차 실행의 결과와 시간을 기록합니다.
  2. 작업을 충분한 크기의 묶음으로 나누고, 작업자 수를 적게 시작합니다.
  3. 풀 생성부터 결과 수집까지 포함한 전체 시간을 비교합니다.
  4. 반복 실행한다면 풀을 재사용하는 경우도 따로 측정합니다.
  5. 시간과 함께 최대 메모리 사용량, 결과의 일치 여부도 확인합니다.

위 합계 예제는 API의 사용 흐름을 보여 주기 위한 작은 예제입니다. 이 예제만으로 실제 서비스의 처리량이나 속도 향상 비율을 예측할 수는 없습니다.

4. 어떤 실행기를 고를까?

선택의 출발점은 작업 성격과 격리 요구입니다. 아래는 도입을 위한 판단 기준이며, 최종 선택은 사용하는 빌드와 라이브러리에서 측정해야 합니다.

  • 네트워크·파일 응답을 기다리는 작업: ThreadPoolExecutor부터 검토하기 좋습니다. 기존 객체를 공유하는 구조를 유지하기 쉽습니다.
  • 독립적으로 나눌 수 있는 CPU 작업: InterpreterPoolExecutor를 후보로 두고 전달 비용과 의존성 호환성을 확인합니다.
  • 별도 프로세스가 필요한 작업: ProcessPoolExecutor를 검토합니다. 프로세스 생성과 데이터 전달 비용도 비교 대상입니다.

세 실행기는 유사한 API를 제공하지만 실행 단위와 상태 공유 방식이 다릅니다. 이름만 바꾸는 것으로 모든 기존 코드가 같은 의미로 동작하지는 않습니다. concurrent.futures 공식 문서

5. 도입 전에 놓치기 쉬운 두 가지

패키지 호환성을 먼저 확인해야 합니다. 공식 문서는 모든 PyPI 패키지가 다중 인터프리터를 지원하는 것은 아니라고 안내합니다. 실제로 사용하는 확장 모듈이 작업자 안에서 정상적으로 가져와지고 실행되는지 작은 테스트부터 진행하세요.

인터프리터 격리는 보안 경계로 쓰면 안 됩니다. 같은 프로세스의 메모리를 사용하는 구조이며 확장 모듈이 격리를 깨뜨릴 수도 있습니다. 신뢰할 수 없는 코드를 안전하게 실행하는 샌드박스로 사용하면 안 됩니다. 공식 격리 주의사항

정리

InterpreterPoolExecutor를 도입할 때 가장 먼저 할 일은 작업 경계를 정하는 것입니다. 입력은 명시적으로 넘기고, 계산은 독립적으로 수행하며, 결과는 반환값으로 모으세요. 그다음 순차 실행 및 다른 실행기와 결과·시간·메모리를 비교하면 이 방식이 자신의 코드에 적합한지 판단할 수 있습니다.

공식 문서와 릴리스 자료를 바탕으로 AI의 도움을 받아 작성·검토한 활용 가이드입니다.

댓글