저자는 strlen
C 함수를 벤치마킹하여 FFI, C 확장, 간접 루비 호출, 직접 루비 호출 간의 성능 차이를 명확히 보여줍니다. 벤치마크 결과, String#bytesize
직접 호출이 가장 빠르며, C 확장이 그다음, FFI가 가장 느린 성능을 보였습니다. 이는 FFI를 통한 네이티브 함수 호출 시 상당한 오버헤드가 발생함을 시사합니다.
이러한 오버헤드를 줄이기 위해 저자는 외부 함수 호출에 필요한 코드를 JIT(Just-In-Time) 컴파일하여 머신 코드를 직접 생성하는 아이디어를 제안합니다. 이 아이디어는 attach_function
과 같은 FFI 정의 시점에 함수 이름, 파라미터 및 반환 타입 정보를 활용하여 루비 타입을 래핑/언래핑하고 외부 함수를 호출하는 데 필요한 머신 코드를 동적으로 생성하는 것을 목표로 합니다.
이 비전을 실현하기 위해 AArch64
및 Fisk
젬을 통한 머신 코드 생성, JITBuffer
젬을 통한 실행 가능한 메모리 할당, 그리고 가장 중요한 RJIT
의 활용이 강조됩니다. RJIT는 루비로 작성된 루비용 JIT 컴파일러로, 최근 외부 젬으로 추출될 예정이며, 이는 서드파티 JIT 컴파일러가 루비의 내부 타입 정보를 얻고, 생성된 머신 코드를 루비가 자동으로 실행하도록 등록할 수 있는 중요한 기반을 제공합니다.
이러한 기술적 기반을 바탕으로 저자는 “FJIT”(FFI JIT)라는 소규모 개념 증명(Proof of Concept)을 개발했습니다. FJIT는 FFI와 유사한 인터페이스를 제공하지만, attach_function
호출 시 필요한 머신 코드를 런타임에 생성합니다. FJIT를 포함한 벤치마크 결과는 놀랍게도 FJIT가 C 확장보다 약간 더 빠르며, 기존 FFI보다 2배 이상 빠른 성능을 보여주며 두 번째로 빠른 성능을 기록했습니다. 이는 JIT 컴파일을 통해 FFI의 성능 병목을 효과적으로 해결할 수 있음을 입증합니다.