JavaScript には、整数を添え字として指定し、それと関連付けた値を読み書きできる機能がいくつもある。
具体的には、配列・型付き配列・Map・Objectなどがある。
しかし、この中でどれがどのくらい速いのだろうか?
というわけで、実験を行ってみた。
今回の実験
JSBench.me - JavaScript performance benchmarking playground
を用いて、それぞれのデータ構造に適当にアクセスするプログラムを実行し、処理速度を比較する。
具体的には、以下のプログラムを用意した。
型付き配列の実験は、代表として Int32Array で行った。
Array vs Int32Array vs Map (version 1) - JavaScript benchmark at JSBench.me
const nelem = 10000;
const niter = 100000;
const array = [];
for (let i = 0; i < nelem; i++) array[i] = i;
for (let i = 0; i < niter; i++) {
const a = (i * 591364) % nelem;
const b = (i * 195872) % nelem;
const t = array[a];
array[a] = array[b];
array[b] = t;
}
const array = new Int32Array(nelem);
for (let i = 0; i < nelem; i++) array[i] = i;
for (let i = 0; i < niter; i++) {
const a = (i * 591364) % nelem;
const b = (i * 195872) % nelem;
const t = array[a];
array[a] = array[b];
array[b] = t;
}
const array = new Map();
for (let i = 0; i < nelem; i++) array.set(i, i);
for (let i = 0; i < niter; i++) {
const a = (i * 591364) % nelem;
const b = (i * 195872) % nelem;
const t = array.get(a);
array.set(a, array.get(b));
array.set(b, t);
}
const array = {};
for (let i = 0; i < nelem; i++) array[i] = i;
for (let i = 0; i < niter; i++) {
const a = (i * 591364) % nelem;
const b = (i * 195872) % nelem;
const t = array[a];
array[a] = array[b];
array[b] = t;
}
初期設定で、扱う要素数 nelem および入れ替え処理の回数 niter を定義する。
そして、各データ構造について、以下の処理を行う。
- 連番を格納する
- 適当な要素を選び、それらの値を入れ替える
適当な要素を選ぶのに Math.random() を用いてしまうと、この中身によって性能に影響が出る懸念があるため、今回は適当な値を掛けて要素数で割った余りを取ることで、適当な要素を選ぶことにした。
今回の処理は、「データ構造に適当な回数アクセスする」ことを目的としており、要素のシャッフルを目的としたものではない。
実験結果
手元の Windows 11 環境で、Firefox 154.0 および Google Chrome 151.0.7922.174 でそれぞれ 1 回測定を行ったところ、以下の結果が得られた。
| データ構造 | Firefox | Google Chrome |
|---|---|---|
| 配列 | 830 ops/s ± 0.94% | 190 ops/s ± 2.22% |
| 型付き配列 | 913 ops/s ± 0.6% | 189 ops/s ± 5.36% |
| Map | 177 ops/s ± 0.52% | 88 ops/s ± 0.84% |
| Object | 815 ops/s ± 1.46% | 188 ops/s ± 9.81% |
Firefox では、型付き配列のパフォーマンスが若干良く、配列と Object のパフォーマンスが同じくらいで、Map のパフォーマンスが圧倒的に悪かった。
Google Chrome では、Map のパフォーマンスが Firefox ほどの差ではないが悪く、他のパフォーマンスは同じくらいだった。
また、Google Chrome の Map 以外のパフォーマンスは、Firefox の Map 程度しか出なかった。
おわりに
Map は近代的な連想配列でパフォーマンスに優れていると思い込んでいたので、試してみると Object よりもパフォーマンスが出ないというのは意外だった。
また、型付き配列は今回扱った他のデータ構造と比べて要素数の拡大がしにくい。
その分配列よりもパフォーマンスが良くなると思い込んでみたが、試してみるとあまり変わらないこともあるようである。
とはいえ、これはあくまで今回の実験の結果である。
データやクエリの数を変えてみたり、行う処理の内容を変えてみたり (たとえば、不連続な添字や要素の削除を扱う) すると、結果は変わってくるかもしれない。
