Python/ETC / / 2023. 1. 27. 22:24

[EuroPython] 어떻게 Python 3.11은 빨라질 수 있었을까? 2편 - dictionary

일반적인 파이썬 객체

class C:
	def __init__(self, a, b):
    	self.a = a
        self.b = b

위으 코드는 두개의 attribute를 가지고 있으며 a, b를 할당한다. 위의 코드가 메모리 상에서 어떻게 접근하고 메모리를 점유하는지 알아보자.

 

대부분의 모든 파이썬 Ojbect는 __dict__을 가지고 있다. 이 때 __dict__ 속성은 거의 직접적으로 사용되지 않는다. 파이썬 객체는 고정된 크기를 가지고 있지 않다. 그리고 __slots__이라는 속성을 가지고 있을 수 있다. 또는 List처럼 빌트인 타입들로부터 상속받을 수 있다. 이러한 것들은 읽기 쉽고 직관적인 코드를 제공해준다. 이는 곧 dict["attribute"]처럼 할 필요 없이 dict.attribute로 가능케 한다. 하지만 모든 객체가 이러한 방식을 사용하는 것이 아니다. 아까 언급했듯이 __slots__라는 속성을 가지고 있을 수 있고, 그 외에도 다양한 타입을 가질 수 있다.

 

이는 곧 다시금 말하지만 크기가 가변적이라는 것이다. 이러한 특성으로 인해 일반적인 파이썬 객체의 실행은 느리다. 

 

위의 그림을 보면, 흰색 박스로 된 객체가 있다. 그 객체는 클래스의 향하는 포인터를 가지고 있다. 그리고 다시 이 클래스는 객체를 가리키는 포인터를 가지고 있다. 이는 가변적인 사이즈로 인해 발생하는 문제이다. 따라서 dict_offset을 통해 __dict__이 있는 곳을 찾는다. (초록색 박스), 여기서 이 클래스는 객체가 몇개가 있든 단 하나만 존재한다. 하지만 빨간색 박스는 다르다. 딕셔너리 속 값들은 해시와 키, 그리고 값들로 이루어져있다. 

 

따라서 위의 경우를 self.a에 대입하면, 키 a에 저장되는 값들이 테이블로 이루어진다. 이를 개선한 것이 3.2까지의 일이다.

 

3.3 부터 3.10까지는 데이터 구조를 조금 변경해서 딕셔너리 키들(3.2까지는 테이블에 있던 것들을) 클래스의 인자로서 작동하도록 만들었다. 이를통해 중복되는 키값을 지울 수 있게 되었다. 이러한 것들은 전부 분리된 데이터 구조로 분리되어 class와 dictionary가 공유되도록 해준다.

 

 

3.11에서는 한번 더 바뀌게 되는데, 포인터의 위치 변경이다. 기본적으로 딕셔너리의 포인터 __dict__이 고정된 offset 즉, object의 앞으로 옮겨지게 되었다. 이는 곧 dict_offset을 통해 참조할 필요가 없어짐을 보이는 것이며, 이를 통해 메모리 접근을 줄이는 결과를 보이기도 한다. 즉 메모리 크기를 줄인 것은 아니지만, 메모리 접근, 메모리 사이클에 대해서 어느정도 줄이게 되었음을 의미한다. 또한 딕셔너리(dictionary)를 줄여서 딕셔너리 키를 class를 통해 접근할 수 있게 한다. 모든 값들이 object에 넣어졌을 때 접근할 수 있도록 하기도 했다. 

 

즉 이를 요약하자면, 기존에 있던 dictionary의 메모리 크기는 줄이지 않았지만, 메모리 자체에 대한 접근을 줄여서 속도를 향상시킨 것이다.


다음 정리 글에서는 알고리즘 변경에 대해서 이야기 해보겠다.

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유