home..

[V8 Study] 00-introduction

[!] 우선적으로 한글로 작성하여 추후 번역할 예정.

Summary

우리가 대중적으로 사용하는 Google Chrome browser에는 웹페이지를 동적으로 실행시키기 위해 javascript가 구동될 수 있도록 지원하고 있다. 그렇다면, JS를 원활하게 실행하고 컴파일하는 역할은 어떤 것이 맡고 있을까? 이 챕터에서는 내가 공부했던 chromium javascript engine인 V8에 대해서 알아보고자 한다.

V8의 구조는 어떻게 되어 있을까?

V8같은 경우 Chrome 구동 시 하나의 프로세스로 실행되어 heap 및 stack 메모리 영역을 관리하면서 Javascript의 각종 변수와 메소드를 실행하는 가장 핵심적인 execution file로 동작한다. 이외에 Task/Callback queue, Microtask queue, WebAPI 등 다양한 요소가 Chromium browser를 구성하고 동작하는데 사용된다.

가장 대표적인 역할은 javascript 코드를 컴파일된 컴퓨터에 최적화된 코드로 변환시켜 전달하게 된다는 것이다. 이때 JIT(Just-In-Time) Compiler라는 독창적인 컴파일러를 사용한다.

V8만의 특징은 무엇이 있을까?

inter-comp.png

우선 인터프리터와 컴파일러에 대해 알아야 할 것이다.

  1. 인터프리터
    • 장점 : 한 줄 한 줄 변환하기 때문에 실행 속도가 빠르다.
    • 단점 : 자바스크립트 코드가 복잡해질수록 점점 속도가 느려진다. 예를 들어, 같은 코드를 여러차례 반복하는 반복문의 경우, 같은 결과를 반복하는 것임에도 불구하고 코드를 한 줄 한 줄 읽는 방식에 의해 그때그때 실행된다.
  2. 컴파일러
    • 장점 : 파일 전체를 한번에 읽어서 컴파일러는 작업을 단순화시킨다. 예를 들어 특정 함수를 10억번 반복해야 할 경우, 컴파일 과정에서 함수를 반복하는 것이 아니라 함수의 결과물을 반복하도록 컴파일 한다. 이처럼 불필요한 동작을 제거하는 컴파일러의 방식을 최적화, optimization이라고 한다. (인터프리터는 optimize하지 않는다 )
    • 단점 : 코드를 바로 실행하지 않고, 코드 실행 전 전체를 컴파일 하는 과정이 필요하기 때문에 초기에 속도가 느릴 수 밖에 없다.

자바스크립트 엔진의 내부를 공부하다가 잠시 인터프리터와 컴파일러의 간단한 개념과 장단점을 짚어봤다. 그러면 과연 V8 엔진은 어떤 방식을 취했길래 다른 자바스크립트 엔진보다 더 빠르고 효율적으로 자바스크립트 명령을 수행할 수 있을까? 바로 여기에서 V8 엔진의 독특한 언어 변환 도구인 JIT Compiler가 들어온다. JIT은 Just In Time의 약자이다.

JIT Compiler의 구조

jit.png

V8 엔진은 다음과 같은 구조로 되어 있다.

  1. Parser : 코드를 Lexical Analysis라는 과정을 통해 토큰으로 분해한다.
  2. AST(Abstract Syntax Tree) : Parser에서 분해된 토큰을 바탕으로 추상적인 트리를 생성한다.
  3. Ignition : AST에서 나온 트리를 V8의 인터프리터인 ignition에게 전달하고 이것을 인터프리터는 bytecode로 변환시킨다. (High-level Lang to Low-level Lang)
  4. TurboFan, Profiler : bytecode를 실행하여 Human-readable Lang을 실행시키고, 그중 자주 사용되는 코드라고 판별된 코드 조각은 TurboFan으로 보내져 최적화를 수행하여 새로운 컴파일된 코드로 재구성된다. (이때 자주 사용되는 코드라는 말이 매우 추상적이므로 Profiler 라는 친구한테 사용 빈도 수 등을 수집하게끔 역할을 부여하고, 모인 데이터를 통해 Turbofan의 알고리즘대로 최적화를 한다)

Turbofan은 어떤 조건으로 최적화하는 걸까?

  1. 코드가 뜨겁고 안정적인 것, 쉽게 말하면 자주 호출되고(뜨겁고) 코드가 안 변함(안정적)이라는 것이다. 매번 같은 행동을 수행하는 반복문 내에 있는 코드 같은 경우가 여기에 해당하기 쉽다.

  2. 인터프리팅된 바이트 코드의 길이를 보고 특정 임계점을 넘기지 않으면 작은 함수라고 판단해서 최적화를 진행하는 것이다. 작고 단순한 함수는 크고 복잡한 함수보다 동작이 매우 추상적이거나 제한적인 확률이 높기 때문에 안정적이라고 볼 수 있다.

[!] 그러나, 컴파일러는 100% 완벽하지 못하기 때문에 의도와 다르게 발적화(deoptimization)가 일어날 수도 있다.

Next Topic

다음에는 V8에서 자료형이나 JS의 다양한 메소드가 어떤 식으로 저장이 되고 불러올 수 있는지를 알아보고자 한다.

reference




© 2024 Ainsetin   •  Powered by Soopr   •  Theme  Moonwalk