😽

[Bug #20301] Set#add? が2回 hash の走査をしている改善

2024/04/06に公開

[Bug #20301] Set#add? does two hash look-ups

set = Set[1, 2]

# 要素がないので self を返す
pp set.add?(3)
# => #<Set: {1, 2, 3}>

# 既に要素があるので nil を返す
pp set.add?(1)
# => nil
  • この Set#add? の実装では @hash の要素を2回参照している
class Set
  def add?(o
    # 1. `include?(o)` で `@hash` の要素を参照している
    # 2. `add(o)` で2回目の `@hash` の要素を参照している
    add(o) unless include?(o)
  end
end
class Set
  def add?(o)
    # #update_value を利用することで参照を1回にする
    self unless @hash.update_value(o, true)
  end
end
  • チケット内にベンチマークが載っているんですが(見方がよくわからない…)早くなっていると提示されていますねー
  • ちなみに以下のような実装案もコメントに書いてあるんですがこれだとブロックを呼び出すオーバーヘッドがあるらしく、そこまで改善しないらしい
def add?(k)
  added = false
  # 要素が追加された時のみブロックが呼び出される
  @hash.add(k){ added = true }
  self if added
end
def add?(o)
  n = size
  add(o)
  m = size
  return n == m ? nil : self
end
GitHubで編集を提案

Discussion