파이썬에 GIL이 있는 이유

마수리·2025년 4월 11일
0

파이썬

목록 보기
1/1

저는 최근에 한번도 사용해보지 않았던 Python을 이용해 일을 하고 있습니다.

저는 .NET 프로그래머로써 이런 생각을 해 보았습니다.

Python을 사용하면서 CPU 집약적인 일에 병렬적으로 작업하면 좋지 않을까?

결론적으로 Python에선 불가능한 일이였습니다. 물론 Python에도 async, await를 이용한 비동기 작업은 있지만 이 비동기 작업은 병렬적인 상황이 아닌 동기적인 상황에서만 우리가 예상했던 대로 동작합니다. 간단하게 얘기하면

Python은 동기적 상황에서 비동기를 지원하지만 병렬적 상황은 GIL로 인해 처리가 불가하다.

GIL

그럼 도대체 GIL이 무엇인지 알아봅시다. Global Interpreter Lock 이란 뜻으로

GIL은 파이썬의 객체가 레퍼런스 카운팅으로 관리되는 상황에서, 여러 스레드가 동시에 같은 객체를 수정하는 것을 막기 위해 인터프리터 전체에 하나의 큰 락을 거는 방식입니다.

GIL은 프로그램 전체에서 동시에 1개의 일만 진행하게 만다는 것입니다. 그 이유는 파이썬의 객체를 초쇠한의 GC 호출로 레퍼런트 카운트가 0이 되는 순간 메모리에서 삭제하기 위함입니다. 이건 구현도 쉽고 실시간으로 메모리를 삭제할 수 있는 장점이 있습니다.

내가 아닌 다른 언어는 왜 GIL이 없는거야?

제가 아는 C#이나 C++에는 GIL이 없습니다. 그래서 가장 처음에 의문을 가졌던 병렬적인 동작이 안됐던 Python과 다르게 병렬적인 처리가 가능해 이런 면에서는 더 높은 성능을 보일 수 있습니다. 그럼 여기서 궁금증이듭니다.

왜.. GIL은 파이썬에만 있는거야?

GIL은 파이썬에만 있지 않다.

알아본 결과 GILPython에만 있는 것이 아니였고 Ruby, R과 같은 다른 인터프리터 언어들에도 있는 개념이였습니다. 여기서 인터프리터라는 용어가 나왔습니다. GIL은 다른 인터프리터 언어에도 있는 개념이라고 합니다! 왜죠..??

인터프리터가 GIL이 필요한 이유

제가 사용해 보았던 C#이나 C++은 약간의 차이는 있지만 컴파일 되는 언어입니다. 인터프리터와는 상반된 개념이죠. 컴파일이 되는 언어는 컴파일 타임에 함수나 변수 등에 대해서 많은 정보를 취합할 수 있습니다. 이 변수는 스택에 저장하고 이 변수는 얼마나 들어올지 모르는 동적으로 변하는 변수니까 에 할당해야되겠다 라는 것 등을 말이죠.

하지만 인터프리터는 컴파일을 하지 않는 언어이기 때문에 모든 변수가 에 저장 됩니다.

x = 42

이 코드의 메모리 생성 관점을 도표로 보면 아래와 같습니다.

스택:                   힙: 
+-----------------+     +------------------+ 
| x (변수)        |     | 정수 객체 42 |    
| → 0x12345678    | --> | (메모리 주소:| 
+-----------------+     | 0x12345678)  |
                       +------------------+

이런 식으로 실제 데이터는 42는 힙에 저장되고 이것을 받는 변수 x에 힙에 있는 레퍼런스를 담는 방식입니다. 모든 Python 변수의 메모리 할당은 이렇게 이루어집니다.

이 말은 메모리를 지우기 위해서 모든 변수의 레퍼런스 카운트를 체크해야 한다는 말입니다. 이런 상황에서 모든 변수에 각각 lock을 걸어서 race condition없이 카운트를 관리하는 것은 크나큰 낭비일 것입니다.

어차피 모든 변수에 lock을 걸어서 관리할거면 프로그램 전체적으로 동시에는 단 1개의 쓰레드만 일하도록 하여서 race condition을 없애버리는게 성능에 좋겠다! 라고 그 당시에는 생각했던 것 같습니다.

결론

Python이 개발 될 당시에 이런 인터프리터의 메모리 관리 방식에 대한 해법은 GIL을 만드는 것이였던 것 같습니다. 이제 인터프리터 언어컴파일 언어의 차이점 하나를 더 알아보았습니다.

profile
.NET 개발자 마수리입니다 🖐

0개의 댓글