よたらぼ
自分の興味の赴くままにIT技術系のネタを取りとめもなくメモっています。
Ruby言語やLinuxのネタが多いです。

January 20, 2006 [おもひで]

[Ruby] 保存しておいた文字列を高速に検索する

同じHTTPリファラから10回以上のアクセスがあった場合、それ以上(11回目から)のアクセスができないようにするためにはどのような実装が良いのかな。

まずは、リファラのURLをHashのキーとして持つことを考えてみた。

@deny_urls = Hash.new
def allow?(referer)
  ret = true
  if @deny_urls[referer]
    if @deny_urls[referer] >= 10
      ret = false
    else
      @deny_urls[referer] += 1
    end
  else
    @deny_urls[referer] = 1
  end
  ret
end
 
s = Time.now
(0...10000).each do |i|
  (0...15).each do
    allow?("http://www.yotabanana.com/#{i}")
    allow?("http://www.yahoo.com/#{i}")
    allow?("http://www.google.com/#{i}")
  end
end
p Time.now - s

なんだか、if文だらけになっちゃったし、使用例の部分もいまいちだけど、まぁいっか。

まぁ、ともかく、次に、keyに文字列のhashを使ってみる(ってかRubyのHashってこういう使い方しても大丈夫なんだっけ?(^^;))。

def allow?(referer)
  ret = true
  hash = referer.hash  #(1)
  if @deny_urls[hash]
    if @deny_urls[hash] >= 10
      ret = false
    else
      @deny_urls[hash] += 1
    end
  else
    @deny_urls[hash] = 1
  end
  ret
end

allow?メソッドのとこだけ書き換え。他は同じね。

で、両者の性能を測ると

% ruby test.rb
4.146538
% ruby test2.rb
3.488657

うーん、もうちょっと高速化したいなぁ。他に高速化する方法ってある?

ポイントとしては、以下の感じ。Rubyクイズスタート!応募はツッコミ欄で!!ちなみに応募しても何も出ません(ってオイ(^^;))

  1. (1)のような数値化はallow?メソッドの中で実行する必要がある。(ループの外には出せない)
  2. 別にデータ構造自体Hashでなくてもいい。
  3. だいたい(90%以上で良いかな)うまくいけばOK。
  4. データは不可逆でOK。

本日のツッコミ(全4件) [ツッコミを入れる]
リンク元プラグイン更新しなくちゃzunda (January 20, 2006 18:16)

ret = falseの前のifは本当はunlessでしょうか?それはそうとおもしろそうなので応募してみたいでーす。眠くなるのとどっちが早いか!?

眠くなってきたzunda (January 20, 2006 18:45)

こんなのはどうでしょう?

@deny_urls = Hash.new( 0 )
def allow?(referer)
  hash = referer.hash
  if @deny_urls[hash] < 10
    @deny_urls[hash] += 1
  end
end

ifの返り値を使うのがいやらしい感じ。

むとう (January 20, 2006 19:59)

あ、ごめんなさい。&lt;と&gt;間違えて書いてました。直しておきました。
#手書きなんです・・・(苦笑)。

むとう (January 20, 2006 20:08)

うーん、すごい。
zundaさん案のif文を整理しただけでも高速化するんですね!

% ruby test3.rb
3.202119

いやぁ、条件分岐、しっかり考えなきゃダメだってことですねぇ<自分。(苦笑)

ありがとうございましたm(__)m。


編集