Open10
javascriptでstringをn文字ずつchunkするときのパフォーマンス良い方法をさがす
ピン留めされたアイテム
前提
- 英数字のみ考慮
- わりかし大きい入力まで考慮
多分最速
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のほうが早いっぽい
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
}
初期化しないシンプル板。入力が小さい場合は差が無いが、入力が巨大になるほど負けていく
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)にしてみたりしたけど当然意味なかった
そんなに早くないシリーズ
const splitReg = (data: string, size: number) => {
return data.match(new RegExp(`.{1,${size}}`, "g"))
}
こことかで出てくるRegExp使ったやり方。最速のやり方からすると1/4ぐらいパフォーマンス悪い
lodash使う場合。結構早いのだけど最速のやり方と比べるとそこまででもなかった
const splitLodash = (data: string, size: number) => {
return chunk(data.split(""), size)
}
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のやりかた。配列むけの汎用性のあるやり方なので、こういう場合は全然勝てなさそう
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)
})
}
ベンチマーク
✓ 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