V8 엔진의 과거, 현재 구조

dalbodre·2022년 2월 2일
4

study

목록 보기
1/6
post-thumbnail

https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e?gi=e0eeb78a5d24 내용을 기반으로 정리한 내용입니다. 스터디 내용 정리를 더 이상 미뤄놓으면 안될 것 같아 사용했던 ppt를 기반으로 간단하게나마 정리해보려고 합니다.


과거)) 인터프리터 언어의 컴파일?

시작하기 전에 간단하게 짚고 넘어가자면, 컴파일 언어는 실행 전 별도로 소스코드를 컴파일하여 기계어로 변환하기 때문에 실행시간이 비교적 빠르고 인터프리터 언어는 코드를 실행할 때에 인터프리터가 기계어로 번역하고 실행기 때문에 비교적 느립니다.

javascript는 인터프리터 언어이지만 코드를 해석하고 실행하는 엔진에서 각자 인터프리터 방식으로 구현될 수도 있고, 컴파일러 방식으로 구현될 수도 있습니다. 특히 과거 V8 엔진의 경우 자바스크립트 수행 속도를 개선하기 위해 자바스크립트 코드를 (바이트 코드를 거치지 않고) 머신코드로 번역하여 사용하는 컴파일러 형식을 사용했습니다.

  • full-codegen : a simple and very fast compiler that produced simple and relatively slow machine code.
  • Crankshaft : a more complex (Just-In-Time) optimizing compiler that produced highly-optimized code.

풀코드젠은 자바스크립트를 머신코드로 변환하여 빠르게 실행할 수 있도록 해주는 일종의 컴파일러이고 크랭크샤프트는 풀코드젠으로 코드가 실행된 뒤, 자바스크립트의 추상구문트리를 최적화하여 static single-assignment, SSA form으로 변환하는 옵티마이저입니다.

V8 엔진은 내부적으로 여러 쓰레드를 사용하는데, 메인 쓰레드에서는 코드를 컴파일하고 실행하고, 프로파일러 쓰레드에서는 어떤 메서드에서 시간이 오래 걸리는지 알려주며, 그 외에도 가비지 컬렉터 등을 위한 여러 쓰레드가 있습니다. 크랭크샤프트는 풀코드젠으로 코드가 실행된 이후 프로파일러 쓰레드가 충분한 데이터를 얻게된 이후, 그 데이터들을 바탕으로 최적화를 실행합니다.


크랭크 샤프트의 최적화 기법

1. 인라이닝

크랭크샤프트의 최적화는 미리 가능한 많은 코드를 인라이닝(inlining)하는 것에서부터 시작합니다. 인라이닝이란 호출 지점(함수가 호출된 곳의 코드 위치)을 호출된 함수의 내용으로 바꾸는 단순한 과정으로, 이후의 최적화 과정에서 사용됩니다.

2. 히든 클래스

이후 크랭크 샤프트는 히든클래스를 생성합니다. 자바스크립트는 프로토타입 기반의 언어이기 때문에 실질적인 클래스라는 것은 없으며 각 객체는 복제 과정을 통해 생성되는데, 객체 속성 값의 위치를 메모리에 저장하기 때문에 속성값을 읽어오는 것이 자바나 C#같은 언어에서보다 코스트가 큽니다. (자바에서는 모든 객체 속성이 컴파일 전에 고정된 객체 레이아웃에 의해 설정되고 런타임에서는 동적으로 추가되거나 제거될 수 없기도 하고 자바스크립트에서 해시함수를 이용해서 메모리 상에서 객체 속성의 위치를 매번 찾아내는 것이 코스트가 크기도 함) 따라서 크랭크샤프트는 자바와 비슷하게 런타임에 객체에 포함되는 속성들을 미리 결정하는 방식을 사용합니다.


Point라는 예시를 통해 히든클래스가 어떻게 만들어지는지 살펴보겠습니다. 일단 new Point(1, 2)가 실행되면 V8은 이라는 C0 히든클래스를 생성합니다. C0은 첫번째 클래스 단계를 표현하기 위한 임의의 이름이며, Point에 아직 아무 속성도 정의되지 않았으므로 C0은 비어있습니다.


첫 번째 구문인 this.x = x가 수행되면 V8은 C0을 기반으로 C1이라는 두 번째 히든 클래스를 생성합니다. C1은 x속성을 찾을 수 있는 메모리상의 위치에 대한 설명을 포함하고 있으며, 생성되었던 C0 클래스에도 만약 x속성이 추가되면 객체가 가리키는 히든 클래스가 C0에서 C1으로 전환되어야 한다는 내용이 포함됩니다.


this.y = y가 수행될 때도 동일한 동작이 반복됩니다. C2라는 새로운 히든클래스가 생성되고, C1에 y속성이 포인트 객체에 추가되면 C2로 변경되어야 한다는 클래스전환이 추가되며, 포인트 객체의 히든 클래스는 C2로 업데이트됩니다.


