Monoruby 개요 및 호환성
- Monoruby는 자체 Ruby 파서, 가비지 컬렉터, 인터프리터로 구성된 독립적인 Ruby 구현체입니다.
- 현재 Gem 지원 및
bundler
연동을 목표로 개발 중이며,Bignum
,Fiber
,Binding
, 기본 연산자 재정의 등 성능에 영향을 미치는 주요 기능들을 지원합니다. - 단, C 확장, 네이티브 스레드,
ObjectSpace
,TracePoint
,Refinements
,callcc
등 일부 CRuby 기능은 현재 지원하지 않거나 향후에도 지원하지 않을 예정입니다.
성능 벤치마크
- 마이크로벤치마크(white-bench) 결과, Monoruby는 CRuby 3.2 인터프리터 대비 3~10배 빠른 성능을 보입니다.
- Monoruby 인터프리터 자체도 CRuby 인터프리터와 유사한 성능을 나타내며, JIT 컴파일러를 적용하면 프레임당 처리 속도가 6~7배 향상됩니다.
truffleruby
와 비교 시,truffleruby
는 느린 시작 시간을 가지나 이후 뛰어난 성능을 보이며, Monoruby는 빠른 시작과 지속적인 성능 향상을 추구합니다.
JIT 컴파일러 동작 원리
- Monoruby의 JIT 컴파일러는 자주 호출되는 메서드나 반복문 코드를 머신 코드로 변환하여 실행 속도를 높입니다.
- 바이트코드에는 인터프리터가 수집한 타입 정보(예:
add
연산 시 좌항과 우항의 클래스 ID)가 포함되어 인라인 메서드 캐시로 활용됩니다. - JIT 컴파일된 코드는 특정 가정(예: 메서드 호출 시 리시버의 클래스)하에 생성되며, 이 가정이 깨질 경우 디옵티마이제이션(deoptimization)을 통해 인터프리터로 폴백(fall back) 후 재시도합니다.
레지스터 상태 추적 및 코드 생성
- 효율적인 코드 생성을 위해 JIT 컴파일러는 각 레지스터의 값을 런타임에 어디에 저장할지(스택, CPU 범용 레지스터, 부동소수점 레지스터 등) 추적합니다.
- 예시로 원의 넓이 계산(
r * r * 3.14
) 시, 컴파일러는r
의 타입(정수/부동소수점)을 추적하고, 오버플로우 검사 및 적절한 레지스터(부동소수점 연산 시 XMM 레지스터) 할당을 통해 최적화된 머신 코드를 생성합니다.
특수화(Specialization)
- Ruby의 동적 특성상 블록(block)이나
yield
호출 시, 컴파일 시점에 블록의 시그니처(인자 개수, 키워드 인자 등)를 알기 어렵습니다. - Monoruby는 호출자 메서드와 블록을 함께 컴파일하여 이 정보를 효율적으로 활용합니다.
Array#each
와 같은 메서드를 Ruby 자체로 구현하여 정보를 효율적으로 사용하고, 블록이 주어진 경우 불필요한 조건 분기 및 기본 블록을 제거하여 최적화합니다.- 이러한 특수화 기법은
Integer#times
,Integer#step
,Array#map
등에서 CRuby 대비 큰 성능 향상을 가져옵니다.