hmk run dev

자바스크립트와 V8 본문

javascript

자바스크립트와 V8

hmk run dev 2024. 4. 11. 21:51

마이크로프로세서(CPU)

 

일단 V8엔진에 대해서 설명하기 전에 우리가 작성한 javscsript 코드가 어떻게 실행되는지 아주 간단하게 훑어보자

 

컴퓨터의 모든 시스템은 마이크로프로세서(CPU)를 포함하고 있다.

지금 블로그 글을 보고 있는 것도 그 덕분이다.

 

마이크로프로세서는 전자적인 신호로 동작하는 기계이다.

우리는 마이크로프로세서에게 지시(instruction)를 주고 마이크로프로세서는 job을 수행한다.

 

여기서 지시란 것은 마이크로프로세서가 해석할 수 있는 언어로 되어있다.

 

일반적으로 IA-32, x86-64, MIPS 그리고 ARM이 있다.

이러한 언어들은 직접 하드웨어와 소통하며 기계어라고 불린다.

 

우리가 컴퓨터에 코딩한 고수준 언어들은 컴파일되어 기계어 되어 마이크로프로세서가 실행할 수 있는 형태가 된다.

 

음.. 사실 위의 내용엔 약간의 오류가 있다.

V8 엔진은 javascript를 기계어가 아닌 바이트 코드로 컴파일한다.

이 부분은 아래 V8엔진의 작동원리의 컴파일 단계에서 적어놓았다.

 


V8 엔진의 작동원리를 살표 보는 이유

 

자바스크립트로 뽑아낼 수 있는 최적의 성능을 사용하고 싶다면
내 코드가 어떤 식으로 실행되는지에 대한 이해는 어느 정도 있어야 한다.


V8 엔진이란?

구글이 주도하여 C++로 작성된  고성능의 자바스크립트 & 웹어셉블리 엔진

오픈 소스로 git repo에서 코드를 살펴볼 수 있다.

 

주로 Google Chrome과 NodeJS에서 사용되고 있으며, ECMAScript와 WebAssembly를 표준에 맞게 구현했다.

 


V8 엔진의 작동원리

 

1. 파싱(Parsing)

 

자바스크립트 엔진은 소스 코드를 읽고 이를 토큰으로 나누어 파싱 합니다.
파싱은 코드를 추상 구문 트리(Abstract Syntax Tree, AST)로 변환하는 과정입니다.

 

우리가 작성한 소스 코드를 컴퓨터가 알아먹기 쉽게 구조화하는 단계

 

 


2. 최적화(Optimiaztion)


V8 엔진은 추상 구문 트리를 최적화하는 단계를 거칩니다.
이 단계에서는 코드의 성능을 향상하기 위해 여러 최적화 기법을 적용합니다. 예를 들어, 인라이닝, 인라인 캐싱, 코드 인라이닝 등이 있습니다.

 


 

3. 컴파일(Compilation):


최적화된 추상 구문 트리는 기계어로 변환됩니다.

여기서 최적화된 코드를 바이트로 변환하는 Ignition이 실행됩니다.
V8 엔진은 고성능 JIT(Just-In-Time) 컴파일러를 사용하여 코드를 컴파일합니다.
JIT 컴파일러는 자주 사용되는 코드 블록을 미리 컴파일하여 성능을 향상합니다.

 

 

 

Ignition으로 바이트 코드(Bytecode) 생성하기

 

자바스크립트는 C++과 같은 정적 타이핑 언어가 아닌 동적 타이핑 언어라서
소스 코드가 실행되기 전에는 알 수 없는 값들이 너무 많았기 때문에 이런 접근 방법으로는 최적화를 하기도 힘들었다고 한다.

 

그래서 Ignition을 개발할 때는 모든 소스를 한 번에 해석하는
컴파일 방식이 아닌 코드 한 줄 한 줄이 실행될 때마다 해석하는 인터프리트 방식을 채택하여 다음 세 가지 이점을 가져가고자 하였다.

 

 

1. 메모리 사용량 감소. 자바스크립트 코드에서 기계어로 컴파일하는 것보다 바이트 코드로 컴파일하는 것이 더 편하다.
2. 파싱 시 오버헤드 감소. 바이트 코드는 간결하기 때문에 다시 파싱 하기도 편하다.
3. 컴파일 파이프 라인의 복잡성 감소. Optimizing이든 Deoptimizing이든 바이트 코드 하나만 생각하면 되기 때문에 편하다.

- Ross McIlroy Ignition - an interpreter for V8

 

 

간단하게 말해서 Ignition은 코드가 한줄한줄 실행될 때마다 코드를 바이트 코드로 바꿔주는 역할을 한다고 기억해 두면 좋다.

 

 

TurboFan으로  최적화하기

 

TurboFan 최적화된 바이트코드를 기계어로 컴파일하는 역할을 합니다.

  • TurboFan은 V8 엔진의 최적화 단계에서 사용됩니다.
  • TurboFan은 최적화된 바이트코드를 받아들여 최적화된 기계어 코드로 컴파일합니다.
  • TurboFan 자주 사용되는 코드 블록을 추적하고, 이를 기반으로 최적화를 수행합니다. 이를 통해 성능을 향상시키고, 실행 시간을 최소화합니다.

최적화 기법으로는 히든 클래스(Hidden Class)인라인 캐싱(Inline Caching) 등 여러 가지 기법을 사용한다.

