javascript 配列のシャローコピー処理で「スプレッド構文」と「Array.from」と「concat」と「slice」のパフォーマンスを計測する

javascript 配列のシャローコピー処理で「スプレッド構文」と「Array.from」と「concat」と「slice」のパフォーマンスを計測する

javascriptで、配列のシャローコピー処理で「スプレッド構文」と「Array.from」と「concat」と「slice」で同じ処理を行った時のパフォーマンスを計測するサンプルコードを記述してます。「slice」を使用するのが一番良さそうです。ここでは「chrome」と「firefox」と「safari」の3つブラウザで結果を掲載してます。

環境

  • OS windows11 pro 64bit
  • Apache 2.4.43
  • ブラウザ chrome 110.0.5481.78

パフォーマンス計測

「performance.now」を使用して、「スプレッド構文」と「Array.from」と「concat」と「slice」をそれぞれ使用して、配列のシャローコピー処理を10万回実行し、パフォーマンスを計測するサンプルコードとなります。

<script>

  // 実行回数
  const times = 100_000;

  // 空白を埋めるだけの関数
  function spacePadding(val, n = 8) {
    for (; val.length < n; val += ' ');
    return val;
  }

  // 計測結果を表示
  const benchmark = (name, start, end) => {
    let report = (end - start).toPrecision(3);
    // 表示を見やすくするため関数名に空白を埋める
    name = spacePadding(name)
    console.log(`実行回数:${times}回 関数名:${name} 実行時間:${report}(ms)`);
  }

  // 配列
  let arr = [...Array(100)].map( (x, i) => i + 1 );
  let new_arr;

  // 計測
  start = performance.now();

  for (let i = 0; i < times; ++i) {
    new_arr = [...arr];
  }

  end = performance.now();

  benchmark('スプレッド構文', start, end);

  // 計測
  start = performance.now();

  for (let i = 0; i < times; ++i) {
    new_arr = Array.from(arr);
  }

  end = performance.now();

  benchmark('Array.from', start, end);

  // 計測
  start = performance.now();

  for (let i = 0; i < times; ++i) {
    new_arr = [].concat(arr);
  }

  end = performance.now();

  benchmark('concat', start, end);

  // 計測
  start = performance.now();

  for (let i = 0; i < times; ++i) {
    new_arr = arr.slice();
  }

  end = performance.now();

  benchmark('slice', start, end);

</script>

実行結果(chrome 109.0.5414.75)

<1回目>
実行回数:100000回 関数名:スプレッド構文  実行時間:370(ms)
実行回数:100000回 関数名:Array.from 実行時間:229(ms)
実行回数:100000回 関数名:concat   実行時間:4.50(ms)
実行回数:100000回 関数名:slice    実行時間:4.60(ms)

<2回目>
実行回数:100000回 関数名:スプレッド構文  実行時間:378(ms)
実行回数:100000回 関数名:Array.from 実行時間:227(ms)
実行回数:100000回 関数名:concat   実行時間:6.80(ms)
実行回数:100000回 関数名:slice    実行時間:5.40(ms)

<3回目>
実行回数:100000回 関数名:スプレッド構文  実行時間:367(ms)
実行回数:100000回 関数名:Array.from 実行時間:225(ms)
実行回数:100000回 関数名:concat   実行時間:9.20(ms)
実行回数:100000回 関数名:slice    実行時間:8.60(ms)

パフォーマンスは、「slice」が一番良さそうです。

firefox109も、「slice」が一番良さそうです。

<1回目>
実行回数:100000回 関数名:スプレッド構文  実行時間:186(ms)
実行回数:100000回 関数名:Array.from 実行時間:161(ms)
実行回数:100000回 関数名:concat   実行時間:87.0(ms)
実行回数:100000回 関数名:slice    実行時間:17.0(ms)

<2回目>
実行回数:100000回 関数名:スプレッド構文  実行時間:156(ms)
実行回数:100000回 関数名:Array.from 実行時間:145(ms)
実行回数:100000回 関数名:concat   実行時間:86.0(ms)
実行回数:100000回 関数名:slice    実行時間:17.0(ms)

<3回目>
実行回数:100000回 関数名:スプレッド構文  実行時間:142(ms)
実行回数:100000回 関数名:Array.from 実行時間:151(ms)
実行回数:100000回 関数名:concat   実行時間:91.0(ms)
実行回数:100000回 関数名:slice    実行時間:18.0(ms)

safari15.5も、「slice」か「concat」が良さそうです。

実行回数:100000回 関数名:スプレッド構文  実行時間:34.0(ms)
実行回数:100000回 関数名:Array.from 実行時間:334(ms)
実行回数:100000回 関数名:concat   実行時間:29.0(ms)
実行回数:100000回 関数名:slice    実行時間:29.0(ms)