V8 성능 튜닝 핵심 전략

rada·2025년 4월 6일
0

개발

목록 보기
32/43

🚀 V8 성능 튜닝 핵심 전략

1. ✅ Hidden Class 최적화

✳️ 개념

V8은 객체에 Hidden Class를 붙여서, 객체 구조를 빠르게 파악하고 IC를 최적화함.

⚠️ 안 좋은 예

function makeUser(name) {
  const user = {};
  user.name = name;
  user.age = 30; // → 여기가 나중에 추가되면 Hidden Class 변화
  return user;
}

✅ 좋은 예

function makeUser(name) {
  const user = { name: name, age: 30 }; // → 한 번에 정의, Hidden Class 고정
  return user;
}

💡 팁

  • 객체 프로퍼티를 항상 같은 순서, 같은 시점에 정의하라
  • 클래스를 쓰면 거의 자동으로 해결됨 (class User { constructor(...) {...} })

2. 🌀 Inline Cache를 깨지 말 것

⚠️ 문제되는 예

function print(obj) {
  console.log(obj.value);
}

print({ value: 1 });        // 첫 호출: Monomorphic IC
print({ value: "hello" });  // 타입은 같지만 구조가 다름 → Polymorphic IC
print({ val: 1 });          // 다른 프로퍼티 이름 → Megamorphic fallback

###💡 팁

  • 동일한 구조의 객체를 반복 사용하라
  • 동일한 프로퍼티명, 순서 유지
  • 클래스나 팩토리 함수 패턴 사용

3. 🧠 Hot Path 분리

예: 타입이 섞이는 연산

function sum(x, y) {
  return x + y;
}
  • 처음에 number만 들어오면 TurboFan이 float64 add로 최적화
  • 나중에 string 들어오면 Deopt

✅ 해결 방법

function sum(x, y) {
  if (typeof x === 'number' && typeof y === 'number') {
    return x + y;
  }
  return String(x) + String(y);
}

→ 타입 guard를 넣어서 Hot Path를 분리, Speculative Optimizer가 안전하게 최적화 가능

4. 📦 배열은 Packed 상태 유지

V8의 배열 내부 구조

  • Packed elements: [1, 2, 3]
  • Holey elements: [1, , 3] → 성능 저하 (중간에 hole)

⚠️ 안 좋은 예

const arr = [1, 2, 3];
arr[100] = 4; // → Hole 발생 → 배열 전체가 Dictionary elements로 변경

✅ 팁

  • 배열은 항상 촘촘하게 사용
  • 유사 배열 자료구조로 사용하지 말 것 (sparse array 금지)
  • .push() 만 사용하면 보통 Packed 유지됨

5. 🔧 Native Syntax 활용 (--allow-natives-syntax)

성능 강제 최적화

%PrepareFunctionForOptimization(myFunc);
%OptimizeFunctionOnNextCall(myFunc);
myFunc(); // 최적화됨

디버깅 예

%GetOptimizationStatus(myFunc);
// 1 = optimized, 2 = not optimized, 4 = always optimized, etc.

💡 실전에서는 Node.js 실행 시 --allow-natives-syntax와 함께 사용

6. 📊 플래그 기반 분석

플래그설명
--trace-opt최적화된 함수 로그 출력
--trace-deoptDeopt 발생 시점 로그 출력
--print-bytecodeIgnition 바이트코드 보기
--trace-inlining함수 인라이닝 로그 출력

🚨 특히 Deopt 로그는 성능 저하의 “실시간 경고등” 역할

7. 🧪 성능 측정 방법 (마이크로벤치마크)

console.time("sum");
for (let i = 0; i < 1e6; i++) sum(1, 2);
console.timeEnd("sum");
  • 벤치마크는 반드시 워밍업 이후에 (V8이 최적화 전까지는 느림)
  • 최소 1,000번 이상 호출 후 측정해야 의미 있음

✨ 추가 보너스 팁

  • 함수는 "작고 단순하게" 하면 인라이닝 최적화가 잘 됨
  • try-catch는 성능에 안 좋은 영향을 줄 수 있음 (TurboFan이 잘 안 건드림)
  • arguments 사용하면 최적화 깨질 수 있음 → rest parameter (...args) 사용 권장

필요하면:

  • 실제 V8 로그 분석 예시
  • 특정 함수 최적화 여부 확인
  • 디컴파일 수준에서 바이트코드/머신코드 비교
profile
So that my future self will not be ashamed of myself.

0개의 댓글