GIL(Global Interpreter Lock)은 파이썬 인터프리터가 한 번에 한 스레드만 수행할 수 있도록 제한하는 메커니즘이다.
다시 말하자면, 파이썬은 멀티쓰레딩을 처리해도 병렬적인 동작이 불가능하다는 것이다.
파이썬 진영에서는 왜 이러한 불필요한 리소스낭비를 하면서까지 GIL을 도입했을까?
그 이유를 알아보기로 했다.
What Is the Python Global Interpreter Lock (GIL)?
위 글에서는 다음과 같이 표현했다.
The problem was that this reference count variable needed protection from race conditions where two threads increase or decrease its value simultaneously. If this happens, it can cause either leaked memory that is never released or, even worse,
(의역) 문제는 레퍼런스 카운트 변수가 복수의 쓰레드에 의해 동시에 증감이 발생할때 레이스 컨디션으로부터 보호받아야 한다는 것이다. 만약 레이스 컨디션이 발생한다면, 메모리 릭이 발생하여 아예 메모리 해제가 되지 않거나 더 안좋을 수 있다...
여기서 레퍼런스 카운트 값의 레이스 컨디션은 무슨 말일까?
파이썬은 기본적으로 레퍼런스 카운팅을 활용하여 메모리 해제를 진행한다.
해당 객체를 참조하는 아이들의 갯수를 지속적으로 카운팅하는 알고리즘이다.
실제 구조를 보며 이해해보자.
Cpython/object.h
struct _object {
_PyObject_HEAD_EXTRA
union {
Py_ssize_t ob_refcnt;
#if SIZEOF_VOID_P > 4
PY_UINT32_T ob_refcnt_split[2];
#endif
};
PyTypeObject *ob_type;
};
파이썬의 최상위 객체인 object 클래스 코드를 보면 위와 같은 스니펫이 존재한다.
ob_refcnt 라는 아이가 눈에 띄는데, 이 놈이 바로 레퍼런스 카운팅을 위한 변수이다.
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
{
if (_Py_IsImmortal(op)) {
return;
}
_Py_DECREF_STAT_INC();
_Py_DEC_REFTOTAL();
if (--op->ob_refcnt != 0) {
if (op->ob_refcnt < 0) {
_Py_NegativeRefcount(filename, lineno, op);
}
}
else {
_Py_Dealloc(op);
}
}
py_DECREF 매크로를 보면 ob_refcnt가 0일 때 _Py_Dealloc(op) 를 실행한다.
파이썬은 객체에 참조하는 수치를 ob_refcnt에 저장하고 카운팅하다가, 0이 되면 메모리에서 해제를 진행한다는 것이다.
When the strong reference is no longer needed, Py_DECREF() should be called on it to decrement the object reference count.
출처 아티클에도 위와 같이 쓰여있다.
(의역) 참조가 더이상 필요치 않을때, Py_DECREF가 호출되며, 레퍼런스 카운트 값을 감소시키기 위함이다.
하지만, 멀티 쓰레딩을 하다가 동시에 ob_refcnt를 건드린다면? 그래서 값이 정상적으로 반영되지 않는다면?
메모리 누수가 엄청날 것이다. 그래서 object 별로 대응하는 Lock이 필요하게 된다.
그러나, 여러 개의 Lock을 객체마다 사용하는 것은 성능적으로 이슈가 크다.
따라서 파이썬 진영에서는 race condition이 발생하지 않게 원천 차단하는 대신, 성능 향상을 포기한 것이다.
메모리를 취하고 성능을 잃는 것이다.
요약
1. 파이썬 진영에서는 GC를 위해 Reference counting 알고리즘을 활용한다.
2. 해당 알고리즘에서 멀티쓰레딩을 통해 Race-condition이 발생하게 하지 않기 위해 GIL을 활용한다.
3. GIL은 싱글 스레드로 파이썬이 동작하게 만든다.
참고 출처
'컴퓨터 공학' 카테고리의 다른 글
👉[Java] JWT 검증방식 Interceptor 구현 (0) | 2023.06.01 |
---|---|
👉 [Java] JVM 메모리 관리는 어떤 원리로 돌아갈까? (0) | 2023.05.13 |
👉 [CS] Python 은 정말로 인터프리터 언어일까? (1) | 2023.04.13 |
👉 [DB] B+Tree 인덱스는 왜 LIKE 연산시 Full Scan을 할까? (0) | 2023.04.03 |
👉 [CS] 아이폰 시리를 chatGPT로 바꿔서 써보기 Feat.노코드 (0) | 2023.04.02 |