✏️

どれが早いの?Benchmark.js使ってみようよ。

2 min read 4

JavaScriptを使用していると、実現したい方法がいく通りが存在します。
少ない処理はあまり変わらないですが、多くのデータを使用するとなればパフォーマンスに大きく影響してきます。

そのパフォーマンスを確かめるためにBenchmark.jsを使用してみます。

ドキュメントはこちらから。

インストール

今回はディープコピーについて、3つの方法を比べてみましょう。

まずは必要パッケージをインストールします。

npm install benchmark lodash

コード

nodeを使用して実行します。
この例ではオブジェクト配列をコピーする前提です。

index.js
const Benchmark = require('benchmark')
const clonedeep = require('lodash/cloneDeep')
const suite = new Benchmark.Suite

const samples = [
  {
    name: 'test',
    age: 26,
    gender: 'male'
  },
  {
    name: 'suite',
    age: 30,
    gender: 'female'
  },
]

suite
  .add('json', {
    fn: () => {
        const jsonDeep = JSON.parse(JSON.stringify(samples))
    }
  })
  .add('map', {
    fn: () => {
        const mapDeep = samples.map(sample => ({...sample}) )
    }
  })
  .add('lodash', {
    fn: () => {
        const loDeep = clonedeep(samples)
    }
  })
  .on('cycle', (event) => {
    console.log(String(event.target))
  })
  .on('complete', () => {
    console.log(`Fastest is ${suite.filter('fastest').map('name')}`)
  })
  .run({ async: true })

addメソッドでテストを追加します。
cycleイベントで詳細を出力し、completeイベントでテストの最速を表示するようにしています。

では結果はどうなんでしょうか。

json x 339,283 ops/sec ±4.38% (83 runs sampled)
map x 15,391,325 ops/sec ±3.14% (79 runs sampled)
lodash x 532,430 ops/sec ±2.88% (78 runs sampled)
Fastest is map

mapを使用した方法が一番早いですね。
といった具合に比較して結果を出力してくれます。

ちなみに

単なるオブジェクトのコピーだったらmapは使用できません。
lodashとJSONを使用した方法の比較を載せておきます。

index.js
const obj = {
  name: 'Tom',
  male: 'male',
  family: {
    sister: 'Henry',
    father: 'Lee',
    mother: 'Mary'
  }
}

suite
  .add('json', {
      fn: () => {
          const jsonDeep = JSON.parse(JSON.stringify(samples))
      }
    })
    .add('lodash', {
      fn: () => {
          const loDeep = clonedeep(samples)
      }
    })
  ...
json x 332,315 ops/sec ±8.37% (78 runs sampled)
lodash x 673,675 ops/sec ±1.46% (88 runs sampled)
Fastest is lodash

lodashの勝利。

Discussion

ループ数を決めるのはBenchmark.js側に任せること出来ないんですか?
pythonのtimeitモジュールではお任せでも適当にいい感じに決めてくれるので気になりました

コメントいただき、ありがとうございます。
ドキュメントを見直したところ、minSamplesプロパティで設定できるのではないかと思います。

minSamplesのデフォルトは5です。

.add('json', {
    fn: () => {
      const jsonDeep = JSON.parse(JSON.stringify(samples))
    },
   minSamples: 100
  })

また、for文で回すと誤差が20%近くも出ているので、for文を使用せずデフォルトで測定するように記事修正いたしました。

詳しくは冒頭のドキュメントを参考にしていただけると幸いです(私が把握しきれておらず申し訳ありません...)

これ思い出しました。

JavaScriptのディープコピー速さ比較 〜7つの手法/ライブラリを比べてみた〜 - Qiita

https://qiita.com/suin/items/80e687dd1789b9d9d2fd

JSONは、日付型がグダグダになるのでcloneDeepとして使うのはちょっと怖いです。

コメント、ありがとうございます!
suinさんの記事ですね、こちらの方がより検証されていますね。
たしかにJSONは日付型に対応していませんから、なるべく別の手段が好ましいですね。。

ログインするとコメントできます