配列操作でのshiftはコストが高いことで有名です。ところでつい先日ES2015にcopyWithinというメソッドが追加されていたことを知りました(今更)。調べてみたところ「左詰め」のようにズラす操作が得意なようです。これはshiftに使えるのではないか、と思い検証してみました。
検証
検証にはjsPerfを使用しました。fast shiftが今回つくった関数、native shiftが配列のshiftメソッドです。(500)とあるものは500個左へ詰める操作をしたときです。
Browser | fast shift | native shift | fast shift(500) | native shift(500) |
---|---|---|---|---|
Chrome 60.0.3112 | 1,646±8.20% 17% slower | 1,990±8.18% | 2,188±9.71% | 1,685±6.45% 21% slower |
Edge 13.10586.0 | 1,401±3.93% 14% slower | 1,622±3.91% | 1,499±4.67% | 1,073±2.31% 27% slower |
Mobile Safari 11.0.0 | 5,646±8.47% 66% slower | 15,479±1.34% | 8,585±0.90% | 5,681±1.05% 34% slower |
Chrome Mobile iOS 62.0.3202 | 5,800±3.93% 63% slower | 15,380±1.57% | 8,271±2.02% | 5,715±0.51% 30% slower |
きれいな結果が出ました。
考察
1つだけズラす場合はネイティブのshiftメソッド、500個ズラす場合はcopyWithinを使った関数のほうが早いという結果になりました。
つまり、適当なしきい値を決めて、それ以下ならネイティブ、それより上ならcopyWithin版を使うようにすればいいということです。
ということでこれが高速化されたshiftです。
const fastShift = (arr, n) => { if (n > 100) { const res = arr.slice(0, n); arr.copyWithin(0, n); arr.length -= n; return res; } const res = []; for (let i = 0, _i = arr.length; i < _i; i = 0 | i + 1) { res[i] = arr.shift(); } return res; }
イマイチ用途の判然としないcopyWithinですが、左詰めする場面なら使い道があるよ、という話でした。もっとも、複数個をまとめて左詰めしたくなる場面なんてそうない気がしますが。