반응형

 GIL과 lock에 대해 공부하다 보면, 연산이 atomic하다. 그렇지 않다 이야기는 많이 들을 듯 싶습니다. a += 1 같은 것도 사실 바이트 코드로 보면 몇 개의 연산으로 이루어져 있을 겁니다. c python에서 한 줄의 코드에 어떤 바이트 코드들이 들어 있는지 간단하게 확인하는 방법이 없을까요? dis 모듈을 이용하시면 편하게 하실 수 있습니다. 저는 문서에 나 온 것 중, dis 함수만 언급하겠습니다. 나중에, 다른 것이 필요하다면 따로 보는 게 좋을 듯 싶어요.

 

 


 dis 모듈은 뭔지는 잘 모르겠어요. 그런데, Disassembler가 나오고, byte code가 나오는 걸로 보아서는 파이썬 코드를 기계어나, 혹은 바이트 코드로 변환하는 역할을 하는 모양새입니다.

 

 

 여기에 있는 모듈 중에 우리가 볼 것은 dis 함수입니다. 이것은 class, methods, functions 등을 디셈블 합니다. 이것만 보고는, 무엇인지 모르겠으니 간단하게 work 함수를 디셈블 해 보도록 하겠습니다.

 


 먼저, work 함수는 간단한 로직으로 구성되어 있습니다.

 

 

 단지, 전역 변수 x를 불러와서, 거기에 1을 더하는 연산만 하고 있어요. 11번째 줄에 dis.dis(work)가 있는데요. work는 function 입니다. 실행 결과만 보도록 하겠습니다.

 

 

  뭔가 복잡한 코드들로 이루어져 있음을 알 수 있어요. 바이트 코드 단위로만 락이 걸린다고 해 봅시다. 바이트 코드가 여러개 있고, 글로벌 변수를 불러오는 것과 x += 1에 관여하는 부분이 4개는 족히 되어 보입니다. lock을 적절하게 걸지 않았다면, 바이트 단위 코드의 락이 있다고 해도, thread 안전하지 않다는 것을 알 수 있어요. 잘못된 값이 나올 확률이 있다는 건데요. 이것은 다음에 재현을 해 보도록 하고, 간단하게 디셈블된 코드만 보도록 하겠습니다.

 

 

 먼저, LOAD_GLOBAL입니다. 일단 GLOBAL만 빼고 보면, 변수의 값을 로드한다는 것을 알 수 있습니다.

 

 

 그러면, 이것을 어디엔가 저장을 해 놓을 겁니다.

 

 

 다음에 상수 1을 또 로드해서 어딘가에 저장해 놓습니다. 다음에 INPLACE_ADD가 나오는데요. 문서에 따르면, 제자리 연산을 하는데, 이 글에서는 제자리 연산이 어떻게 일어나는지가 중요하지 않습니다. 단지, TOS = TOS1 + TOS가 더 중요할 뿐입니다. 결과값이 반영이 된다는 것입니다.

 

 

 요래 되겠군요. 다음에, STORE GLOBAL이 있는데요.

 

 

 이건 단지, x에다가 결과값을 저장하기 위한 코드일 뿐입니다. 정리하면, GLOBAL x하고, x += 1 이 두 문장을 잘 보면, 글로벌 변수 x의 값을 얻어오고, 1을 로드하고, 1과 로드한 x를 더한 값을 어딘가에 저장하고, 그것을 다시 글로벌 변수 x에 저장하게 됩니다. 살짝 깊숙한 동작을 dis 모듈의 dis 함수를 이용해서 읽어낼 수 있었습니다.

반응형

댓글을 달아 주세요