リモートIPアドレス計算の最適化
ActionDispatch::RemoteIp::GetIp#calculate_ip の実装を最適化することで、クライアントIPアドレスの取得処理が約3倍高速化されました。本番環境のプロファイリングで判明したボトルネックを解消し、リクエスト全体の実行時間を削減します。
背景
本番環境のウォール/CPUプロファイリングによって、クライアントIPアドレスの取得処理がリクエスト全体の実行時間の数パーセントを占めていることが判明しました。特に rack-attack のようなミドルウェアが計算済みIPアドレスに繰り返しアクセスする環境では、この処理コストが無視できない影響を与えていました。
アロケーションプロファイリングでも、IP計算処理が上位にランクインしており、オブジェクト生成の観点からも最適化の余地があることが示されていました。#56805 はこのパフォーマンスボトルネックに対処しています。
技術的な変更
actionpack/lib/action_dispatch/middleware/remote_ip.rb の calculate_ip メソッドと filter_proxies メソッドが最適化されました。主な変更は以下の3点です。
配列操作の最適化
変更前:
ips = forwarded_ips + client_ips
ips.compact!
filter_proxies(ips + [remote_addr]).first || ips.last || remote_addr
変更後:
ips = forwarded_ips + client_ips
ips.compact!
ips << remote_addr
first_non_proxy(ips) || ips[-2] || ips.last
remote_addr を ips 配列に直接追加することで、filter_proxies 呼び出し時の配列結合操作(ips + [remote_addr])を削減しています。また、フォールバック処理を ips[-2] || ips.last に変更し、配列アクセスの効率を向上させています。
プロキシフィルタリングの最適化
filter_proxies メソッドを first_non_proxy に置き換え、処理ロジックを刷新しました。
変更前:
def filter_proxies(ips)
ips.reject do |ip|
@proxies.any? { |proxy| proxy === ip }
end
end
変更後:
def first_non_proxy(ips)
ips.find do |raw_ip|
return unless raw_ip
ip = IPAddr.new(raw_ip)
@proxies.none? do |proxy|
if proxy.is_a?(IPAddr)
proxy.include?(ip)
else
proxy === raw_ip
end
end
end
end
reject + first の組み合わせを find に置き換えることで、最初の非プロキシIPが見つかった時点で走査を終了します。また、IPAddr オブジェクトの場合、include? メソッドを用いてIPアドレスがプロキシの範囲に含まれるかを確認します。これにより、CIDR表記されたプロキシ範囲との比較が可能になります。それ以外の場合は === 演算子を使用する分岐を追加しています。
ベンチマーク結果
PRに添付されたベンチマークでは、以下の環境で約3.11倍の性能向上が確認されています。
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-darwin21]
Comparison:
optimized: 33102.0 i/s
original: 10638.0 i/s - 3.11x slower
1回の実行時間が94.00μsから30.21μsに短縮され、スループットが大幅に向上しています。
設計判断
早期リターンとアロケーション削減を組み合わせた最適化 が採用されました。
reject による全要素の走査を find による早期リターンに置き換えることで、プロキシでないIPアドレスが見つかった時点で処理を終了します。加えて、ips + [remote_addr] による一時的な配列生成を削減し、アロケーションプロファイリングで観測されたオブジェクト生成コストを抑制しています。
フォールバック処理の ips[-2] は、remote_addr が配列末尾に追加される実装を前提とした最適化です。プロキシでないIPが見つからない場合、末尾から2番目の要素(remote_addr の直前)を返すことで、従来の ips.last と同等の結果を得ています。
まとめ
本PRは、配列操作の最適化と早期リターンの導入により、リモートIPアドレス計算処理を約3倍高速化しました。rack-attackのようなミドルウェアが頻繁にIPアドレスを参照する環境では、この改善がリクエスト全体のパフォーマンスに直接寄与します。本番環境のプロファイリングに基づく実装の見直しにより、Railsアプリケーションの実行効率が向上しました。