rubocop-performance 導入による正規表現マッチングとArray生成の慣用的な書き換え
bootsnap に rubocop-performance を導入し、正規表現マッチングと配列生成の記述をパフォーマンス上より好ましいイディオムへ統一しました。変更点はスタートアップ処理やテストコードに分散していますが、コードベース全体の一貫性を高める整備として意義があります。
背景
Rubyの静的解析ツールである RuboCop には、パフォーマンスに関する追加ルールセット rubocop-performance が存在します。このプラグインは、意味的に等価でありながらより高速な代替表現を検出し、自動的に指摘します。
bootsnap はRailsアプリケーションの起動高速化を目的としたライブラリであり、自身のコードにも無駄がないことが望ましいのは自明です。PRの説明によれば「スタートアップとテストにおける変更のためパフォーマンスの改善は大きくはないが、それでも価値があると考える」とされており、ライブラリの性質に照らした一貫した姿勢といえます。
技術的な変更
変更は大きく3種類のパターンに分類され、いずれも rubocop-performance のルールに対応しています。
正規表現マッチング: =~ から .match? または .include? への書き換え
文字列が固定のパターンを含むか検査する箇所では、=~ 演算子を String#include? または Regexp#match? に置き換えています。=~ は $~ などのグローバル変数への副作用を持つため、マッチデータが不要な用途では match? や include? を使うほうが高速かつ意図が明確です。プラットフォーム判定コードの代表例を示します。
変更前:
if RUBY_PLATFORM =~ /java/
変更後:
if RUBY_PLATFORM.include?("java")
同様のパターンが lib/bootsnap.rb、lib/bootsnap/compile_cache/iseq.rb、lib/bootsnap/load_path_cache/store.rb、および複数のテストファイルで適用されています。/mswin|mingw|cygwin/ のように複数パターンのOR検索では String#include? に置き換えられず、Regexp#match? が選ばれています。
配列生成: N.times.map から Array.new(N) への書き換え
Enumeratorを経由した配列生成は、直接 Array.new を使う形に変更されました。2.times.map { ... } は中間のEnumeratorオブジェクトを生成するのに対し、Array.new(2) { ... } はそれを生成しないため、アロケーション数を削減できます。
変更前:
pids = 2.times.map do
::Process.fork { exit!(true) }
end
@workers = @size.times.map { Worker.new(@jobs) }
変更後:
pids = Array.new(2) do
::Process.fork { exit!(true) }
end
@workers = Array.new(@size) { Worker.new(@jobs) }
テストコードの 10.times.map(&:to_s) も同様に Array.new(10, &:to_s) へ書き換えられています。
設定: .rubocop.yml と Gemfile へのプラグイン追加
プラグインの追加は最小限の設定変更で完結しています。.rubocop.yml に plugins: - rubocop-performance を加え、Gemfile の development グループに gem "rubocop-performance" を追記しています。既存のルール設定への影響はなく、新たなルール群が追加される形です。
設計判断
固定文字列の検索に include? を使い、ORパターンには match? を使う という使い分けが一貫して適用されています。/java/ のような単純な固定文字列は include? で表現でき、/mswin|mingw|cygwin/ のような複数候補のORはそのまま正規表現として match? に渡しています。これにより、文字列操作の意図が記述から直接読み取れる設計になっています。
また、ソースコードとテストコードを区別せず同一のルールで整備している点も注目できます。テストコードも実行時にパフォーマンスへの影響を持つことを踏まえ、一括して慣用的な記述に統一する判断がとられています。
まとめ
rubocop-performance の導入によって、正規表現マッチングと配列生成という頻出パターンが、アロケーションを削減しグローバル変数への副作用を持たない表現へ統一されました。個々の改善は小さくても、起動時のコードパスを担うライブラリにとって無駄のない実装を規約として維持する仕組みを手に入れた点に、この変更の本質的な価値があります。