blog.pisik.club
article thumbnail
728x90

V8 엔진

V8 엔진은 구글 크롬과 Node.js에서 사용하는 자바스크립트 엔진입니다.
이 엔진은 자바스크립트 코드를 고속으로 실행 할 수 있도록 설계되었으며, Just-In-Time(JIT) 컴파일러 를 이용해 성능을 극대화합니다.

 

주요특징

  • 자바스크립트 코드를 바이트코드(Bytecode) 로 변환 후 실행
  • 실행 속도를 높이기 위해 JIT(Just-In-Time) 컴파일 사용
  • 가비지 컬렉션(Garbage Collection) 으로 메모리 관리

 


 

V8 엔진의 주요 구성 요소

V8 엔진은 크게 파싱 → 컴파일 → 실행 → 최적화 과정을 거칩니다.
이 과정을 담당하는 핵심 요소는 다음과 같습니다.

1️⃣ 파서(Parser): 자바스크립트 코드를 분석하여 추상 구문 트리(AST) 생성
2️⃣ 인터프리터(Interpreter) - Ignition: AST를 바이트코드(Bytecode)로 변환
3️⃣ 컴파일러(Compiler) - TurboFan: 바이트코드를 최적화하여 기계어(Machine Code) 로 변환
4️⃣ 가비지 컬렉터(Garbage Collector): 불필요한 메모리를 자동으로 정리

 


 

V8 엔진 실행 과정 

파싱 - AST(Abstract Syntax Tree) 생성

자바스크립트 코드는 파서(Parser) 를 통해 분석되며, AST로 변환됩니다.

 

예제

function add(x, y) {
    return x + y;
}
add(2, 3);

 

AST 변환

FunctionDeclaration
  ├── Identifier (add)
  ├── Parameters (x, y)
  ├── BlockStatement
      ├── ReturnStatement
          ├── BinaryExpression (+)
              ├── Identifier (x)
              ├── Identifier (y)

 

AST는 자바스크립트 코드를 트리 구조로 변환하여, 컴퓨터가 쉽게 분석할 수 있도록 합니다.

 


 

Ignition 인터프리터 - 바이트코드 변환

V8 엔진의 Ignition 인터프리터는 AST를 바이트코드 로 변환합니다.
바이트코드는 기계어보다 상위 개념으로, 최적화 없이 빠르게 실행할 수 있습니다.

 

예제

function multiply(a, b) {
    return a * b;
}
multiply(3, 4);

 

바이트코드 변환

0x00 LdaNamedProperty a
0x01 MulNamedProperty b
0x02 Return

 

바이트코드는 CPU가 직접 실행할 수 없지만, V8 엔진이 이를 해석 및 실행합니다.

 


 

TurboFan 컴파일러 - 최적화 컴파일(기계어)

V8 엔진은 실행 성능을 높이기 위해 TurboFan 컴파일러 를 사용합니다.
TurboFan은 JIT 컴파일러을 사용하여, 자바스크립트 코드를 실행 시 필요한 부분만 즉시 기계어로 변환하고 실행이 반복되는 코드를 추가로 최적화하며 불필요한 연산을 제거하여서 성능을 최적화합니다.

 

예시

function add(x, y) {
    return x + y;
}


console.log(add(1, 2));  // 정수 
console.log(add(1.2, 2.2));  // 부동소수점 
console.log(add("Hello, ", "World!")); // 문자열 연결 (최적화 불가능)

 

동작과정

1️⃣ add(1, 2)을 실행하면 V8 엔진은 x와 y가 정수라는 것을 학습
2️⃣ add(1.2, 2.2)을 실행하면 부동소수점 연산도 가능하도록 추가 분석
3️⃣ add("Hello, ", "World!") 실행하면 숫자 연산 최적화가 무효화됨
4️⃣ TurboFan은 한 가지 타입만 다룰 때 가장 빠르게 실행되도록 최적화하지만, 다양한 타입이 혼합되면 최적화를 해제하고 일반적인 연산 방식으로 돌아감.

즉, 일관된 타입을 사용해야 TurboFan을 최대로 활용할 수 있음.

 

 

예시2

function sumArray(arr) {
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
        sum += arr[i];
    }
    return sum;
}

let numbers = [1, 2, 3, 4, 5];
console.log(sumArray(numbers));

 

