Ruby에 ZJIT 병합: 프로젝트 개요 및 아키텍처

ZJIT has been merged into Ruby

작성자
발행일
2025년 05월 14일

핵심 요약

  • 1 ZJIT는 RubyKaigi 2025 이후 Ruby에 공식 병합된 새로운 JIT(Just-In-Time) 컴파일러입니다.
  • 2 기존 YJIT와 달리 고수준 중간 표현(HIR)을 사용하고 전통적인 컴파일러 방식으로 설계되어 커뮤니티 기여를 용이하게 합니다.
  • 3 YARV, HIR, LIR, Assembly 단계를 거쳐 Ruby 코드를 효율적인 머신 코드로 변환하며, 현재 개발 초기 단계에 있습니다.

도입

RubyKaigi 2025에서의 발표와 Matz의 승인 이후, 새로운 JIT(Just-In-Time) 컴파일러인 ZJIT가 Ruby에 공식적으로 병합되었습니다. ZJIT는 기존 YJIT를 개발한 동일한 컴파일러 그룹(Maxime Chevalier-Boisvert, Takashi Kokubun 등)이 Ruby의 참조 구현인 YARV를 기반으로 개발했습니다. 본 게시물은 아직 개발 초기 단계에 있는 ZJIT 프로젝트의 개괄적인 설명과 아키텍처, 그리고 향후 계획에 대해 다룹니다.

ZJIT는 여러 면에서 기존 YJIT와 차별점을 가집니다. 첫째, YJIT가 YARV 바이트코드를 저수준 IR(LIR)로 직접 컴파일하는 반면, ZJIT는 고수준 SSA(Static Single Assignment) 기반 중간 표현(HIR)을 사용합니다. 둘째, YJIT가 한 번에 하나의 기본 블록을 컴파일하는 것과 달리 ZJIT는 전체 메서드를 한 번에 컴파일합니다. 셋째, 타입 프로파일링 방식에서도 YJIT가 지연 기본 블록 버전 관리(LBBV)를 사용하는 것과 달리 ZJIT는 프로파일링된 인터프리터에서 과거 타입 정보를 읽어옵니다. 넷째, 최적화 측면에서는 YJIT가 YARV를 LIR로 내리는 과정에서 최적화를 수행하는 반면, ZJIT는 HIR에서 동작하는 모듈식 고수준 최적화 도구를 갖추고 있습니다. 이러한 핵심적인 차이점은 ZJIT가 커뮤니티의 기여를 용이하게 하기 위해 보다 전통적인 ‘교과서적’ 컴파일러 방식으로 설계되었다는 점입니다.

ZJIT의 컴파일 파이프라인은 YARV 바이트코드를 입력받아 내부적으로 IR을 구축하고 최적화를 거쳐 최종적으로 머신 코드를 생성합니다.

  • YARV (Yet Another Ruby VM): Ruby VM은 Ruby 코드를 스택 기반의 YARV 바이트코드로 컴파일합니다. 예를 들어, opt_plus와 같은 연산은 인수의 타입을 확인하고 Integer#+가 재정의되지 않았는지 확인하여 빠른 경로 또는 일반 경로를 선택합니다. 인터프리터에서 함수가 일정 횟수 이상 실행되면 ZJIT는 opt_plus와 같은 Opcode를 zjit_opt_plus와 같은 수정된 버전으로 재작성하여 입력 값의 타입을 기록합니다.

  • HIR (High-level Intermediate Representation): HIR은 바이트코드와 달리 그래프 형태를 띠며, 스택 대신 데이터 생성 명령에 직접 포인터로 연결되어 데이터 흐름이 명시적입니다. 초기 HIR은 SendWithoutBlock과 같은 일반적인 연산을 보여주지만, 최적화 과정을 거치면 GuardTypeFixnumAdd와 같은 타입 특화 코드로 변환됩니다. GuardType 명령은 런타임에 피연산자가 Fixnum인지 확인하며, Fixnum이 아닐 경우 생성된 코드는 인터프리터로 폴백합니다. 이를 통해 FixnumAdd와 같은 특화된 코드만 생성할 수 있어 효율성을 높입니다.

  • LIR (Low-level Intermediate Representation): LIR은 다중 플랫폼 어셈블러의 역할을 하며, 가상 LIR 레지스터를 물리 레지스터 및 스택 위치에 매핑합니다. LIR은 HIR의 고수준 연산을 어셈블리와 유사한 언어로 변환하며, FrameSetup, FrameTeardown, Test, Jz, Jo, Sub, Add와 같은 낮은 수준의 명시적인 명령어를 포함하여 HIR보다 더 자세한 실행 과정을 보여줍니다.

  • Assembly (ASM): LIR에서 최종적으로 생성되는 머신 코드입니다. 이 어셈블리 코드에서 GuardTypeFixnumAdd는 각각 몇 개의 매우 빠른 머신 명령만 요구하여 타입 특화의 효율성을 명확히 보여줍니다. ZJIT는 현재 x86뿐만 아니라 ARM 백엔드도 지원하여 다양한 플랫폼에서 활용될 수 있습니다.

결론

ZJIT 프로젝트는 아직 초기 개발 단계에 있으며, 따라서 현재 프로덕션 환경에서의 사용은 권장되지 않습니다. Ruby 3.5는 YJIT와 ZJIT를 모두 포함하여 출시될 예정이며, 개발팀은 ZJIT가 YJIT와 동등한 기능 및 성능을 갖추도록 지속적으로 개선할 계획입니다. 현재는 `GuardType`가 예상치 못한 타입을 만났을 때 인터프리터로 점프할 수 있도록 하는 사이드 이그짓(side-exits) 기능을 구현 중입니다. 이 기능은 Ruby 테스트 스위트 및 `yjit-bench`와 같은 실제 애플리케이션을 실행하여 ZJIT의 정확성과 성능 기준점을 확보하는 데 중요한 역할을 할 것입니다. 앞으로 개발팀은 프로파일링 및 최적화 작업에 집중하여 ZJIT의 성능 향상에 기여할 예정입니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!