[번역] How the V8 engine works?

V8 엔진에 관한 한글 문서가 부족해서 이 글을 번역하고 개인적으로 몰랐던 용어들에 대한 설명을 추가했습니다. 이 문서 또한 비슷한 내용을 담고 있네요(번역 후에 찾아버림).

V8 엔진은 어떻게 동작할까?

엔진의 동작 원리와 엔진에게 효율적인 JavaScript 코드 작성법

V8이란?

V8은 독일 구글 개발 센터에서 만들어진 JavaScript 엔진이다. 오픈 소스이고 C++로 작성되었다. 클라이언트쪽(Google Chrome)과 서버쪽(node.js) JavaScript 어플리케이션 모두에 쓰인다.

V8은 웹 브라우저 안에서 실행되는 JavaScript의 성능을 높이기 위해 처음 고안되었다. 속도를 높이기 위해서 V8은 인터프리터를 이용하는 대신 JavaScript 코드를 좀더 효율적인 기계어 코드로 번역한다. V8은 SpiderMonkey나 Rhino(Mozilla)같은 많은 요즘의 JavaScript 엔진처럼 JIT(Just-In-Time) 컴파일러를 적용하여 JavaScript 코드를 실행할 때 컴파일하여 기계어 코드로 만든다. V8의 가장 큰 차이는 바이트코드 또는 다른 중간 코드를 생성하지 않는 다는 것이다.

이 글은 V8이 클라이언트단, 서버단에서 최적화된 코드를 생성하기 위해 어떻게 하고 있는지 이해하고 설명하기 위해서 쓰게 되었다. 당신이 “내가 JavaScript 성능을 신경써야 할 필요가 있나?” 라고 묻고 있다면 이 인용문으로 답을 해주겠다. Daniel Clifford(tech lead, manager of the V8 team): “그저 지금 당신의 어플리케이션을 빠르게 하는 데 관한 것뿐만 아니라, 당신이 예전에 할 수 없었던 것들까지 가능하게 만드는 데에 대한 것입니다.”

Hidden class

JavaScript는 프로토타입 기반 언어이다: 클래스가 없고 오브젝트는 복제하는(cloning) 프로세스를 이용해서 만들어진다. 또한, JavaScript는 동적으로 타입된다: 타입과 타입 정보는 명시적이지 않고 실행 중에 오브젝트의 프로퍼티가 덧붙여지고 삭제될 수 있다. 타입과 프로퍼티에 효율적으로 접근하는 것이 V8의 어려운 첫번째 도전과제였다. 오브젝트의 프로퍼티를 저장하고 프로퍼티가 저장된 곳을 알기 위한 동적인 탐색(lookup)을 하기 위해 dictionary-like 자료 구조를 사용하는 대신에(대부분의 JavaScript 엔진들이 하고 있다) V8은 타입 시스템을 내부적으로 표현하고 프로퍼티 접근 시간을 줄이기 위한 히든 클래스를 실행시간에 생성한다.

“Point” 함수와 두 개의 “Point” 오브젝트들 생성으로 예를 들어보자.

지금의 예처럼 레이아웃이 같다면 p와 q는 V8이 생성한 같은 히든 클래스에 속한다. 이 점은 히든 클래스를 사용하는 또다른 장점을 부각시킨다: V8이 프로퍼티가 같은 오브젝트들을 묶을 수 있게 해준다. 여기 “p”와 “q”는 같은 최적화된 코드를 사용한다.

이제 “q” 오브젝트를 선언한 직후 “z” 프로퍼티를 덧붙이고 싶다고 해보자(동적인 타입 언어에서는 아무 문제없다).

어떻게 V8이 이 시나리오에 대처할까? 사실 V8은 생성자 함수가 프로퍼티를 정의할 때마다 새로운 히든 클래스를 생성하고 히든 클래스의 변화를 추적한다. 왜일까? 왜냐면 두 오브젝트(“p”와 “q”)가 생성되었고 두번째 오브젝트(“q”)가 생성된 후에 멤버가 더해졌다면, V8은 생성된 마지막 히든 클래스(첫번째 오브젝트 “p”를 위한)를 가지고 있어야하고 새로운 멤버를 가진 새로운 히든 클래스(두번째 오브젝트 “q”를 위한)도 생성해야 하기 때문이다.

새 히든 클래스가 생성될 때마다 이전의 것은 대신 사용될 히든 클래스로 업데이트 된다.

코드 최적화

