😽
[Bug #20301] Set#add? が2回 hash の走査をしている改善
[Bug #20301] Set#add?
does two hash look-ups
-
Set#add?
は『要素が追加された場合はself
を返し、そうでない場合はnil
を返す』というメソッドになる
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
- これを内部で [Feature #20300] Hash: set value and get pre-existing value in one call を利用することで改善したいという内容のチケット
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
Discussion