247 lines
5.5 KiB
Markdown
247 lines
5.5 KiB
Markdown
---
|
|
id: perf-v8-optimization
|
|
title: V8 Optimization — Hidden class / Inline cache / Hot path
|
|
category: Coding
|
|
status: draft
|
|
source_trust_level: B
|
|
verification_status: conceptual
|
|
created_at: 2026-05-09
|
|
updated_at: 2026-05-09
|
|
tags: [performance, v8, javascript, vibe-coding]
|
|
tech_stack: { language: "JS / TS", applicable_to: ["Backend", "Frontend"] }
|
|
applied_in: []
|
|
aliases: [V8, hidden class, monomorphic, inline cache, deopt, Sparkplug, TurboFan]
|
|
---
|
|
|
|
# V8 Optimization
|
|
|
|
> JIT 친화 코드 = 100x 빠름. **Monomorphic > polymorphic > megamorphic**. Hidden class 안정 + 같은 type 만 + tight loop. 보통 알고리즘 < V8 hint.
|
|
|
|
## 📖 핵심 개념
|
|
- Hidden class (Map): object shape — V8 가 cache.
|
|
- IC (Inline Cache): 같은 site 같은 type → fast.
|
|
- Monomorphic / Polymorphic / Megamorphic.
|
|
- Deopt: 다른 type 출현 → JIT 코드 폐기.
|
|
|
|
## 💻 코드 패턴
|
|
|
|
### Hidden class 안정
|
|
```ts
|
|
// ❌ 다른 순서 / 다른 필드 = 다른 hidden class
|
|
const a = { x: 1 };
|
|
a.y = 2;
|
|
const b = { y: 2 };
|
|
b.x = 1;
|
|
// a, b 가 다른 hidden class
|
|
|
|
// ✅
|
|
const a = { x: 1, y: 2 };
|
|
const b = { x: 3, y: 4 };
|
|
// 같은 hidden class
|
|
```
|
|
|
|
→ Object literal 시 모든 필드 한 번에. 추가 X.
|
|
|
|
### Monomorphic 함수
|
|
```ts
|
|
// ❌ 다양한 type
|
|
function getX(obj) { return obj.x; }
|
|
getX({ x: 1 }); // shape A
|
|
getX({ x: 1, y: 2 }); // shape B
|
|
getX({ x: 1, y: 2, z: 3 }); // shape C
|
|
// → polymorphic / megamorphic — 느림
|
|
|
|
// ✅ 같은 shape 만
|
|
class Point { constructor(x, y) { this.x = x; this.y = y; } }
|
|
const points = [new Point(1, 2), new Point(3, 4)];
|
|
points.forEach(p => p.x); // monomorphic
|
|
```
|
|
|
|
### Class > 동적 object
|
|
```ts
|
|
// ✅ 빠름
|
|
class User {
|
|
constructor(public id: string, public email: string) {}
|
|
}
|
|
|
|
// ⚠️ 느릴 수 있음
|
|
const user = { id, email, age, ...rest };
|
|
```
|
|
|
|
→ Class 가 hidden class 강제.
|
|
|
|
### Tight loop
|
|
```ts
|
|
// ✅
|
|
let sum = 0;
|
|
for (let i = 0; i < arr.length; i++) {
|
|
sum += arr[i];
|
|
}
|
|
|
|
// ⚠️ functional — V8 가 잘 최적화 하지만 일부 case 느림
|
|
const sum = arr.reduce((s, x) => s + x, 0);
|
|
```
|
|
|
|
→ Hot path = 측정 후 결정.
|
|
|
|
### Avoid deopt triggers
|
|
```ts
|
|
// ❌ try-catch 안 hot loop (V8 옛 — 새 V8 는 OK)
|
|
function hot() {
|
|
try {
|
|
for (...) inner();
|
|
} catch (e) { ... }
|
|
}
|
|
|
|
// 새 V8 는 try-catch 거의 무 비용.
|
|
|
|
// ❌ delete (hidden class 변경)
|
|
delete obj.x;
|
|
|
|
// ❌ undefined / null mix
|
|
function add(a, b) { return a + b; }
|
|
add(1, 2);
|
|
add('x', 'y'); // string concat — deopt
|
|
```
|
|
|
|
### TypedArray (대량 숫자)
|
|
```ts
|
|
// ✅
|
|
const arr = new Float32Array(1_000_000);
|
|
arr[0] = 1.5;
|
|
// 빠름, 작은 메모리, GC 압력 적음
|
|
|
|
// ❌ 일반 array
|
|
const arr = new Array(1_000_000).fill(0);
|
|
```
|
|
|
|
### String concatenation
|
|
```ts
|
|
// ✅ 큰 string
|
|
const parts: string[] = [];
|
|
for (...) parts.push(line);
|
|
const result = parts.join('\n');
|
|
|
|
// ❌
|
|
let result = '';
|
|
for (...) result += line;
|
|
// V8 가 어느 정도 최적화 하지만 array.join 가 안전.
|
|
|
|
// ✅ 작은 string
|
|
const s = `${a} - ${b}`; // 일반 OK
|
|
```
|
|
|
|
### Object 재사용 (allocation 줄이기)
|
|
```ts
|
|
// ❌ 매 iteration object 새로
|
|
function process(items) {
|
|
for (const it of items) {
|
|
const tmp = { x: it.x, y: it.y };
|
|
use(tmp);
|
|
}
|
|
}
|
|
|
|
// ✅
|
|
const tmp = { x: 0, y: 0 };
|
|
function process(items) {
|
|
for (const it of items) {
|
|
tmp.x = it.x;
|
|
tmp.y = it.y;
|
|
use(tmp);
|
|
}
|
|
}
|
|
```
|
|
|
|
⚠️ Mutation 주의 — 라이브러리에 안 맞음.
|
|
|
|
### Inline 작은 함수
|
|
```ts
|
|
// V8 가 작은 함수 자동 inline. 너가 따로 inline 할 필요 X.
|
|
// 단 monomorphic 강한 경우.
|
|
```
|
|
|
|
### Map vs Object (key set 자주 변경)
|
|
```ts
|
|
// ✅ Map — 동적 key 안정
|
|
const m = new Map();
|
|
m.set('a', 1); m.set('b', 2); m.delete('a');
|
|
|
|
// ❌ object — delete 가 hidden class 변경
|
|
const o = {};
|
|
o.a = 1; delete o.a;
|
|
```
|
|
|
|
### --trace-deopt (debug)
|
|
```bash
|
|
node --trace-deopt app.js
|
|
# 어느 함수가 deopt 됐는지
|
|
```
|
|
|
|
```bash
|
|
node --trace-opt app.js
|
|
# Optimize / 다시
|
|
```
|
|
|
|
### Hot function 측정
|
|
```ts
|
|
const COUNT = 1_000_000;
|
|
const arr = Array.from({ length: COUNT }, (_, i) => i);
|
|
|
|
const t = performance.now();
|
|
for (let i = 0; i < arr.length; i++) hotFn(arr[i]);
|
|
console.log('avg:', (performance.now() - t) / COUNT, 'ms');
|
|
```
|
|
|
|
### V8 inspector profiling
|
|
```bash
|
|
node --prof app.js
|
|
node --prof-process isolate-*.log > profile.txt
|
|
```
|
|
|
|
또는 위 (Node profiling) 의 0x / clinic.
|
|
|
|
### Algorithm > Micro-opt
|
|
```
|
|
Big O 가 99% 의 답.
|
|
O(n²) → O(n log n) = 1000x 빠름.
|
|
Monomorphic vs polymorphic = 2-10x.
|
|
```
|
|
|
|
→ 알고리즘 먼저, micro-opt 마지막.
|
|
|
|
### Bun / Deno
|
|
```
|
|
Bun = JSC (Safari engine).
|
|
Deno = V8 (Chrome engine).
|
|
|
|
코드 호환 — engine 별 미세 차이.
|
|
```
|
|
|
|
## 🤔 의사결정 기준
|
|
| 작업 | 우선순위 |
|
|
|---|---|
|
|
| Big O | 1순위 |
|
|
| Monomorphic / class | 2순위 |
|
|
| TypedArray (숫자) | hot loop |
|
|
| Allocation 줄이기 | very hot path |
|
|
| try-catch / delete | 이젠 무 의미 (V8 modern) |
|
|
| Inline / unroll | 측정 후 |
|
|
|
|
## ❌ 안티패턴
|
|
- **Algorithm 무시 + V8 trick**: O(n²) 가 V8 trick 으로 안 됨.
|
|
- **모든 곳 micro-opt**: 가독성 잃음. hot path 만.
|
|
- **Object shape 매번 다름**: deopt.
|
|
- **Ah-hoc benchmark (1번 측정)**: warmup 필요. 평균.
|
|
- **매 turn V8 update 따라가기**: 너무 unstable. 알고리즘 우선.
|
|
- **Premature optimization**: 측정 X 추측.
|
|
|
|
## 🤖 LLM 활용 힌트
|
|
- Big O 우선. micro-opt 는 hot path 측정 후.
|
|
- Monomorphic class > 동적 object.
|
|
- TypedArray = 숫자 큰 array.
|
|
|
|
## 🔗 관련 문서
|
|
- [[Perf_Node_Profiling]]
|
|
- [[Perf_Web_Memory_Leak]]
|
|
- [[Perf_React_Reconciler]]
|