간단히만 설명하면

 

히들 클래스 - 비슷한 것들끼리 분류해 놓고 가져다 사용

인라인 캐싱 - 자주 사용되는 코드가 만약 excute()와 같은 함수의 호출 부라면 이걸 function excute () {... }와 같이

함수의 내용으로 바꿔서 캐싱하는 기법

 

 


4. 실행(Execution):

 

컴파일된 코드는 CPU에서 실행됩니다.
실행 중에는 필요에 따라 동적 최적화가 수행될 수 있습니다. 이는 코드의 실행 도중에 특정 패턴이나 상황을 감지하여 더 효율적인 방식으로 코드를 실행하도록 하는 것입니다.

 


5. 가비지 컬렉션(Garbage Collection)


실행 중에 더 이상 사용되지 않는 메모리를 회수하기 위해 가비지 컬렉션 기능이 수행됩니다.
V8 엔진은 가비지 컬렉션을 효율적으로 수행하여 메모리 누수를 방지합니다.

 

 


그렇다면 어떻게 코드를 짜야할까?

 

Hidden Classes

  • Javascript는 컴파일시에 사용하는 타입정보에 대해 제한적이다.
  • Javascript는 런타임시에 데이터 타입을 변경할 수 있다.
  • V8은 런타임시에 객체 처리를 위해 내부적으로 hidden class를 만들어서 사용한다.
function Point(x, y) {
  this.x = x;
  this.y = y;
}

var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
// 여기에서 p1과 p2는 hidden class를 공유합니다.
p2.z = 55;
// 경고! p1과 p2는 이제 다른 hidden class를 갖습니다.

 

결론

  • 모든 객체 멤버를 생성자 함수 안에서 초기화 (나중에 멤버 타입이 변하지 않음)
  • 항상 같은 순서로 멤버를 초기화

 


Numbers

  • V8은 데이터 타입 변환시 값을 효율적으로 나타내는 태그 사용
  • 사용자가 사용하는 값을 통해서 number타입 추론
  • 데이터 타입은 동적으로 변할 수 있기 때문에, 효율적으로 값을 나타내는 태그 사용
  • number타입을 지속적으로 쓰는 것이 중요

 

var i = 42;  // 31비트 부호있는 정수입니다.
var j = 4.2;  // 이 값은 double 타입의 부동 소수점 숫자 데이터입니다.

 

결론

  • 31비트 부호있는 정수를 사용

 

 


Normal Arrays

  • 큰 배열 처리를 위해 두가지 유형의 내부 배열 저장소가 존재한다.
    • Fast Elements : 키 값이 순서대로 채워진 경우 사용되는 선형 저장소
    • Dictionary Elements : 위 경우가 아닐 때 사용하는 해쉬 테이블 저장소
  • 배열 저장소가 한 유형에서 다른 유형으로 변경되지 않는것이 중요

 

  a = new Array();
  for (var b = 0; b < 10; b++) {
    a[0] |= b;  // 안 좋아요!
  }
  //vs.
  a = new Array();
  a[0] = 0;
  for (var b = 0; b < 10; b++) {
    a[0] |= b;  // 훨씬 좋습니다. 2배 더 빨라요.
  }

결론

  • 인덱스 0부터 시작하는 연속키 사용
  • 배열 선언시 최대사이즈를 할당하지 말고 ( > 64K 원소), 사용하면서 크기를 늘려간다
  • 숫자 배열의 요소를 삭제하지 않는다
  • 초기화 안한 요소는 호출하지 않는다 (아래 코드 참조)

 

Reference

 

https://evan-moon.github.io/2019/06/28/v8-analysis/

 

V8 엔진은 어떻게 내 코드를 실행하는 걸까?

이번 포스팅에서는 구글의 V8 엔진이 어떤 방식으로 자바스크립트를 해석하고 실행하는지 살펴 보는지에 대해 포스팅하려고 한다. 은 로 작성되었지만 필자의 메인 언어가 이 아니기도 하고, 워

evan-moon.github.io

https://velog.io/@ainochi95/20mV8-%EC%97%94%EC%A7%84%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%84-%EA%B0%9C%EC%84%A0%ED%96%88%EC%9D%84%EA%B9%8C

 

V8 엔진은 무엇을 개선했을까?

2012년 구글 I/O 2012에서 발표한 JS의 성능에 관련된 발표가 있다

velog.io

 

https://engineering.linecorp.com/ko/blog/v8-hidden-class

 

V8의 히든 클래스 이야기

자바스크립트가 되어 그 기분을 헤아릴 수 있다면 안녕하세요? LINE Fukuoka의 프론트엔드 엔지니어 Yonehara입니다. 저는 프론트엔드 개발자로서 아직 웹 브라우저나 자바스크립트의 기분을 헤아려

engineering.linecorp.com

 

https://joshua1988.github.io/web-development/web-perf/perf-tip-javascript-in-v8/

 

'javascript' 카테고리의 다른 글

자바스크립트의 call by value와 call by reference  (0) 2024.03.13
자바스크립트 가비지 콜렉션  (0) 2024.03.10
CommonJS와 ESM(esModule)  (0) 2024.02.24
자바스크립트의 stack & heap  (0) 2024.01.18
javascript 동작 원리  (0) 2023.04.12
Comments