---
title: Ginza.js #12 — V8の最適化を覗いてみよう！
tags: 
author: [Katsumi Kobayashi](https://docswell.com/user/katsumikobayashi)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/4EMY1RM6EW.jpg?width=480
description: Ginza.js #12 の発表のスライドです
published: October 21, 26
canonical: https://docswell.com/s/katsumikobayashi/Z8NEE1-20261019-ginzajs
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/4EMY1RM6EW.jpg)

Ginza.js #12 / 2026.10.19
V8の最適化を覗いてみよう！
綺麗なコードで⾼速化……！？
Katsumi Kobayashi


# Page. 2

![Page Image](https://bcdn.docswell.com/page/PER9LRVNJ9.jpg)

⼩林 克海
所属
株式会社プレイド
Core Platform(基盤)
新卒2年⽬
早稲⽥⼤学基幹理⼯学研究科表現⼯学専攻
橋⽥研究室(HCI)
修⼠2年
twitter: @takabayap
JS歴
12年⽬(中学1年⽣〜)


# Page. 3

![Page Image](https://bcdn.docswell.com/page/P7XQWRZ8EX.jpg)

JavaScript は⾃由だ‼


# Page. 4

![Page Image](https://bcdn.docswell.com/page/37K9VR8L7D.jpg)

こんな Array も書ける
JavaScript
const a = [1, 2, 3];
a.push(0.5);
a.push(&quot;hello&quot;);
a.push({ x: 1 });
異なる種類の値を混在できる


# Page. 5

![Page Image](https://bcdn.docswell.com/page/LJ3WM826J5.jpg)

こんな Array も書ける
JavaScript
const a = [1, 2, 3];
a.label = &quot;hello&quot;;
a[&quot;3&quot;] = 4;
a[10] = 5;
a.length; // 11
a[&quot;3&quot;] と a[3] は同じ
10番への代⼊で length は11


# Page. 6

![Page Image](https://bcdn.docswell.com/page/8JDK6MZMEG.jpg)

こんな Array も書ける
JavaScript
const a = [1, 2, 3];
a.label = &quot;hello&quot;;
a[&quot;3&quot;] = 4;
a[10] = 5;
a.length; // 11
console.log(JSON.stringify(a));
// [1,2,3,4,null,null,null,
null,null,null,5]
a[&quot;3&quot;] と a[3] は同じ
10番への代⼊で length は11


# Page. 7

![Page Image](https://bcdn.docswell.com/page/VEPKYRDQ78.jpg)

こんな Array も書ける
JavaScript
const a = [1, 2, 3];
a.label = &quot;hello&quot;;
a[&quot;3&quot;] = 4;
a[10] = 5;
でも速い！
a.length; // 11
console.log(JSON.stringify(a));
// [1,2,3,4,null,null,null,
null,null,null,5]
a[&quot;3&quot;] と a[3] は同じ
10番への代⼊で length は11


# Page. 8

![Page Image](https://bcdn.docswell.com/page/27VVGR1P7Q.jpg)

速度⽐較
n-body / 50,000,000 steps / 単⼀スレッド
相対速度 C = 1
CPU時間
C (gcc)
5.54 s
Go
9.64 s
Node.js
9.64‒9.99 s
Python 3
528.16 s
0
0.25
0.5
Benchmarks Game / n-body #8 / 速度 = CのCPU時間 ÷ 各⾔語のCPU時間
0.75
1


# Page. 9

![Page Image](https://bcdn.docswell.com/page/5JGL8M2Q7L.jpg)

速さを⽀える仕組み


# Page. 10

![Page Image](https://bcdn.docswell.com/page/47QY9RPWEP.jpg)



# Page. 11

![Page Image](https://bcdn.docswell.com/page/KE4WX8Y1J1.jpg)

実⾏環境と JavaScript エンジン
実⾏環境
JavaScript エンジン
Chrome
V8
Node.js
V8
Safari
JavaScriptCore
Bun
JavaScriptCore


# Page. 12

![Page Image](https://bcdn.docswell.com/page/L71YN265JG.jpg)

実⾏環境と JavaScript エンジン
実⾏環境
JavaScript エンジン
Chrome
V8
Node.js
V8
Safari
JavaScriptCore
こっちも速いよ！
Bun
JavaScriptCore


# Page. 13

![Page Image](https://bcdn.docswell.com/page/G7WG4RWWE2.jpg)

TypeScript
type Item = number | string | { x: number };
const a: Item[] = [1, 2, 3];
a.push(0.5);
a.push(&quot;hello&quot;);
a.push({ x: 1 });


# Page. 14

![Page Image](https://bcdn.docswell.com/page/4JZL2R52E3.jpg)

JavaScript
const a = [1, 2, 3];
a.push(0.5);
a.push(&quot;hello&quot;);
a.push({ x: 1 });
消えちゃう！


# Page. 15

![Page Image](https://bcdn.docswell.com/page/YE6WVRV6EV.jpg)

JavaScript
const a = [1, 2, 3];
V8 実⾏時に分類
a.push(0.5);
a.push(&quot;hello&quot;);
a.push({ x: 1 });


# Page. 16

![Page Image](https://bcdn.docswell.com/page/GE5MD8D5E4.jpg)

V8 の最適化
01
02
03
値と形を観測
規則性を記録
処理を特化


# Page. 17

![Page Image](https://bcdn.docswell.com/page/97298K8GJR.jpg)

Elements Kind
値の表現
SMI / DOUBLE / ELEMENTS
⽳の扱い
PACKED / HOLEY


# Page. 18

![Page Image](https://bcdn.docswell.com/page/DJY46N6G7M.jpg)

SMI: ⼩さい整数を格納する
JavaScript
const a = [1, 2, 3];
// PACKED_SMI_ELEMENTS
整数をそのまま格納する


# Page. 19

![Page Image](https://bcdn.docswell.com/page/V7NYZXZ8E8.jpg)

DOUBLE: 数値を倍精度で格納する
JavaScript
const a = [1, 2, 3];
a.push(0.5);
// PACKED_DOUBLE_ELEMENTS
64bit の数値を直接格納


# Page. 20

![Page Image](https://bcdn.docswell.com/page/YJ9PZ8ZX73.jpg)

汎⽤のELEMENTS: いろいろな値を持てる
JavaScript
const a = [1, 2, 3];
a.push(0.5);
a.push(&quot;hello&quot;);
// PACKED_ELEMENTS
値や保存先のアドレスを格納


# Page. 21

![Page Image](https://bcdn.docswell.com/page/GJ8D1819JD.jpg)

PACKEDとHOLEY: 途中に⽳があるか
JavaScript
const a = [1, 2, 3];
// PACKED_SMI_ELEMENTS
a[5] = 6;
// HOLEY_SMI_ELEMENTS
⽳を確認する処理が増える


# Page. 22

![Page Image](https://bcdn.docswell.com/page/LJLMQRQMER.jpg)

要素の変化に応じて Elements Kind が遷移する
⼩さい整数
倍精度の数値
汎⽤の値
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
⽳を作る
HOLEY_SMI_ELEMENTS
V8 Blog “Elements kinds in V8”


# Page. 23

![Page Image](https://bcdn.docswell.com/page/47MY1R167W.jpg)

内部表現による実⾏時間の差


# Page. 24

![Page Image](https://bcdn.docswell.com/page/P7R9LRLNE9.jpg)

65,536個の整数を⾜す時間を⽐べる
01
02
03
同じ整数列を⽤意
ウォームアップ
実⾏時間を計測
⼩数・⽂字列を⼀度代⼊
または要素を⼀度削除
計測前に同じ処理を反復
300ms間の反復を2セット
全要素を⾜す処理を反復
7プロセスで各7回測定
元の整数列に戻して⽐較
Node 24.19.0 / V8 13.6.233.17-node.51 / Linux x64 / EPYC 9V74 / CPU 0固定


# Page. 25

![Page Image](https://bcdn.docswell.com/page/PJXQWRW87X.jpg)

全要素を⾜す処理の実⾏時間
0〜127の同じ整数列を使⽤
PACKED_SMIを1とした時間⽐
中央値
PACKED_SMI
1.00
SMI + a.label
1.01
PACKED_DOUBLE
1.19
PACKED_ELEMENTS
1.21
HOLEY_SMI
1.20
0
0.5
⼩さいほど短時間
Node 24.19.0 / V8 13.6.233.17-node.51 / Linux x64 / EPYC 9V74 / CPU 0固定
1
1.5


# Page. 26

![Page Image](https://bcdn.docswell.com/page/3JK9VRVLJD.jpg)

全要素を⾜す処理の実⾏時間
0〜127の同じ整数列を使⽤
PACKED_SMIを1とした時間⽐
中央値
1.00
PACKED_SMI
SMI + a.label
1.01
PACKED_DOUBLE
1.19
PACKED_ELEMENTS
1.21
HOLEY_SMI（値は復元）
1.20
同じ内部型でもアクセス履歴は変わる
0
0.5
⼩さいほど短時間
Node 24.19.0 / V8 13.6.233.17-node.51 / Linux x64 / EPYC 9V74 / CPU 0固定
1
1.5


# Page. 27

![Page Image](https://bcdn.docswell.com/page/LE3WM8M6E5.jpg)

Arrayの範囲外に⼀度でもアクセスすると
そこから先は遅くなる……！？


# Page. 28

![Page Image](https://bcdn.docswell.com/page/8EDK6M6M7G.jpg)

https://v8.dev/blog/elements-kinds#avoid-reading-beyond-the-length-of-the-array


# Page. 29

![Page Image](https://bcdn.docswell.com/page/V7PKYRYQJ8.jpg)

JavaScript
for (let i = 0, item;
(item = items[i]) != null; i++) {
doSomething(item);
}
V8 Blog “Elements kinds in V8”（2017）の例
null / undeﬁned が出るまで読む
最後に範囲外も読んでしまう


# Page. 30

![Page Image](https://bcdn.docswell.com/page/2JVVGRGPJQ.jpg)

JavaScript
for (let i = 0, item;
(item = items[i]) != null; i++) {
doSomething(item);
}
null / undeﬁned が出るまで読む
最後に範囲外も読んでしまう
“jQuery uses this pattern in a few places.”
V8 Blog “Elements kinds in V8”（2017）の例


# Page. 31

![Page Image](https://bcdn.docswell.com/page/5EGL8M8QJL.jpg)

JavaScript
const elements =
document.getElementsByTagName(&quot;input&quot;);
elements.length;
// IEでは要素 他では数値
elements.namedItem(&quot;length&quot;);
//現代はこっちで要素が取れる
https://bugs.jquery.com/ticket/945/
&lt;input name=&quot;length&quot;&gt;
がある場合に昔のIEでバグったかららしい


# Page. 32

![Page Image](https://bcdn.docswell.com/page/4JQY9R9W7P.jpg)

範囲外アクセスを計測前に1回だけ⼊れる
01
02
03
範囲内で繰り返す
1回だけ条件を変える
範囲内へ戻して計測
全要素を同じ関数で処理
1秒間繰り返す
実験群：末尾の次まで読む
対照群：末尾まで読む
範囲内だけで1秒間反復
その後に実⾏時間を測る


# Page. 33

![Page Image](https://bcdn.docswell.com/page/K74WX8X1E1.jpg)

合計を求める処理と最⼤値を探す処理
合計
最⼤値
1+2+3=6
max(1, 2, 3) = 3
JavaScript
let sum = 0;
for (let i = 0; i &lt; end; i++) {
const value = a[i];
sum += value === undefined
? 0 : value;
}
return sum;
JavaScript
let max = -1;
for (let i = 0; i &lt; end; i++) {
const value = a[i];
if (value &gt; max) max = value;
}
return max;


# Page. 34

![Page Image](https://bcdn.docswell.com/page/LJ1YN2N5EG.jpg)

速度⽐較
同じ呼び出し箇所・同じ値
中央値
合計：範囲内だけ
1.00
合計：範囲外を1回
2.49
最⼤値：範囲内だけ
1.00
最⼤値：範囲外を1回
1.94
0
1
Node 24.19.0 / V8 13.6.233.17-node.51 / Linux x64 / EPYC 9V74 / CPU 0固定
2
3


# Page. 35

![Page Image](https://bcdn.docswell.com/page/GJWG4R4W72.jpg)

配列の範囲外でも prototype の値を読む
JavaScript
const a = [10, 20, 30];
const p = { 3: 99 };
Object.setPrototypeOf(
p, Array.prototype);
Object.setPrototypeOf(a, p);
a[a.length]; // 99
式
評価結果
a.length
3
a[a.length]
99


# Page. 36

![Page Image](https://bcdn.docswell.com/page/4EZL2R2273.jpg)

配列の分類は同じでも IC の情報は変わる
観測項⽬
範囲外を読む前
1回読んだ後
Elements Kind
PACKED_SMI
PACKED_SMI
IC の範囲外アクセス対応
なし
あり
IC = Inline Cache


# Page. 37

![Page Image](https://bcdn.docswell.com/page/Y76WV1667V.jpg)

Object の形と最適化


# Page. 38

![Page Image](https://bcdn.docswell.com/page/G75MD46574.jpg)

Hidden Class は Object の形を表す
JavaScript
同じ Hidden Class
const a = { x: 1, y: 2 };
const b = { x: 3, y: 4 };
Object
プロパティ
function readX(o) { return o.x; }
readX(a);
readX(b);
a
x, y
b
x, y


# Page. 39

![Page Image](https://bcdn.docswell.com/page/9J298VMGER.jpg)

違う形の Object も同じ関数に渡せる
JavaScript
const point = { x: 1, y: 2 };
const box = {
x: 3, width: 4, height: 5
};
function readX(o) { return o.x; }
readX(point);
readX(box);
異なる Hidden Class
Object
プロパティ
point
x, y
box
x, width, height


# Page. 40

![Page Image](https://bcdn.docswell.com/page/DEY463XGJM.jpg)

同じプロパティでも作成順序で形が変わる
JavaScript
異なる Hidden Class
const a = { x: 1, y: 2 };
const b = { y: 2, x: 1 };
Object
プロパティ
function readX(o) { return o.x; }
readX(a);
readX(b);
a
x, y
b
y, x


# Page. 41

![Page Image](https://bcdn.docswell.com/page/VJNYZV1878.jpg)

Hidden Class から x の位置が分かる
Hidden Class と x の格納位置
JavaScript
function readX(o) {
return o.x;
}
Hidden Class
プロパティの順序
x の位置
A
{ x, y }
1番⽬
B
{ y, x }
2番⽬


# Page. 42

![Page Image](https://bcdn.docswell.com/page/YE9PZ65XJ3.jpg)

同じo.xに異なる作り⽅のObjectを渡す
Hidden Class 1種
Hidden Class 2種
Hidden Class 5種
各順序を個別に計測
2順序を交互に渡す
キー構成を増やす
{ x, y } のみ
{ y, x } のみ
{ x, y } / { y, x }
{ x, y } / { x, z } など
5種類のキー構成
準備・ウォームアップの後に計測


# Page. 43

![Page Image](https://bcdn.docswell.com/page/GE8D1ZY9ED.jpg)

逆順⾃体は同程度
同じ値のo.xを合計
2順序を混ぜると約1割差
{x, y}だけを1とした時間⽐
中央値
{x, y}：Hidden Class 1種
1.00
{y, x}：Hidden Class 1種
1.00
2順序：Hidden Class 2種
1.12
5構成：Hidden Class 5種
3.49
0
1
2
Node 24.19.0 / V8 13.6.233.17-node.51 / Linux x64 / EPYC 9V74 / CPU 0固定
3
4


# Page. 44

![Page Image](https://bcdn.docswell.com/page/LELMQD4M7R.jpg)

JavaScript
Object.keys({ x: 1, y: 2 });
// [&quot;x&quot;, &quot;y&quot;]
Object.keys({ y: 2, x: 1 });
// [&quot;y&quot;, &quot;x&quot;]
通常の⽂字列キーは作成順


# Page. 45

![Page Image](https://bcdn.docswell.com/page/4JMY1PK6JW.jpg)

JavaScript
Object.keys({ x: 1, y: 2 });
// [&quot;x&quot;, &quot;y&quot;]
通常の⽂字列キーは作成順
Object.keys({ y: 2, x: 1 });
// [&quot;y&quot;, &quot;x&quot;]
→トランスパイル時に並びかえれば⾼速になるのでは？


# Page. 46

![Page Image](https://bcdn.docswell.com/page/PJR9L1XN79.jpg)

順序を変えると計算結果が変わる
JavaScript
const steps = {
add: n =&gt; n + 1,
double: n =&gt; n * 2,
};
Object.values(steps).reduce(
(n, step) =&gt; step(n), 1
); // 4
プロパティの順序
計算結果
add → double
4
double → add
3


# Page. 47

![Page Image](https://bcdn.docswell.com/page/PEXQWY68JX.jpg)

プロパティを並べ替えると評価順も変わる
JavaScript
const obj = {
y: trace(&quot;y&quot;),
x: trace(&quot;x&quot;),
};
評価順は y → x
並べ替えると副作⽤の順も変わる


# Page. 48

![Page Image](https://bcdn.docswell.com/page/3EK9V31LED.jpg)

まとめ
● JavaScript は⾃由な⾔語
● V8 は様々な⼯夫で⾼速に実⾏する
● 仕様と互換性の制約があり、書き⼿が⼯夫できる余地もまだある


# Page. 49

![Page Image](https://bcdn.docswell.com/page/L73WMGQ675.jpg)

綺麗なコードで速くなる……かもしれない！


