GIL 없는 파이썬이 왔다: free-threading 현재 상태와 써야 할 때
모던 파이썬 고급 #5에서 GIL 때문에 CPU 바운드 작업에는 스레드가 무의미하다고 정리했습니다. 코어가 8개 있어도 파이썬 스레드는 한 번에 하나만 바이트코드를 실행하니, CPU를 다 쓰려면 multiprocessing으로 프로세스를 띄우는 게 정답이었습니다. 그런데 이 전제가 지금 바뀌고 있습니다. GIL이 없는 파이썬 빌드가 3.13에서 실험으로 등장했고, 3.14에서 공식 지원 단계에 들어왔습니다. 이 글에서는 free-threading이 무엇인지, 2026년 중반 시점에서 어디까지 왔는지, 그리고 지금 써야 하는지를 판단할 수 있게 정리하겠습니다.
free-threading이 무엇인가 #
PEP 703이 제안한 변경입니다. 한 줄로 요약하면 GIL을 제거하고도 인터프리터가 안전하게 동작하도록 CPython 내부를 고치는 작업입니다.
GIL이 지켜주던 것은 객체 참조 카운팅의 일관성이었습니다. 락을 그냥 빼버리면 여러 스레드가 동시에 참조 카운트를 만져 메모리가 망가집니다. 그래서 free-threaded 빌드는 참조 카운팅 방식 자체를 바꿨습니다. 스레드별로 카운트를 나눠 관리하는 biased reference counting, 객체 단위의 세밀한 락, 락 없이 읽을 수 있는 내부 자료구조 같은 장치들이 GIL의 일을 대신합니다.
결과적으로 겉에서 보는 동작은 같습니다. threading.Thread를 쓰는 코드가 그대로 돌아갑니다. 달라지는 것은 하나입니다. CPU 바운드 멀티스레딩이 진짜로 병렬 실행됩니다.
타임라인: 실험에서 공식 지원까지 #
- Python 3.13 (2024년 10월): free-threaded 빌드가 실험(experimental) 단계로 처음 포함됐습니다. 별도 빌드인
3.13t로 제공됐고, 싱글 스레드 오버헤드가 컸습니다. - Python 3.14 (2025년 10월): PEP 779로 공식 지원(officially supported) 단계에 들어왔습니다. 실험 딱지가 떨어졌고, 적응형 인터프리터(PEP 659) 최적화가 free-threaded 모드에서도 켜지면서 성능이 크게 개선됐습니다.
주의할 점이 하나 있습니다. 공식 지원이 됐다고 해서 기본 빌드가 바뀐 것은 아닙니다. free-threaded는 여전히 별도 빌드이고 opt-in입니다. python.org에서 받는 기본 설치본은 지금도 GIL이 있는 빌드입니다. GIL 빌드를 완전히 대체하는 마지막 단계는 아직 일정이 정해지지 않았습니다.
설치와 확인 #
버전 뒤에 t가 붙은 것이 free-threaded 빌드입니다. uv를 쓰면 한 줄로 설치할 수 있습니다.
# free-threaded 빌드 설치
uv python install 3.14t
# 프로젝트에 지정
uv init my-app --python 3.14t
# 빌드 확인 (free-threading build 문구가 보입니다)
python -VV실행 중인 프로세스에서 GIL이 정말 꺼져 있는지는 코드로 확인합니다.
import sys
print(sys._is_gil_enabled()) # False면 GIL 없이 동작 중이 확인이 중요한 이유가 있습니다. free-threading을 지원하지 않는 C 확장을 import 하면 인터프리터가 프로세스 전체의 GIL을 조용히 다시 켭니다. 에러도 경고도 없이 GIL 빌드처럼 동작하게 됩니다. 그래서 의존성을 모두 import 한 뒤에 sys._is_gil_enabled()로 점검하는 습관이 필요합니다. 환경 변수 PYTHON_GIL=1 또는 실행 옵션 -X gil=1로 free-threaded 빌드에서 GIL을 강제로 켤 수도 있습니다.
무엇이 빨라지나 #
CPU 바운드 작업을 스레드로 나누면 코어 수에 비례하는 가속을 기대할 수 있습니다.
from concurrent.futures import ThreadPoolExecutor
def count_primes(limit):
count = 0
for n in range(2, limit):
if all(n % d for d in range(2, int(n ** 0.5) + 1)):
count += 1
return count
with ThreadPoolExecutor(max_workers=8) as pool:
results = list(pool.map(count_primes, [200_000] * 8))GIL 빌드에서 이 코드는 스레드 8개가 사실상 직렬로 돌아 1개일 때와 비슷한 시간이 걸립니다. free-threaded 빌드에서는 8개 코어가 동시에 일해서 코어 수에 가까운 배율로 빨라집니다.
multiprocessing과 비교했을 때의 장점도 분명합니다. 프로세스 생성 비용이 없고, 데이터를 pickle로 직렬화해서 주고받을 필요가 없습니다. 큰 배열이나 모델을 스레드끼리 같은 메모리에서 그대로 공유합니다. CPU 병렬과 데이터 공유를 동시에 원하던 워크로드에는 구조적인 개선입니다.
대가 1: 싱글 스레드 오버헤드 #
GIL은 단순한 만큼 빠른 동기화 장치이기도 했습니다. 이를 세밀한 락과 분산된 참조 카운팅으로 대체하면 스레드 하나만 쓸 때의 성능은 오히려 떨어집니다.
3.14 기준으로 싱글 스레드 오버헤드는 플랫폼에 따라 대략 5〜10% 수준입니다. pyperformance 벤치마크 평균으로 보면 macOS aarch64에서 약 1%, x86-64 리눅스에서 약 8% 정도로 플랫폼 차이가 있습니다. 3.13 실험 빌드 시절에는 적응형 인터프리터가 꺼져 있어 이보다 훨씬 컸는데, 3.14에서 크게 줄었고 이후 버전에서도 계속 줄여가는 것이 CPython 팀의 방향입니다.
해석하면 이렇습니다. 스레드를 하나만 쓰는 프로그램이라면 free-threaded 빌드는 손해만 있습니다. 멀티 코어 가속이 이 오버헤드를 상쇄하고 남을 때만 이득입니다.
대가 2: C 확장 생태계의 과도기 #
순수 파이썬 코드는 대부분 그대로 동작합니다. 문제는 C 확장입니다. 30년 동안 GIL이 있다는 전제로 작성된 확장들이 thread-safety를 스스로 보장하도록 고쳐져야 하고, free-threaded 빌드용 휠(cp314t)을 따로 빌드해서 배포해야 합니다.
2026년 중반 시점의 현황은 다음과 같습니다.
- NumPy: 2.4 계열이 free-threading 지원을 지속 개선 중이고, cp314t 휠을 주요 플랫폼에 제공합니다.
- PyTorch: 2.9에서 프리뷰 휠을 거쳐 2.10.0 (2026년 1월)부터 정식 cp314t 휠을 제공합니다.
- 미지원 패키지: grpcio처럼 아직 cp314t 휠이 없는 기반 패키지도 있습니다. 이런 패키지에 의존하는 프로젝트는 그 위 전체가 막힙니다.
생태계 전반의 호환 현황은 py-free-threading.github.io의 추적 테이블에서 패키지별로 확인할 수 있습니다. 핵심 수치 계산 라이브러리는 대부분 넘어왔고, 롱테일의 작은 C 확장들이 남아 있는 상태라고 요약할 수 있습니다.
지금 프로덕션에서 써야 하나 #
판단 기준을 정리하겠습니다.
아직 기본 빌드가 답인 경우가 대부분입니다.
- 웹 API 서버, 스크립트, I/O 바운드 워크로드는 free-threading의 이득이 거의 없습니다. 오버헤드만 부담합니다.
- 의존성 트리에 C 확장이 많고 그중 하나라도 미지원이면 GIL이 조용히 다시 켜지므로, 도입 효과 자체가 사라집니다.
- 운영 환경의 모니터링, 프로파일링 도구가 free-threaded 빌드를 지원하는지도 별도 확인이 필요합니다.
시도할 가치가 있는 경우는 분명히 있습니다.
- 멀티 코어 CPU 연산과 큰 공유 데이터가 함께 필요한 워크로드로, multiprocessing의 IPC 비용에 시달리던 코드가 대표적입니다.
- 의존성이 적거나 NumPy, PyTorch처럼 이미 호환을 마친 라이브러리 위주의 프로젝트가 적합합니다.
- 새로 시작하는 프로젝트에서 동시성 설계를 검증해보고 싶은 경우.
uv python install 3.14t한 줄이면 격리된 실험 환경이 생기니 비용이 낮습니다.
요약하면 전면 전환은 이르고, 워크로드를 골라서 실험할 단계입니다. 도입한다면 배포 전에 sys._is_gil_enabled() 점검과 멀티스레드 환경에서의 race condition 테스트를 함께 넣어야 합니다. GIL이 가려주던 동시성 버그가 free-threaded에서 드러나는 경우가 있기 때문입니다.
multiprocessing과 asyncio는 어떻게 되나 #
free-threading이 기본이 되는 미래에도 두 도구가 사라지지는 않습니다. 역할이 좁아질 뿐입니다. multiprocessing은 프로세스 격리 자체가 목적인 경우(크래시 격리, 메모리 상한 분리, 별도 인터프리터 상태)에 남고, “CPU 병렬을 위해 어쩔 수 없이” 쓰던 용법은 스레드로 넘어갈 가능성이 큽니다. asyncio는 GIL과 무관하게 대규모 I/O 동시성이라는 자기 영역이 그대로입니다. 오히려 free-threaded 위에서 이벤트 루프 여러 개를 스레드별로 돌리는 조합이 새로 열리는 쪽에 가깝습니다. 도구 선택 기준이 “GIL을 피하는 방법"에서 “워크로드 성격에 맞는 방법"으로 단순해지는 변화라고 보면 됩니다.
정리 #
- free-threading은 GIL을 제거하고도 안전하게 동작하도록 CPython을 고친 별도 빌드입니다 (PEP 703).
- 3.13에서 실험 단계로 도입됐고, 3.14에서 PEP 779로 공식 지원 단계가 됐습니다. 기본 빌드는 여전히 GIL 빌드입니다.
uv python install 3.14t로 설치하고, import 이후sys._is_gil_enabled()로 GIL이 정말 꺼져 있는지 확인합니다.- CPU 바운드 멀티스레딩이 코어 수에 비례해 빨라지고, multiprocessing의 직렬화 비용 없이 메모리를 공유합니다.
- 대가는 싱글 스레드 오버헤드 약 5〜10%(플랫폼별 차이)와 C 확장 생태계의 과도기입니다.
- 지금은 전면 전환보다 워크로드를 골라 실험할 단계이고, CPU 병렬 + 공유 데이터 워크로드부터 시도할 가치가 있습니다.
GIL 일반론과 threading/multiprocessing/asyncio의 분담은 모던 파이썬 고급 #5에서 정리했으니, 배경이 필요하면 그 글을 먼저 읽는 것을 권합니다.