お任せください!V8エンジンが裏で**「うわぁ、勘弁してくれよ…(最適化解除)」**と泣きながら処理速度を落としてしまう、代表的な「V8に嫌われるコード」の例を3つ紹介します。
これらはエラーにはなりませんが、「爆速モード(TurboFan)」から「通常モード(または低速モード)」に叩き落とされる書き方です。
1. 配列に「穴」を空ける(Holey Arrays)
V8は「配列=メモリがきれいに並んでいる」と信じて最適化します。しかし、途中に穴(empty / undefined)を作ると、その信頼が崩壊します。
😢 V8が嫌がるコード:
const arr = [1, 2, 3];
// いきなり遠くのインデックスに代入する
arr[100] = 99;
// 結果: [1, 2, 3, empty × 97, 99]
なぜ遅くなる?
- V8の言い分: 「全部詰まっていれば単純な計算 ($O(1)$) で場所がわかるのに、穴があるせいで『プロトタイプチェーンまで遡って値がないか確認する』という面倒な手順が必要になったぞ!」
-
対策: 配列は詰めて使うか、最初からサイズが決まっているなら
new Array(n).fill(0)等で埋めておきましょう。
2. オブジェクトのプロパティ順序を変える(Hidden Classesの分裂)
V8は、プロパティを追加した**「順序」**を覚えています。同じプロパティを持っていても、追加した順番が違うと「別の設計図(Hidden Class)で作られた別物」とみなされます。
😢 V8が嫌がるコード:
interface Point { x: number, y: number }
// パターンA
const p1 = {} as Point;
p1.x = 1;
p1.y = 2; // 順序: x -> y
// パターンB
const p2 = {} as Point;
p2.y = 2; // 順序: y -> x (!?)
p2.x = 1;
// ここで関数に渡すと…
function processPoint(p: Point) { /* ... */ }
processPoint(p1); // V8「設計図Aだな、よし最適化!」
processPoint(p2); // V8「えっ、こっちは設計図B? 違う型が来たから最適化やり直しだ…」
なぜ遅くなる?
-
V8の言い分: 「中身は同じ
x, yだけど、メモリ上のレイアウトが違うから、同じ高速化コードが使えない! 両方に対応できるようにチェック処理を挟まないといけない…(多態性による低速化)」 -
対策: プロパティは常に同じ順序で初期化しましょう。クラス(
class)のコンストラクタを使って一括で定義するのが一番安全です。
3. 関数にいろんな型を渡しまくる(Polymorphism)
V8は関数が呼ばれたとき、「引数は前回と同じ型(例:整数)だな?」と予測して待ち構えています。これを「単相(Monomorphic)」といい、最高に速いです。
しかし、いろんな型を渡すとV8は混乱します。
😢 V8が嫌がるコード:
function add(a: any, b: any) {
return a + b;
}
add(1, 2); // V8「整数足し算用にコンパイル完了!爆速!」
add("a", "b"); // V8「えっ文字列? 文字列連結用に書き換えなきゃ…(少し遅くなる)」
add(obj1, obj2); // V8「オブジェクト!? もう無理、汎用的な激遅コードに戻すわ…」
なぜ遅くなる?
- V8の言い分: 「来るたびに型が違うんじゃ、専用の高速道路を用意できないよ! いちいち型チェックして分岐するから遅くなるぞ。」
-
対策: パフォーマンスが重要な関数(ゲームのループ処理など)では、引数の型を固定しましょう。TypeScriptを使っていれば自然と守れることが多いですが、
anyやUnion型を使いすぎると発生します。
まとめ:過剰に恐れる必要はないけど…
これらは「マイクロ最適化」の世界の話なので、普段のアプリ開発で神経質になりすぎる必要はありません。今のV8は、多少汚いコードでも十分速く動きます。
しかし、**「数万回のループ処理」や「リアルタイム描画」**をする際は、以下の「V8へのおもてなし」が効いてきます。
- 配列には穴を空けない。
- 同じ形のオブジェクトは、同じ順序で作る。
- 計算量が多い関数の引数は、型を統一する。
これを踏まえると、TypeScriptの「型定義」や「クラス」という仕組み自体が、人間だけでなくV8エンジンにとっても読みやすく、最適化しやすいコードを書く助けになっているというのが面白い点です。
次は、逆に**「V8がメモリをどうやって掃除しているか(ガベージコレクション)」**という話も面白いですが、興味ありますか? それともTypeScriptの実践的なテクニックに戻りますか?