GIL(Global Interpreter Lock)을 파이썬에 도입한 이유
GIL(Global Interpreter Lock)은 CPython(파이썬의 가장 널리 사용되는 구현체)에서 도입된 메커니즘으로, 여러 스레드가 동시에 실행될 때, 한 번에 하나의 스레드만 Python 바이트코드를 실행할 수 있도록 제한합니다. 이는 다중 스레드 환경에서 데이터 안정성과 성능을 유지하기 위해 고안된 것입니다.
1. GIL의 도입 이유
(1) 메모리 관리의 간단화
- CPython의 메모리 관리 구조:
- CPython은 참조 카운트(reference counting) 기반으로 객체의 메모리를 관리합니다.
- 참조 카운트는 각 객체가 몇 개의 변수에서 참조되는지 기록하고, 참조 카운트가 0이 되면 메모리를 해제합니다.
- 문제:
- 여러 스레드가 동시에 객체의 참조 카운트를 변경하면, 경쟁 상태(race condition)가 발생할 수 있습니다.
- 이를 방지하려면 참조 카운트 연산에 대한 락(lock)이 필요합니다.
- GIL의 역할:
- GIL은 한 번에 하나의 스레드만 Python 바이트코드를 실행하도록 제한하여, 참조 카운트 연산이 항상 안전하게 처리되도록 합니다.
- 별도의 락 없이 메모리 관리를 단순화합니다.
(2) 구현의 단순화
- CPython은 기본적으로 C로 작성된 인터프리터입니다.
- 다중 스레드 환경에서의 동시성 처리(예: 락, 세마포어 등)를 구현하는 것은 코드 복잡도를 크게 증가시킵니다.
- GIL로 통합된 락 관리:
- GIL은 스레드 간의 동시 실행 문제를 단일 락으로 단순화하여, 인터프리터 구현을 간단하게 유지합니다.
(3) 성능 최적화 (단일 스레드 환경에서)
- 단일 스레드 프로그램에서는 GIL이 별다른 성능 저하를 일으키지 않습니다.
- 멀티 스레드 대신 단일 스레드 프로그램에서의 성능 최적화에 집중한 결정이었습니다.
- 과거에는 멀티코어 시스템이 일반적이지 않았으므로, GIL은 설계 당시 성능 타협으로 받아들여졌습니다.
(4) 외부 C 확장 모듈의 호환성
- CPython은 외부 C 확장 모듈과 긴밀히 통합되어 있으며, 많은 C 확장 모듈은 멀티스레드 환경에서의 안전성을 보장하지 못합니다.
- GIL은 이러한 C 확장 모듈이 스레드 안전성을 걱정하지 않고도 동작할 수 있도록 보장합니다.
2. GIL의 장점
코드 간소화:
- GIL 덕분에 CPython의 메모리 관리와 스레드 동기화가 간단해짐.
- 개발자가 복잡한 동기화 문제를 직접 처리할 필요가 없음.
안정성 보장:
- GIL은 Python 스레드가 경쟁 상태(race condition) 없이 실행되도록 보장.
- 멀티스레드 환경에서도 데이터 손상을 방지.
C 확장 모듈 지원:
- GIL은 C 기반 확장 모듈이 Python의 멀티스레드 환경에서 안정적으로 동작할 수 있도록 돕습니다.
3. GIL의 단점
멀티코어 CPU의 활용 제한:
- GIL로 인해 Python 멀티스레드 프로그램은 멀티코어 CPU에서 병렬 실행이 불가능.
- 하나의 코어만 사용하며, 나머지 코어는 유휴 상태가 됨.
멀티스레드 프로그램 성능 저하:
- GIL은 스레드가 자주 전환될 때 오버헤드를 초래.
- CPU 바운드 작업에서는 멀티스레드의 성능 이점을 잃게 됨.
I/O 바운드 작업에서의 제약:
- GIL이 멀티스레드 I/O 작업의 성능에 영향을 미치지는 않지만, 병렬성이 제한되어 다른 구현체보다 느릴 수 있음.
4. GIL의 대안과 개선 노력
(1) 멀티프로세싱(multiprocessing) 모듈
- Python은 GIL의 한계를 극복하기 위해 멀티프로세싱을 제공합니다.
- 프로세스는 GIL의 영향을 받지 않으므로, 멀티코어 환경에서 병렬 처리가 가능합니다.
from multiprocessing import Pool
def square(x):
return x * x
if __name__ == "__main__":
with Pool(4) as p:
print(p.map(square, [1, 2, 3, 4]))
(2) GIL을 제거한 구현체
- PyPy:
- Python의 또 다른 구현체로, GIL 없이 동작.
pypy3 multithread_example.py
- Jython, IronPython:
- 각각 Java와 .NET 기반 구현체로, GIL 없이 멀티스레드 환경을 지원.
jython multithread_example.py
ipy multithread_example.py
5. 결과 요약
구현체 | GIL 존재 | 병렬 실행 | JIT 사용 | 비고 |
---|---|---|---|---|
CPython | 있음 | 불가능 | 없음 | 안정적, 널리 사용됨, 멀티코어 활용 제한. |
PyPy | 있음 | 제한적 | 있음 | JIT으로 단일 스레드 성능은 뛰어남. |
Jython | 없음 | 가능 | 없음 | Java 환경에서 멀티코어 활용 가능. |
IronPython | 없음 | 가능 | 없음 | .NET 플랫폼에서 병렬 실행 가능. |
6. 결론
- GIL은 Python(CPython)의 단순한 메모리 관리와 안정성을 유지하기 위한 타협으로 도입된 메커니즘입니다.
- 멀티코어 환경이 보편화된 현재에서는 단점이 두드러지지만, I/O 바운드 작업이나 단일 스레드 프로그램에서는 여전히 적합한 성능을 제공합니다.
- GIL의 한계를 극복하려면 멀티프로세싱, 비동기 I/O, 또는 GIL이 없는 Python 구현체(PyPy, Jython 등)를 사용하는 방법을 고려해야 합니다.
'그때그때 CS 정리' 카테고리의 다른 글
thisown 속성: Python과 C++ 객체 메모리 관리의 이해 (0) | 2025.01.01 |
---|---|
ProcessPoolExecutor의 코드 및 동작원리 (0) | 2024.11.23 |
멀티스레드(ThreadPoolExecutor), 멀티프로세서(ProcessPoolExecutor) 비교 (0) | 2024.11.06 |
ThreadPoolExecutor 내부 구조 분석(큐를 활용한 멀티스레딩) (0) | 2024.11.05 |
json 평활화 작업을 통한, 멀티스레드 속도 비교(ThreadPoolExecutor) (0) | 2024.11.03 |