left-padの文字列連結はクソではない

ここから始まる一連の、モジュールの依存性に関する議論はなかなか興味深いが、自分的に気になったのは以下の一節

GCを虐めるためとかコンパイラの最適化を確認するために用意する、「無駄に一時オブジェクト量産するクソコードの典型例」みたいな実装

ソースを見てみようか。

left-pad/index.js at 0e04eb4da3a99003c01392a55fa2fdb99db17641 · azer/left-pad · GitHub

なるほど一見するとクソコードにみえる。確かに、過去のJS処理系では str = ch + str のような文字列連結を大量に行うコードは非常に遅かった。このため多数の文字列を連結する場合、Arrayに結合する文字列をpushし、最後にjoinするという方法で高速化を図っていた。

しかし、それはv8登場以前の話である。今では文字列連結は処理系によって強烈な最適化を施され、Arrayを使うよりも高速に動作するようになっている。内部的には string-builder によって無駄なオブジェクトを生成する事なく文字列を構築するので、現在のJSでは一目でクソと言うほどクソではないはずだ。

と思ったのでベンチとってみた。処理系はnode v4.3.1

  • leftpad1、例のnpmモジュールのコード
  • leftpad2、現状ES.next proporsalのString.prototype.repeatで一気にpad文字列を作るコード
  • leftpad3、arrayに詰めてjoinするコード。ただしpushするのではなくES6のArray.fillで一気にpad文字列を作成するようにした(whileループでpushするよりも高速)

結果

padding len 4 8 16 32 64 128
leftpag1 concat with + 13 80 194 328 607 1138
leftpad2 repeat and substring 92 103 202 228 305 463
leftpad3 array fill and join 16 517 677 1012 1683 3007

数値は処理に要した時間、単位はミリ秒

  • Arrayでjoinは今となっては遅い。
  • lenが16以下の場合は+で接続する方が速い
  • lenが16より大きい場合はrepeatで一気にpad文字列を作るのが有利
  • len = 4のとき、leftpad1/3はほとんどの場合でstrをそのまま返すのでleftpad2より圧倒的に高速

結論

  • シンプルで高速なrepeatを使おう!

テストコード

gist.github.com