JRuby의 시작 시간 최적화는 오랜 과제였으며, 다양한 기술을 통해 점진적으로 개선되어 왔습니다.
JVM에서의 빠른 시작 시간 과제
JRuby는 Ruby 애플리케이션이 매번 컴파일되지 않은 소스에서 시작하고, JRuby 및 JVM 내부 코드가 지연 최적화 방식을 사용하며, JDK 자체도 Java로 작성되어 최적화에 시간이 소요되는 등의 이유로 시작 시간이 느립니다. 예를 들어, JDK 21에서 jruby -e 1 명령은 943ms가 소요되며, 이는 CRuby의 45ms에 비해 현저히 느립니다. 하지만 JVM이 최적화를 시작하면 JRuby의 실제 성능을 확인할 수 있습니다. gem list 명령을 반복 실행하면 첫 실행에 1초 이상이 소요되지만, 이후 실행에서는 0.3초대까지 단축됩니다.
시작 시간 개선 기법
JRuby의 시작 시간을 개선하기 위해 여러 기법이 적용되었습니다.
-
기법 #1: 작업량 줄이기 (
--dev플래그)--dev플래그는 JRuby의 바이트코드 컴파일러와 JVM의 2단계 프로파일링 최적화를 비활성화하여 불필요한 최적화를 건너뜁니다. 이를 통해jruby -e 1실행 시간이 941ms에서 723ms로 단축됩니다. -
기법 #2: AppCDS (Application Class Data Sharing) AppCDS는 JVM 클래스 로딩 및 처리의 초기 부팅 작업을 저장하여 향후 실행 시 해당 처리를 제거합니다. AppCDS를 사용하면
jruby -e 1이 702ms로,--dev모드와 함께 사용 시 567ms로 단축됩니다.gem list명령의 경우 AppCDS 적용 시 1.397초,--dev모드와 함께 사용 시 0.967초로 개선됩니다. -
기법 #3: JDK 업그레이드 (JDK 21 -> JDK 25) JDK 21에서 JDK 25로 업그레이드하는 것만으로도
gem list실행 시간이 1663ms에서 1546ms로 소폭 개선되는 등 전반적으로 미미한 성능 향상이 있었습니다. -
기법 #4: 실행 간 최적화 데이터 저장 (AOTCache) JDK 25의 AOTCache는 AppCDS를 대체하는 주요 메커니즘으로, 훈련 실행을 통해 최적화 프로필을 저장하여 향후 실행 시 최적화를 돕습니다. 훈련 후 AOTCache를 사용하면
jruby -e 1이 581ms로,--dev모드와 함께 사용 시 518ms로 단축됩니다.gem list는 AOTCache 적용 시 1.152초,--dev모드와 함께 사용 시 0.861초로 단축되며,gem list훈련을 통해 0.825초까지 기록했습니다. -
기법 #5: 최적화된 코드 저장 (Project Leyden EA2) Project Leyden의 초기 액세스 빌드에는 AOT 컴파일된 네이티브 코드, 동적 프록시 및 리플렉션 데이터, 클래스 검색 데이터 캐싱 기능이 포함되어 있습니다. Leyden EA2와 AOTCache를 사용하면
jruby -e 1이 462ms로,--dev모드와 함께 사용 시 423ms로 단축됩니다.gem list는 1.035초로,--dev모드와 함께 사용 시 815ms까지 단축되는 경이로운 결과를 보여주었습니다.