여기서 중요한 점은 히든클래스 전환은 객체에 속성이 추가되는 순서에 의존적이라는 것입니다.
예시를 보면 p1과 p2는 모두 객체에 a와 b속성을 추가로 가지지만 서로 다른 순서로 선언되었습니다. 이들은 결과적으로 같은 속성들을 가지게 되지만, 각 개체는 서로 다른 히든클래스를 가리키게 됩니다. 오른쪽 파란 그림으로 확인할 수 있듯이 p1에서는 속성 a가 먼저 추가되고 p2의 경우 b가 먼저 할당되면서 전환 경로도 달라지고, 서로 다른 히든클래스를 사용하게 됩니다.
이와 같은 경우에 같은 히든클래스를 재사용할 수 있도록 속성을 같은 순서로 초기화하는 것이 성능상으로 훨씬 좋습니다.

3. 인라인 캐싱

마지막 최적화 방법 중 하나인 인라인 캐싱은 앞서 말한 히든클래스와도 관련이 있습니다. 특정 객체에 메소드가 호출될 때마다 V8엔진은 특정 속성에 접근하기 위한 오프셋을 계산하기 위해 해당 객체의 히든클래스를 뒤져봐야 합니다. 동일한 히든 클래스의 동일한 메소드의 호출을 두 번 성공하고나면 V8은 히든클래스를 찾는 것을 생략하고 단순하게 스스로 해당 객체 포인터에 속성 오프셋을 더해 놓습니다. 이후 해당하는 메소드에 대한 모든 호출이 있을 때마다 V8엔진은 히든클래스가 변하지 않았다고 가정하고 이전에 캐싱해두었던 오프셋을 이용해 직접 메모리 주소로 이동하고, 이를 통해 실행 속도는 크게 증가합니다.

앞의 예제처럼 타입은 같지만 런타임에서 만들어지는 히든클래스가 다른 객체의 경우(C4와 C6) 인라인캐싱 조건인 “동일한 히든 클래스"를 만족하지 않기 때문에, 최대한 히든클래스를 공유할 수 있도록 하는 것이 성능에 유리합니다


현재)) 이그니션과 터보팬

V8엔진은 5.9버전에서 크게 바뀌었는데, 파이프라인의 변화를 통해 성능 향상과 더불어 자바스크립트 응용프로그램들에서 메모리를 현저하게 절약할 수 있게 되었다고 합니다. 새로운 파이프라인은 인터프리터인 이그니션과 최적화 컴파일러인 터보팬으로 구성되어 있습니다.

저는 몰랐지만 타 블로그에서 V8은 8기통 엔진을, Ignition은 엔진에 시동걸 때 사용하는 점화기, TurboFan 또한 (실행 중 코드가? 너무 뜨거워졌을 때 최적화를 통해) 과열된 상태를 식혀준다는 의미가 있다고 합니다.

Ignition은 기존의 Full-codegen을 대체하는 인터프리터입니다. 기존의 Full-codegen은 전체 소스 코드를 한번에 머신코드로 컴파일하면서 메모리 사용량이 컸는데, 모바일에서 특히 문제가 도드라졌다고 합니다. 이를 해결하기 위해 인터프리터 방식으로 돌아와 자바스크립트코드를 한줄한줄 실행할 때마다 머신코드 이전 단계인 바이트코드 상태로 컴파일하는 이그니션을 사용하게 되었습니다. 이를 통해 메모리 사용량 감소 뿐만 아니라, 파싱하기에도 편해졌으며, 터보팬에서 최적화를 할 때에 바이트코드만 고려하면 됐기 때문에 터보팬에서의 최적화 또한 훨씬 편리해졌다고 크롬 모바일팀에서 발표했습니다. (실제로 7개의 컴퓨터 아키텍쳐를 지원하기 위해서 사용되던 코드가 만육천 줄에서 3천줄로 감소했다고 함. 초기 실행에는 시간이 약간 걸리지만 바이트코드로라도 변환한 이후부터는 c++에 근사하는 성능을 낼 수도 있다고 함. 단, 잘짜진 코드만 ㅎㅎ;;)

터보팬의 경우 이그니션에서 만들어진 바이트 코드를 기반으로, 크랭크샤프트에서 최적화 단계에서 사용하던 히든클래스와 인라인 캐싱을 통해 최적화를 진행합니다. (최적화의 시작은 프로파일러 쓰레드가 메인 쓰레드를 감시하다가 일정 기준 이상 동일한 함수가 호출되면 시작됩니다.)

기본적인 동작 방식은 여기까지입니다! GC나 React Native에서 사용하는 헤르메스 엔진 등등 스터디에서 다뤘던 내용은 한참 남아있는데, 모두 다 정리할 생각을 하니 막막하네요.. 천천히 한걸음부터 해보겠습니다!! 😁😁

profile
휘뚜루마뚜루

1개의 댓글

comment-user-thumbnail
2022년 4월 9일

굿굿

답글 달기