YJIT의 핵심은 ‘역최적화(deoptimization)’입니다. 이는 Ruby의 동적 특성으로 최적화 가정이 깨질 경우, 최적화된 코드를 즉시 폐기하고 인터프리터 모드로 되돌아가는 메커니즘으로, 과감한 최적화를 가능하게 합니다.
YJIT의 전통적인 역최적화 (Ruby 3.1 이후)
- 코드 패치: 상수를 인라인하여 최적화한 후, 재정의되면 해당 코드 위치를 점프 명령어로 패치하여 최적화를 취소하고 인터프리터로 전환합니다.
- 전역 무효화:
TracePoint
와 같은 동적 기능(특히line trace point
)이 활성화되면, YJIT은 해당 메서드의 모든 JIT 코드를 무효화하고 인터프리터로 강제 전환합니다.
Ruby 3.4에 추가된 새로운 역최적화
- 이스케이프된 지역 변수 무효화: Ruby 3.4부터 지역 변수를 레지스터에 할당하여 최적화하지만,
binding
객체나 C API를 통해 외부에서 변경될 경우 최적화를 무효화합니다. - 싱글턴 클래스 무효화: 객체에 싱글턴 클래스가 생성되어 메서드가 재정의될 때, YJIT은 이전에 추측성으로 최적화했던 해당 메서드 호출 코드를 무효화하여 정확성을 유지합니다.
- 지연 프레임 푸시: 메서드 인라인 최적화 시 프레임 푸시를 생략할 수 있으나, 예외 발생 시 필요한 경우에만 프레임을 지연하여 스택에 푸시함으로써 올바른 백트레이스를 보장합니다.
- YJIT 전용 메서드: C와 Ruby 코드 간의 경계 교차 비용을 줄이기 위해, YJIT 활성화 시 C 구현 대신 Ruby로 재구현된 코어 메서드를 사용하도록 전환합니다. 이때
C_TRACE
속성으로 백트레이스 일관성을 유지합니다.