🐙

【bugs.ruby Advent Calender 2025】パフォーマンスに関する警告を出す提案【12日目】

に公開

bugs.ruby Advent Calender 2025 12日目の記事です。

これはなに

今年1年間通してみてきた bugs.ruby のチケットの中から気になったものを1つずつ取り上げていく Advent Calender です。
取り上げるチケットは基本的にこのブログで取り上げたものになります。
記事のまとめは ここを参照 してください。

[Feature #21274] Show performance warnings for easily avoidable unnecessary implicit splat allocations

キーワード引数を渡す際にパフォーマンスで懸念点がある場合に警告を出すようにする提案になります。
どういうことかというと次のように位置引数 + キーワード引数にメソッドの戻り値を直接渡すコードがあるとします。

def kw = nil
ary = []

# kw メソッドの戻り値をキーワード引数で呼び出して渡す
m(*ary, kw:)

このときに kw メソッド内で ary の値を破壊的に変更した場合に評価順序の問題がある可能性があります。

$ary = [1, 2, 3]

def kw
  # この中で $ary を破壊的に変更する
  $ary.select!(&:even?)
end

# このときに p に渡す $ary は kw の変更の影響を受けないようにする必要がある
p(*$ary, kw:)

この問題を避けるために暗黙的にアロケーションが行われてパフォーマンスに影響があります。
このときに次のように先に kw メソッドを呼び出しておくことで暗黙的に評価順序の問題を回避(明示化)することができます。

$ary = [1, 2, 3]

def kw
  # この中で $ary を破壊的に変更する
  $ary.select!(&:even?)
end

kw = self.kw

# kw が先に処理されるのでわざわざ $ary を保持する必要はなくなる
p(*$ary, kw:)

のチケットではこういうケースのコードに対して以下のような警告を出す提案になります。

$ ruby -W:performance -e 'def kw; {} end; a = []; p(*a, **kw)'
-e: warning: This method call implicitly allocates a potentially unnecessary
array for the positional splat, because a keyword, keyword splat, or block pass
expression could cause an evaluation order issue if an array is not allocated
for the positional splat. You can avoid this allocation by assigning the related
keyword, keyword splat, or block pass expression to a local variable and using
that local variable.

$ ruby -W:performance -e 'def b; ->{} end; h = {}; p(**h, &b)'
-e: warning: This method call implicitly allocates a potentially unnecessary
hash for the keyword splat, because the block pass expression could cause an
evaluation order issue if a hash is not allocated for the keyword splat. You
can avoid this allocation by assigning the block pass expression to a local
variable, and using that local variable.

個人的にはこういうカリカリにチューニングするテクニックは好きではあるんですが、それはそれとして書き味が悪くなったり、コードが冗長になると本末転倒ではあるのでなかななバランスが難しいですね…。
パフォーマンスがボトルネックになるような箇所であればこういうテクニックが必要にはなってくると思うんですが恒常的に警告が出ると流石に煩わしさのほうが勝っちゃいそうですかねえ。

実際に Ruby ではこういうパフォーマンス目的なコードよりもより流暢なコードを書くことを重視したいということでこの提案は却下されています。
何かしらピンポイントでこういう検証ができる仕組みがあるとよさそうではあるんですが難しそう。

関連

GitHubで編集を提案

Discussion