🤔

benchmark.jsを使用したディープコピー比較検証

2023/02/05に公開約3,200字

JavaScriptのディープコピー方法はいくつかありますが、パフォーマンスを考えるとどれがいいのか考えものです。

そこでJavaScriptライブラリであるbenchmark.jsを使用してパフォーマンス検証していこうと思います。
https://www.npmjs.com/package/benchmark

検証するデータ

今回は以下のオブジェクトで検証を実施します。

const data = {
  key1: 'value1',
  key2: 'value2',
}

JSON.stringify()

JSON.stringifyでJSON文字列に変換し、JSON.parseで文字列によって記述されているJavaScript の値やオブジェクトを構築します。

const deepcopy = JSON.stringify(data)
data.key1 = 'update'
console.log(data) // { key1: 'update', key2: 'value2' }
console.log(JSON.parse(deepcopy)) // { key1: 'value1', key2: 'value2' }

structuredClone()

node v17から使用できます。

const deepcopy = structuredClone(data)
data.key1 = 'update'
console.log(data) // { key1: 'update', key2: 'value2' }
console.log(deepcopy) // { key1: 'value1', key2: 'value2' }

Lodash clonedeep()

ライブラリLodashから使用できます。
こちらはJSON.stringify()とは違って値に制限がありません。
https://lodash.com/docs/4.17.15#cloneDeep

const clonedeep = require('lodash/cloneDeep')

const deepcopy = clonedeep(data)
data.key1 = 'update'
console.log(data) // { key1: 'update', key2: 'value2' }
console.log(deepcopy) // { key1: 'value1', key2: 'value2' }

検証結果

それぞれの検証を行なっていきます。
検証コードは以下の通りです。

functions/index.js
const clonedeep = require('lodash/cloneDeep')

const deepJson = (originData) => {
  let data = null
  for (let i = 0; i < 10000; i++) {
    data = JSON.stringify(originData)
  }
  return JSON.parse(data)
}

const deepStructure = (originData) => {
  let data = null
  for (let i = 0; i < 10000; i++) {
    data = structuredClone(originData)
  }
  return data
}

const deepLodash = (originData) => {
  let data = null
  for (let i = 0; i < 10000; i++) {
    data = clonedeep(originData)
  }
  return data
}

module.exports =  {
  deepJson,
  deepStructure,
  deepLodash
}
main.js
const Benchmark = require('benchmark')

const { deepJson, deepStructure, deepLodash } = require('./functions')

const originData = {
  key1: 'value1',
  key2: 'value2',
}

const suite = new Benchmark.Suite

suite
  .on('start', () => {
    console.log('Test start')
  })
  .add('JSON', () => {
    deepJson(originData)
  })
  .add('Structure', () => {
    deepStructure(originData)
  })
  .add('Lodash', () => {
    deepLodash(originData)
  })
  .on('cycle', (event) => {
    console.log(String(event.target))
  })
  .on('complete', function () {
    console.log(`Fastest is ${this.filter('fastest').map('name')}`)
  })
  .run({ async: true }) 

結果は以下のようになりました。
1秒でどれくらい実行できたかを表すops/secでは、JSON.stringify()を使用した方法が一番早いです。

Test start
JSON x 647 ops/sec ±0.12% (98 runs sampled)
Structure x 143 ops/sec ±0.60% (83 runs sampled)
Lodash x 479 ops/sec ±1.53% (91 runs sampled)
Fastest is JSON

最後に

一番処理が早かったJSON.stringify()ですが、データの型に注意する必要があります。

データの型に影響を受けないようにするならばLodash clonedeepを候補に入れてもいいかと考えます。また、対応ブラウザに気をつけてstructureClone()を使用するのも一つの手かと思います。

Discussion

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