web-dev-qa-db-ja.com

Rubyでの配列とハッシュのパフォーマンス

1つのクラスの多くのインスタンスを格納するプログラムがあります。たとえば、最大で10.000以上です。クラスインスタンスには、時々必要となるいくつかのプロパティがありますが、最も重要なものはIDです。

class Document
  attr_accessor :id
  def ==(document)
    document.id == self.id
  end
end

さて、これらの何千ものオブジェクトを保存する最も速い方法は何ですか?

私はそれらすべてを一連のドキュメントに入れていました:

documents = Array.new
documents << Document.new
# etc

今、代替策はそれらをハッシュに格納することです:

documents = Hash.new
doc = Document.new
documents[doc.id] = doc
# etc

私のアプリケーションでは、ほとんどの場合、ドキュメントが存在するかどうかを確認する必要があります。ハッシュのhas_key?関数は、配列の線形検索およびDocumentオブジェクトの比較よりも大幅に高速ですか?両方がO(n)内にあるか、has_key?であってもO(1)です。違いがわかりますか?

また、ドキュメントが既に存在する場合、ドキュメントを追加する必要がある場合があります。配列を使用する場合は、以前にinclude?で確認する必要があります。ハッシュを使用する場合は、もう一度has_key?を使用します。上記と同じ質問です。

あなたの考えは何ですか? 90%の時間でIDが存在するかどうか(オブジェクト自体ではない!)

38
slhck

ハッシュはmuchルックアップの方が高速です:

require 'benchmark'
Document = Struct.new(:id,:a,:b,:c)
documents_a = []
documents_h = {}
1.upto(10_000) do |n|
  d = Document.new(n)
  documents_a << d
  documents_h[d.id] = d
end
searchlist = Array.new(1000){ Rand(10_000)+1 }

Benchmark.bm(10) do |x|
  x.report('array'){searchlist.each{|el| documents_a.any?{|d| d.id == el}} }
  x.report('hash'){searchlist.each{|el| documents_h.has_key?(el)} }
end

#                user     system      total        real
#array       2.240000   0.020000   2.260000 (  2.370452)
#hash        0.000000   0.000000   0.000000 (  0.000695)
103
steenslag

Rubyの標準ライブラリにはセットクラスがあります。IDの(追加の)セットのみを保持することを検討していますか?

http://stdlib.rubyonrails.org/libdoc/set/rdoc/index.html

ドキュメントを引用すると、「これはアレイの直感的な相互運用機能とハッシュの高速ルックアップのハイブリッドです」。

5
Michael Kohl

一意の値を使用する場合は、前述の Ruby Set を使用できます。以下はベンチマーク結果です。ただし、ハッシュよりも少し遅いです。

                 user     system      total        real
array        0.460000   0.000000   0.460000 (  0.460666)
hash         0.000000   0.000000   0.000000 (  0.000219)
set          0.000000   0.000000   0.000000 (  0.000273)

ここにある@steenslagのコードに単に追加しました https://Gist.github.com/rsiddle/a87df54191b6b9dfe7c9

このテストではRuby 2.1.1p76を使用しました。

4
Ryan
  1. ドキュメントのセットを使用します。これには、必要なほとんどのプロパティがあります(一定時間のルックアップで、重複は許可されていません)。 Smalltalkerは、必要なプロパティが既に設定されているコレクションを使用することが、ほとんどの場合の戦いだと言っています。

  2. 条件付き挿入には(has_key?ではなく)、|| =を使用して、ドキュメントIDでドキュメントのハッシュを使用します。

ハッシュは、一定時間の挿入と検索のために設計されています。 Rubyのセットは内部でハッシュを使用します。

Documentオブジェクトは#hashと#eqlを実装する必要があることに注意してください。それらがハッシュの等価性を定義するために使用されるため、それらがハッシュキーまたはセットのメンバーとして期待どおりに動作するために適切に。

2
Rein Henrichs