V8이 각 프로퍼티에 대해 새로운 히든 클래스를 생성하기 때문에 히든 클래스는 최소한으로 생성되어야 한다. 그러기 위해선 오브젝트가 생성된 후에 프로퍼티를 더하는 것을 피하고 항상 오브젝트 멤버를 같은 순서로 선언해라(히든 클래스 여러 개 생성을 피하기 위해서).

[업데이트] 다른 요령: 단형적(monomorphic) 연산은 같은 히든 클래스인 오브젝트들에만 적용되는 연산이다. V8은 함수를 호출할 때 히든 클래스를 생성하는데, 만약 다른 parameter 타입들로 다시 호출했다면 V8은 또다른 히든 클래스를 만들어야 한다: 다형적(polymorphic) 코드보다 단형적 코드를 선호해라.

어떻게 V8이 JavaScript 코드를 최적화 하는지 추가 예제들

Tagged values

숫자와 JavaScript 오브젝트에 대해 효율적으로 나타내기 위해 V8은 32 bit 를 사용한다. 한 비트를 이용해 이것이 오브젝트인지(flag = 1) integer인지(flag = 0) 알 수 있다. 이때 integer는 SMall Integer 또는 SMI라고 불리는 데 31 bit이기 때문이다. 그리고 숫자값이 31 bit보다 크다면 V8은 숫자를 감싸서(box) double로 만들고 이 숫자를 넣을 새 오브젝트를 생성한다.

코드 최적화: JavaScript 오브젝트로 만드는 비싼 boxing 연산을 피하기 위해 가능하면 31 bit signed number 를 사용해라.

Arrays

V8은 array를 다루기 위해 여러 방법들을 사용한다: * Fast elements: 키값들이 매우 밀집된 array들을 위해 고안되었다. array들은 매우 효율적으로 접근할 수 있는 선형의 저장 버퍼를 가진다. * Dictionary elements: 모든 엘리먼트들이 없는 sparse array를 위해 고안되었다. 이건 사실 hash table인데 “Fast Elements”보다 접근이 더 비싸다.

코드 최적화: V8이 array를 다루기 위해 “Fast elements”를 사용하고 있는 지 확인해라. 그러니까, 키들이 듬성듬성있는 sparse array를 피해라. 또한, 미리 큰 array를 할당하는 것을 피해라. 자연스럽게 커지는 것이 낫다. 마지막으로 array 안의 엘리먼트들을 삭제하지 마라: 키 집합을 듬성듬성하게 만든다.

V8이 어떻게 JavaScript 코드를 컴파일할까?

V8은 두 개의 컴파일러를 가진다!

  • “전체(Full)” 컴파일러는 어떤 JavaScript 코드도 좋은 코드로 만들 수 있다. 하지만 훌륭한 JIT 코드는 아니다. 이 컴파일러의 목표는 코드를 빨리 생성하는 것이기 때문에 어떤 타입 분석도 하지 않고 타입에 대해서 무지하다. 대신에 프로그램이 돌아가는 도중에 타입에 대한 지식을 정교하게 하는 Inline Caches 또는 “IC” 전략을 사용한다. IC는 매우 효율적이고 20배 빠른 속도 향상을 가져왔다.
  • 최적화 컴파일러는 대부분의 JavaScript 언어에 대해 훌륭한 코드를 생성한다. 나중에 실행되고 hot 함수들을 재컴파일한다. 최적화 컴파일러는 Inline Cache에서 타입들을 얻고 코드를 어떻게 하면 더 최적화할 수 있을지 결정한다. 그런데 몇 언어 특징들, 예를 들어 try/catch 블록같은 것들은 아직 지원하지 않는다. (try/catch 블록에 대한 최적화 작업은 “불안정한” 코드를 함수로 작성하고 try 블록에서 그 함수를 호출하려는 것으로 시도하고 있다)

코드 최적화: V8은 또한 역최적화를 지원한다: 최적화 컴파일러는 다른 타입들에 관한 Inline Cache로부터 최적의 가정을 하는데 역최적화는 이 가정들이 유효하지 않을 때 실행된다. 예를 들면 생성된 히든 클래스가 예상한 것과 다를 때 V8은 최적화된 코드를 버리고 Inline Cache로부터 다시 타입들을 얻는 전체 컴파일러로 돌아온다. 이 과정은 느리며 최적화 이후 함수들을 변경하지 않음으로써 피해야한다.

Resources

  • Google I/O 2012 “Breaking the JavaScript Speed Limit with V8” with Daniel Clifford, tech lead and manager of the V8 team:video and slides.
  • V8: an open source JavaScript engine: video of Lars Bak, V8 core engineer.
  • Nikkei Electronics Asia blog post: Why Is the New Google V8 Engine So Fast?
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.