JavaScriptのfor文、いろいろ記事が出てますがいまいちどれが速いのかよく分かりません。
そこで、自分の環境で比較してみたらすごく意外な結果になったので公開してみます。
何か間違っていたら教えてください。
追記 2019/09/12
@noxi515 さんが新しく検証してます(Firefoxのfor速度比較が遅すぎると思ったので試してみた(2019/05版))ので、より新しい情報はそちらをご参照ください。
間違ってました
追記 2018/02/04
検証してくださった方がいらっしゃいました。firefoxではグローバル変数に触ると急激に速度が落ちるみたいです。モジュールで切ればこの現象は起きませんでした。
私の環境でも、手っ取り早く
{
const arr = new Array(10000000).fill(0).map((v, i) => i);
let sum = 0, i = 0;
const len = arr.length;
for(; i < len; i += 1) {
sum += arr[i]
}
console.log(sum);
}
を書いたら爆速で実行されました。今まで普段はWebpackに投げてたけどこの記事ではグローバルに置いてしまっていたから全く意識してなかった……。
最近firefoxにブラウザを戻したので引き続き愛用していきたいと思います。
@noxi515さん、ありがとうございました。
TL;DR
Chromeではfor
最速。for
≒for of
≒reduce
<forEach
<<<map
firefoxではreduce
最速。reduce
<<<foreach
<map
<<for of
<for
reduce
が可読性でも速度面でも強い外側で宣言したfunction式でもアロー関数ベタ書きでもオブジェクト再生成がなければ速度にはほぼ影響しない
Motivation
for、結局どれで書いたら速いのかいろいろ意見が出ててわからなかったので、今の暫定を決めておきたくテストしてみました。
ブラウザは今手元にあるChromeとfirefoxで動かしてみました。
先人にのっとって、1000万個の連番配列の中身をひたすら足し合わせます。
比較するもの
for
一番普通の for
文です。いかにも速そう。
安全性のため、var
は使わず const
と let
を使用します。
for of
今流行りの for of
です。イテレーターが実装されてれば回せる子ですね。
for in
は配列のプロパティとかに代入しちゃってるとそれまで列挙されてしまいそもそも役割が違うので今回は除外しました。
forEach
1つずつ要素を取ってきて。引数のコールバックに代入して処理します。
for
よりは結構遅そうなイメージ。
map
ES2015の申し子、 map
。
return
で値返せるところがメリットですが、返り値の不要なループの場合はどのぐらいオーバーヘッドが出てしまうのでしょうか。forEach
よりはさらに遅そうです。
reduce
map
の兄弟ポジションの reduce
。配列の値を1つずつ取ってきて何かする、みたいな処理を書くときにすごく楽ですが、コールバックを取るのでちゃんと速度が出るのか不安です。
ソース
5回やって平均を取ります。
コールバック取る系のものは、function
式かarrowベタ書きかでどのぐらい変わるかも比較してみました。
// 初期化
const arr = new Array(10000000).fill(0).map((v, i) => i);
let sum = 0 | 0;
// #1 for
const len = arr.length | 0;
for (let j = 0; j < 5; ++j) {
sum = 0 | 0;
console.time('for');
for (let i = 0 | 0; i < len; i += 1) {
sum += arr[i];
}
console.timeEnd('for');
console.log(sum);
}
// #2 for of
for (let i = 0; i < 5; ++i) {
sum = 0 | 0;
console.time('for of');
for (const v of arr) {
sum += v;
}
console.timeEnd('for of');
console.log(sum);
}
// #3-1 forEach(arrow)
for (let i = 0; i < 5; ++i) {
sum = 0 | 0;
console.time('forEach(arrow)');
arr.forEach(v => {
sum += v;
});
console.timeEnd('forEach(arrow)');
console.log(sum);
}
// #3-2 forEach(pre-defined function)
function addSum(v) {
sum += v;
}
for (let i = 0; i < 5; ++i) {
sum = 0 | 0;
console.time('forEach(function)');
arr.forEach(addSum);
console.timeEnd('forEach(function)');
console.log(sum);
}
// #4-1 map(arrow)
for (let i = 0; i < 5; ++i) {
sum = 0 | 0;
console.time('map(arrow)');
arr.map(v => sum += v);
console.timeEnd('map(arrow)');
console.log(sum);
}
// #4-2 map(pre-defined function)
for (let i = 0; i < 5; ++i) {
sum = 0 | 0;
console.time('map(function)');
arr.map(addSum);
console.timeEnd('map(function)');
console.log(sum);
}
// #5-1 reduce(arrow)
for (let i = 0; i < 5; ++i) {
sum = 0 | 0;
console.time('reduce(arrow)');
sum = arr.reduce((sum, v) => sum + v, 0 | 0);
console.timeEnd('reduce(arrow)');
console.log(sum);
}
// #5-2 reduce(pre-defined function)
function reduceSum(sum, v) {
return sum + v;
}
for (let i = 0; i < 5; ++i) {
sum = 0 | 0;
console.time('reduce(function)');
sum = arr.reduce(reduceSum);
console.timeEnd('reduce(function)');
console.log(sum);
}
実行環境
- Windows 10 Home (64bit)
- Intel Core i7 870 @ 2.93GHz
- 12GB RAM
- Chrome 64.0.3282.140
- firefox 58.0.1
結果
平均 | Chrome | Firefox |
---|---|---|
for |
249.5 | 8311.7 |
for of |
272.7 | 4502.6 |
forEach (arrow) |
424.5 | 2358.3 |
forEach (function) |
424.0 | 2338.1 |
map (arrow) |
3118.2 | 2729.0 |
map (function) |
3251.3 | 2705.3 |
reduce (arrow) |
270.3 | 41.4 |
reduce (function) |
272.9 | 42.0 |
えぇ……
Chromeでは予想通り for
が最速ですね。 for of
や reduce
もほぼ誤差の範囲内なので、可読性を考えると for of
や reduce
を使うのが得策のように思えます。
反して、firefoxではなんと reduce
(arrow) **が最速という結果に。**そんなバカな。
というか for
一番遅いじゃん……。
どうしてこうなった
いや、さすがに reduce
の方が速いとか普通に考えてありえないので、さすがに何か間違ってると思います。
書き方とかにコツがある可能性がありますが、今の私の技術力だとちょっとわかりません。
こんな結果出されると、console.time
が信用できないです。
ソースとか読めば何か分かるのかも。
結論
reduce
や for of
は可読性と速度を両立してるのでこれからは積極的に使っていきましょう。
console.time
信用できない。