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%의 속도 향상이 관찰되었습니다.