TypedData 객체는 겉보기에는 일반적인 Ruby 객체와 동일하게 클래스의 인스턴스이며 인스턴스 메서드를 호출하고 인스턴스 변수를 가질 수 있습니다. 그러나 내부적으로는 임의의 데이터에 대한 포인터를 저장하도록 특화되어 있어, 인스턴스 변수 조회 없이 더 빠르게 데이터를 처리하고 Ruby 객체가 아닌 데이터도 저장할 수 있다는 장점을 가집니다. TypedData 객체의 주요 필드는 가비지 컬렉터 메타데이터를 포함하는 headers
, TypedData 객체의 이름, 마크 함수, 해제 함수, 지원 기능 플래그 등을 포함하는 구성 포인터인 type
, 레거시 목적인 typed_flag
, 그리고 임의의 메모리 영역에 대한 포인터인 data
로 구성됩니다.
Jean Boussier와 저자는 ‘임베디드 TypedData 객체(embedded TypedData objects)’라는 새로운 유형의 TypedData 객체를 구현했습니다. 이는 TypedData 객체의 데이터를 외부 시스템 할당 영역이 아닌, 객체 자체 바로 뒤에 할당하는 방식입니다. 이 기능의 도입은 다음과 같은 주목할 만한 이점을 제공합니다.
- 성능 향상: 기존의 Ruby 객체 할당과 시스템 메모리 할당이라는 두 번의 할당 과정을 단일 Ruby 객체 할당으로 줄여, 시스템 메모리 할당 및 해제에서 발생하는 상당한 성능 오버헤드를 제거하고 전반적인 성능을 크게 향상시킵니다.
- 메모리 접근 최적화: 데이터에 접근할 때 더 이상 별도의 포인터를 따라갈 필요가 없어 메모리 접근 횟수가 줄어들고, 이는 런타임 성능 개선으로 이어집니다.
- 메모리 사용량 감소: 8바이트 크기의 메모리 영역 포인터를 저장할 필요가 없어지고, 시스템 할당 메모리에 대한 부기(bookkeeping) 비용이 절감되어 메모리 사용량이 줄어듭니다.
- 메모리 단편화 완화: 일부
malloc
구현에서 발생할 수 있는 외부 메모리 단편화 문제를 완화하는 데 기여합니다. Ruby의 가비지 컬렉터는 이러한 문제를 완화하도록 설계되어 있습니다.
이 기능은 RUBY_TYPED_EMBEDDABLE
플래그를 사용하여 TypedData 객체별로 선택적으로 적용할 수 있으며, 구현에 약간의 변경이 필요합니다. 또한, 가비지 컬렉션의 압축 단계에서 객체가 이동할 때 데이터의 주소가 변경될 수 있으므로, 데이터가 여러 TypedData 객체 간에 공유되지 않아야 한다는 제약이 있습니다.
현재 이 기능은 Time, Enumerator, Method, TracePoint를 포함하여 가장 일반적으로 사용되는 30개 이상의 TypedData 객체에 구현되었습니다. 시스템으로부터 malloc
을 통한 메모리 할당 필요성을 제거함으로써, 임베디드 TypedData 객체는 할당 속도에서 비약적인 발전을 보였습니다. 구체적인 예시로, Time.now
에서 80%의 속도 향상, Object#to_enum
에서 68%의 속도 향상, 그리고 Object#method
에서 약 50%의 속도 향상이 관찰되었습니다.