配列に文字列を追加していってjoinするのは遅い?
String#<<
メソッドで文字列に文字列を追加していくと破壊的になるので frozen-string-literal を有効にしたときエラーになります。そこで <<
を +=
に置き換えて新しい文字列を作り直すようにしたりしていましたが、変更箇所が多いとやや面倒なので元の変数を配列にして最後に join する方法で対処することもありました。これならたった二箇所の変更で済みます。ただこの配列にする方法は簡単な反面、なんとなく富豪的で遅くなっているような気がしていたので、ちゃんと速度を比較してみました。
結果
RUBY_VERSION # => "2.5.0"
require "active_support/core_ext/benchmark"
def _; "%7.2f ms" % Benchmark.ms { 1000.times { yield } } end
_ { s = ""; 1000.times { s << rand.to_s }; s } # => "1052.91 ms"
_ { s = []; 1000.times { s << rand.to_s }; s.join } # => "1012.97 ms"
_ { s = ""; 1000.times { s += rand.to_s }; s } # => "3393.16 ms"
これでわかったのは遅くなってないということです。配列にしてjoinしても破壊的に追加する方法より遅くなってない、むしろちょっと速かったのは意外でした。一方で3番目の新しい文字列を作り直す方法はインスタンスを生成しまくっているせいか差がでてきてます。
まとめ
バッファ的な文字列変数に断片をどんどん追加していくような処理を frozen-string-literal 対応させるときに、速度が重要なときは <<
を +=
に置き換えるのではなく元の変数を配列にするのも一考です。