Google Docs와 Gmail 같은 현대 사이트는 JavaScript를 너무 많이 실행해서 엔진 속도에 두 가지 별개의 병목이 있었습니다. Firefox의 C++ 바이트코드 인터프리터는 느렸고 타입 정보를 수집하지 않았으며, Baseline JIT는 각 함수를 기계어로 컴파일했습니다. 무거운 페이지에서는 수천 개의 함수를 컴파일해야 했으므로 시간이 실행 대신 컴파일에 쓰였습니다. 최적화 Ion JIT는 Baseline JIT가 타입 데이터를 수집한 뒤에야 빨라졌습니다.

명백한 해결책은 각각 비용이 따랐습니다. C++ 인터프리터를 빠르게 해도 최적화기에 타입 데이터를 주지 못하고, Baseline 컴파일을 지연하거나 옮기면 콜드 경로나 bailout이 위험해지며, Ion bailout을 JIT 코드 중간에서 재개하면 모든 기능에 추가 메타데이터와 디버거·예외 처리를 위한 복잡한 코드가 필요했습니다. 대신 Mozilla는 C++ 인터프리터와 Baseline JIT 사이에 새로운 계층인 Baseline Interpreter를 추가했습니다.

이를 저렴하게 만든 조치는 인터프리터를 처음부터 작성하지 않은 것입니다. Baseline JIT는 템플릿 JIT로, 각 바이트코드 명령어가 이미 대부분 고정된 기계어 시퀀스에 매핑됩니다. Mozilla는 같은 시퀀스를 스레드 인터프리터 루프로 생성하여 하나의 템플릿화된 코드 생성기, 동일한 프레임 레이아웃, 그리고 두 계층이 사용하는 인라인 캐시와 프로파일링 데이터를 담는 하나의 JitScript 구조를 공유했습니다.

인터프리팅이 이제 타입 정보를 수집했기 때문에 Baseline JIT 워밍업 임계값을 10에서 100으로 높일 수 있었고, 컴파일되는 함수 수가 크게 줄었습니다. Firefox Nightly는 페이지 로드 2-8% 향상, devtools 테스트 2-10% 향상, 메모리 절약, 그리고 더 단순한 코드를 측정했습니다. Ion bailout은 인터프리터에서 재개되었고, 복잡한 디버거 및 예외 처리 기계의 상당 부분이 삭제되었습니다.

빠른 계층과 동일한 코드 생성기에서 저렴한 계층을 생성하여 캐시와 타입 데이터가 계속 공유되도록 함

JIT의 코드 생성기를 재사용하면 인터프리터 동작과 인라인 캐시가 구조적으로 JIT와 일치하며, 동기화할 두 번째 구현이 없습니다.

인터프리팅이 이제 JIT가 사용하던 것과 동일한 타입 데이터를 수집하므로, 최적화 계층은 컴파일을 기다리지 않고 프로파일을 얻습니다.

JIT 워밍업 임계값을 열 배로 높이면서 JS가 많은 페이지에서 컴파일되는 함수 수가 크게 줄었습니다.

Ion이 JIT 코드 중간이 아니라 인터프리터에서 재개되므로 bailout, 디버거 지원, 예외 처리가 더 단순해지고 필요한 메타데이터가 줄었습니다.

중간 계층 진입은 저렴합니다. JitScript 할당이 Baseline JIT 컴파일보다 최소 10배 빠릅니다.

느린 계층과 빠른 계층 사이의 중간 계층을 수작업으로 작성하지 말고, 빠른 계층 자체의 템플릿에서 생성하면 캐시, 프레임, 동작이 구조적으로 동일하게 유지됨

Baseline Interpreter는 2019년 7월 Firefox Nightly에 적용되었고 그해 10월 Firefox 70 일반 릴리스로 향했습니다. Mozilla의 측정에 따르면 페이지 로드 2-8% 향상, devtools 테스트 2-10% 향상에 메모리 절약도 더해졌습니다. 이것이 가능하게 한 정리 작업은 Baseline JIT 컴파일도 빨라지게 했고, 팀이 밝힌 다음 단계는 그 컴파일을 메인 스레드 밖으로 옮기는 것이었습니다.

근거 확인하기

출처

  1. The Baseline Interpreter: a faster JS interpreter in Firefox 70 Mozilla Hacks · hacks.mozilla.org ·
  2. Hacker News discussion: The Baseline Interpreter: a faster JS interpreter in Firefox 70 Hacker News · news.ycombinator.com ·