Python에서 스레드를 늘렸는데 CPU 작업이 빨라지지 않는 이유
문제 발생
이미지 리사이징처럼 CPU를 많이 쓰는 작업을 threading 모듈로 4개 스레드에 나눠 돌렸는데, 코어가 4개인데도 속도가 거의 늘지 않았습니다.
import threading
threads = [threading.Thread(target=cpu_heavy_task) for _ in range(4)]
for t in threads:
t.start()
# 4배 빨라질 것으로 기대했지만 거의 그대로원인 분석
CPython(가장 널리 쓰이는 Python 구현체)에는 **GIL(Global Interpreter Lock)**이 있습니다. GIL은 한 프로세스 안에서 한 번에 하나의 스레드만 Python bytecode를 실행하도록 강제하는 락입니다. 즉 스레드를 아무리 늘려도 CPU 연산 자체는 한 번에 한 스레드씩만 실제로 실행됩니다.
GIL이 있음에도 threading이 유용한 경우는 I/O 대기(네트워크 요청, 파일 읽기, DB 쿼리 대기 등)입니다. I/O 대기 중에는 GIL이 해제되어 다른 스레드가 실행될 수 있습니다. 그래서 웹 요청을 여러 개 동시에 보내는 작업은 threading으로 실제 이득을 봅니다. 반면 순수 계산(이미지 처리, 암호화, 수치 연산)은 GIL 때문에 스레드를 늘려도 병렬로 실행되지 않습니다.
해결 방안
- I/O 위주 작업(네트워크, 파일, DB)은
threading이나asyncio로 동시성을 얻습니다. GIL이 병목이 아닙니다. - CPU 위주 작업(계산, 이미지/데이터 처리)은
multiprocessing을 씁니다.multiprocessing은 스레드가 아니라 별도 프로세스를 만들기 때문에 각 프로세스가 자기만의 Python 인터프리터와 GIL을 가져 진짜 병렬 실행이 됩니다.
from multiprocessing import Pool
with Pool(processes=4) as pool:
results = pool.map(cpu_heavy_task, data_chunks)multiprocessing은 프로세스 간 데이터 전달에 직렬화(pickle) 비용이 들고 메모리를 공유하지 않으므로, 작업 단위가 너무 작으면 프로세스 생성/통신 오버헤드가 이득보다 커질 수 있습니다.- NumPy, Pandas 같은 라이브러리의 일부 연산은 내부적으로 C로 구현되어 실행 중 GIL을 해제하므로, 순수 Python 반복문보다 이런 라이브러리를 쓰는 것 자체가 GIL 문제를 우회하는 방법이 되기도 합니다.
- Python 3.13부터 실험적으로 GIL을 비활성화할 수 있는 빌드(PEP 703, free-threaded build)가 제공되기 시작했지만 아직 기본값이 아니며 생태계 호환성이 안정화되는 중입니다 — 프로덕션 적용 전 실제 사용 중인 Python 버전과 배포 상태를 확인해야 합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.