Open10

javascriptでstringをn文字ずつchunkするときのパフォーマンス良い方法をさがす

ピン留めされたアイテム
terrierscriptterrierscript

前提

  • 英数字のみ考慮
  • わりかし大きい入力まで考慮
terrierscriptterrierscript

多分最速

const splitSubstrIdxInit = (data: string, size: number) => {
  let res = Array(Math.ceil(data.length / size))
  for (let i = 0;i < res.length;i++) {
    res[i] = data.slice(i * size, i * size + size)
  }
  return res
}
  • Array(n)で初期化するほうが早い。
  • arr.pushしないでindexアクセスするほうがはや
  • substringかsliceかはあんまり差がないが、入力が大きいとsliceのほうが早いっぽい
terrierscriptterrierscript
const splitSliceIdx = (data: string, size: number) => {
  let res = []
  for (let i = 0;i < Math.ceil(data.length / size);i++) {
    res[i] = data.slice(i * size, i * size + size)
  }
  return res
}

初期化しないシンプル板。入力が小さい場合は差が無いが、入力が巨大になるほど負けていく

terrierscriptterrierscript
const splitSliceArrayFill = (data: string, size: number) => {
  return Array(Math.ceil(data.length / size))
    .fill(null)
    .map((_, i) => {
      return data.slice(i * size, i * size + size)
    })
}
  • 思いの外fillしてmapするのも早かった。ただしdataが小さいときはちょっとだけ不利になった
  • 個人的にはキレイさもあって結構好き
  • see: https://www.30secondsofcode.org/js/s/array-initialization/
  • イマイチ条件がわからないが大量にテストを走らせているとこいつが最速になることがある。ベンチマーク環境の問題かも?
  • fill(null)をfill(undefined)とかfill(0)にしてみたりしたけど当然意味なかった
terrierscriptterrierscript

そんなに早くないシリーズ

terrierscriptterrierscript

lodash使う場合。結構早いのだけど最速のやり方と比べるとそこまででもなかった

const splitLodash = (data: string, size: number) => {
  return chunk(data.split(""), size)
}
terrierscriptterrierscript
const splitYdnChunk = (input: string, size: number) => {
  // @ts-ignore
  return input.split("").reduce((arr: string[], item: string, idx: number) => {
    return idx % size === 0
      ? [...arr, [item]]
      : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]]
  }, [] as string[])
}

you don't need lodashのやりかた。配列むけの汎用性のあるやり方なので、こういう場合は全然勝てなさそう
https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore

terrierscriptterrierscript

Array(len).fill(null)で早いならfromでも変わらんのでは?と思いきや全然遅かった(1/6程度)

const splitSliceArrayFrom = (data: string, size: number) => {
  return Array.from({ length: Math.ceil(data.length / size) }).map((_, i) => {
    return data.slice(i * size, i * size + size)
  })
}
terrierscriptterrierscript

ベンチマーク

  ✓ small (7) 16879ms
     name                            hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · splitSliceIdxInit    15,143,606.85  0.0000  0.8338  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.75%  7571804   fastest
   · splitSliceArrayFill   8,387,064.62  0.0000  0.2817  0.0001  0.0001  0.0002  0.0002  0.0002  ±0.16%  4193533
   · splitReg              4,262,941.22  0.0001  0.4266  0.0002  0.0002  0.0003  0.0003  0.0007  ±1.07%  2131471   slowest
   · splitSliceIdx        14,949,219.91  0.0000  0.5385  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.40%  7474611
   · splitSubstrIdx       14,561,486.41  0.0000  0.3440  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.22%  7280744
   · splitLodash           4,474,437.56  0.0001  0.5211  0.0002  0.0002  0.0002  0.0003  0.0008  ±1.49%  2237219
   · splitSlice           11,868,984.39  0.0000  0.4896  0.0001  0.0001  0.0001  0.0001  0.0002  ±0.34%  5934493
   ✓ large (7) 17381ms
     name                        hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · splitSliceIdxInit    56,152.72  0.0153  0.3582  0.0178  0.0164  0.0203  0.2463  0.2818  ±1.33%    28077   fastest
   · splitSliceArrayFill  39,085.48  0.0218  0.4738  0.0256  0.0231  0.0364  0.3382  0.3927  ±1.64%    19543
   · splitReg             14,043.85  0.0641  0.5658  0.0712  0.0664  0.4173  0.4489  0.5151  ±1.40%     7022   slowest
   · splitSliceIdx        36,265.13  0.0220  0.4645  0.0276  0.0245  0.2976  0.3158  0.3521  ±1.68%    18133
   · splitSubstrIdx       33,356.35  0.0230  0.6535  0.0300  0.0254  0.4096  0.4319  0.5029  ±2.28%    16679
   · splitLodash          21,373.29  0.0348  0.8961  0.0468  0.0370  0.4569  0.4905  0.5664  ±2.64%    10687
   · splitSlice           21,341.87  0.0359  1.7735  0.0469  0.0383  0.5718  0.6395  0.8965  ±3.03%    10671


 BENCH  Summary

  splitSliceIdxInit - __tests__/split.bench.ts > small
    1.01x faster than splitSliceIdx
    1.04x faster than splitSubstrIdx
    1.28x faster than splitSlice
    1.81x faster than splitSliceArrayFill
    3.38x faster than splitLodash
    3.55x faster than splitReg

  splitSliceIdxInit - __tests__/split.bench.ts > large
    1.44x faster than splitSliceArrayFill
    1.55x faster than splitSliceIdx
    1.68x faster than splitSubstrIdx
    2.63x faster than splitLodash
    2.63x faster than splitSlice
    4.00x faster than splitReg