JavaScript 소스 코드
↓
파싱 (Abstract Syntax Tree - AST 생성)
↓
Ignition 인터프리터의 바이트코드 생성
↓
Ignition 바이트코드 실행 (인터프리팅)
↓
실행 중 프로파일링 및 핫 코드 판별
↓
Sparkplug 베이스라인 JIT 컴파일
↓
TurboFan 최적화 JIT 컴파일
↓
디옵티마이제이션 (필요한 경우 최적화 해제)
V8은 JavaScript 소스 코드를 분석하여 AST(추상 구문 트리)를 만듭니다. AST는 코드 구조를 명확하게 표현하며, Ignition 인터프리터가 이를 바이트코드로 압축하여 변환합니다. 이 바이트코드는 메모리 효율성을 높이고 실행 속도를 최적화하기 위한 압축 표현을 사용합니다.
Ignition 인터프리터는 바이트코드를 하나씩 읽고 즉시 실행합니다. 이 단계에서는 타입 체크, 값 로딩, 계산 등 모든 작업을 실시간으로 수행하며, 타입 정보 등 실행 프로파일링 데이터를 수집합니다. 이러한 데이터는 향후 코드 최적화의 중요한 기초로 활용됩니다.
코드가 반복 실행되면, V8 엔진은 프로파일링 정보를 활용해 "핫 코드"를 판단합니다. 핫 코드는 반복 실행되어 성능 개선의 효과가 큰 코드 영역을 의미하며, 이 코드 영역이 명확해지면 JIT 컴파일 단계로 넘어갑니다.
Sparkplug는 빠르게 바이트코드를 머신 코드로 직접 변환하는 경량 JIT 컴파일러입니다. 중간 IR(Intermediate Representation)을 거치지 않고 바이트코드에서 즉시 간단한 머신 코드를 생성하여, Ignition 인터프리터의 실행 속도보다 빠르게 실행되게 합니다. 이 단계는 즉각적인 성능 개선을 위한 초기 JIT 컴파일입니다.
TurboFan은 Sparkplug보다 더욱 복잡한 최적화를 수행하는 고급 JIT 컴파일러입니다. 이 컴파일러는 Sea of Nodes라는 그래프 기반의 중간 표현을 이용해 고급 최적화를 수행합니다. 함수 인라이닝, 타입 특화, 죽은 코드 제거, 범위 검사 제거 등 고급 최적화 기법을 적용해 가장 효율적인 머신 코드를 생성합니다.
런타임 동안 코드 실행 환경이 예상과 다르게 변화하거나 타입의 가정이 깨지는 경우가 발생할 수 있습니다. 이런 경우 V8은 즉각적으로 기존의 최적화 코드를 폐기하고, 이전 단계의 바이트코드 또는 Sparkplug 코드를 다시 사용합니다. 이후 보수적이면서도 안전한 방식으로 다시 최적화를 진행할 수 있습니다.
참고
[JS] V8엔진과 성능 최적화
Sparkplug, the new lightning-fast V8 baseline JavaScript compiler
JavaScript engine fundamentals: Shapes and Inline Caches