동작과정

1️⃣ 작은 크기의 배열에 대해 반복문을 분석
2️⃣ 반복문이 몇 번 실행될지 예측 가능하면 루프 풀어내기 수행
3️⃣ 아래처럼 내부적으로 변환하여 반복문 없이 실행. 즉 루프를 걷어내버림

 

예시2 결과

sum = arr[0] + arr[1] + arr[2] + arr[3] + arr[4];

 


 

메모리 관리 - 가비지 컬렉션

자바스크립트는 개발자가 명시적으로 메모리를 해제할 필요 없이 V8 엔진이 자동으로 메모리를 관리합니다.
V8 엔진은 Mark-Sweep-Compact 알고리즘을 사용하여 불필요한 데이터를 자동 정리합니다.

 

예제

function createUser() {
    let user = { name: "미노" };
    return user;
}

let user1 = createUser();
user1 = null;

 

user1이 null이 되면, 가비지 컬렉터가 해당 메모리를 정리합니다.

 

 

 


 

 

 

V8 엔진의 최적화 기법

V8 엔진은 실행 속도를 높이기 위해 다양한 최적화 기법을 사용하무니다.

 

인라인 캐싱(Inline Caching)

자주 실행되는 함수를 캐싱하여 속도를 높임

function greet(name) {
    return "Hello, " + name;
}
console.log(greet("민호")); // 캐싱완
console.log(greet("마이노")); // 속도 최대로

 

히든 클래스(Hidden Class) 

자바스크립트 객체의 구조를 분석하여 최적화

function User(name, age) {
    this.name = name;
    this.age = age;
}
let user1 = new User("민호", 35);
let user2 = new User("유리", 32);

 

V8 엔진은 User 객체의 클래스 정보를 캐싱하여 빠르게 접근할 수 있도록 최적화합니다.

 

 

 


 

정리

위 내용들을 참고해서 TurboFanIgnition을 적절하게 사용하는 방법은 뭘까여?

 

 

데이터 타입을 일관되게 하자

function add(a, b) {
    return a + b;
}

console.log(add(1, 2));       // 숫자 연산 최적화 하는중
console.log(add("Hello", 5)); // 갑자기 문자열나옴 최적화 해제


=============================================================


function add(a, b) {
    return a + b;
}

console.log(add(2, 3));  //숫자 연산 최적화 하는중
console.log(add(5, 10));  //또 숫자 계속 최적화 중

 

 

객체의 구조를 일관되게 하자 

 

function User(name) {
    this.name = name;
}

let user1 = new User("민호");
user1.age = 35; // 나중에 속성 추가해버리니까 최적화 바로 해제


------------------------------------------------------------


function User(name, age) {
    this.name = name;
    this.age = age;  // 처음부터 모든 속성을 정의
}

let user1 = new User("민호", 35);

 

 

인라인 캐싱을 활용하자

function getAge(obj) {
    return obj.age;
}

let user1 = { name: "민호", age: 35 };
let user2 = { nickname: "유리", years: 32 }; // 속성명이 다름 즉 캐싱안되버림

console.log(getAge(user1));
console.log(getAge(user2)); // 캐싱 브레이크


-----------------------------------------------------------------


function getAge(user) {
    return user.age;
}

let user1 = { name: "민호", age: 35 };
let user2 = { name: "유리", age: 32 };

console.log(getAge(user1)); 
console.log(getAge(user2)); // 동일한 구조로 인라인 캐싱 적용(캐싱 최대로)

 

 

반복문 최적화

let numbers = [1, 2, 3, 4, 5];

for (let i = 0; i < numbers.length; i++) { 
    console.log(numbers[i]); // length를 매번 서칭 
}


---------------------------------------------------


let numbers = [1, 2, 3, 4, 5];
let length = numbers.length; // length를 미리 저장

for (let i = 0; i < length; i++) { 
    console.log(numbers[i]); 
}

 

 

 

함수 최적화

const add = function(a, b) { // 익명함수는 V8이 해석하기 힘듦
    return a + b;
};


------------------------------------------------


function add(a, b) { //명시된 함수는 TurboFan 풀가동 가능
    return a + b;
}

 

 

감사합니다!!

728x90
profile

blog.pisik.club

@pisik

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!