JavaScriptのfor速度比較が意外な結果だったのでシェアしてみる

JavaScriptのfor文、いろいろ記事が出てますがいまいちどれが速いのかよく分かりません。
そこで、自分の環境で比較してみたらすごく意外な結果になったので公開してみます。
何か間違っていたら教えてください。

追記 2019/09/12
@noxi515 さんが新しく検証してます([Firefoxのfor速度比較が遅すぎると思ったので試してみた（2019/05版）](https://qiita.com/noxi515/items/e2813a4be0747013a88c))ので、より新しい情報はそちらをご参照ください。

# 間違ってました

追記 2018/02/04
[検証してくださった方](https://qiita.com/noxi515/items/12abcd18007a107f2bda)がいらっしゃいました。firefoxでは**グローバル変数に触ると急激に速度が落ちる**みたいです。モジュールで切ればこの現象は起きませんでした。

私の環境でも、手っ取り早く

```モジュールを切った.js
{
  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](https://qiita.com/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で動かしてみました。
[先人](https://qiita.com/keroxp/items/67804391a8d65eb32cb8)にのっとって、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ベタ書きかでどのぐらい変わるかも比較してみました。

```source.js
// 初期化
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` **信用できない。**