2009-03-30 5 views
23

2008年7月中旬にメモ帳がRailsコアに追加されました。使用法のデモンストレーションはhereです。Ruby on Railsでmemoizationを使用する場合

私は、メソッドをメモしなければならないときの良い例とそれぞれのパフォーマンスの意味を見つけることができませんでした。たとえば、This blog postは、しばしばメモを使用しないことを示唆しています。

パフォーマンスに大きな影響を与える可能性のあるものについては、簡単なチュートリアルの提供を超えるリソースはほとんどないようです。

自分のプロジェクトで使用されているメモは誰も見ていますか?どのような要因でメソッドをメモすることになりますか?


自分でいくつかの調査をしたところ、メモの生成はRailsコアの中で非常に多く使用されていました。

ここに例を示します:http://github.com/rails/rails/blob/1182658e767d2db4a46faed35f0b1075c5dd9a88/actionpack/lib/action_view/template.rb

この使用法は、上記のブログの記事のメモによれば、パフォーマンスを低下させる可能性があることを発見したことに反するようです。

答えて

33

私は、多くのRails開発者は、どのメモ帳が何をし、どのように動作するのか完全に理解していないと思います。私はそれが遅延ロードされたコレクション(Sequelデータセットのような)を返すメソッドに適用されているか、または引数を取らずインスタンス変数に基づいて何かを計算するメソッドに適用されているのを見てきました。最初のケースでは、メモはオーバーヘッドに過ぎず、2番目にはバグを追跡するのが厄介で難しいソースです。

返された値が計算するだけで少し高価です

  • 場合、私はメモ化を適用しないでしょう。それは非常に高価である必要があり、さらに最適化することはできません、それはメモの価値があるためです。
  • 返される値が遅延ロードされている、または遅延ロードされている可能性があります
  • このメソッドは純粋な関数ではありません。つまり、同じ引数に対して同じ値を返すことが保証されています。他の純粋な関数。インスタンス変数を使用するか、インスタンス変数を使用する呼び出しメソッドは、メソッドが同じ引数に対して異なる結果を返す可能性があることを意味します。

上記の質問や回答のように、メモ編集が適切でない状況もありますが、これは明らかではないと思います。

最後の項目は、おそらく最も重要である:

def unmemoizable1(name) 
    "%s was here %s" % name, Time.now.strftime('%Y-%m-%d') 
end 

def unmemoizable2 
    find_by_shoe_size(@size) 
end 

の両方が、しかし、書き換えることができます。この方法は、それがメモ化することはできません。このように見える場合メモ化は、メソッドの引数に基づいて結果をキャッシュしますメモ化(これらの2つのケースで、それは明らかに他の理由のために行われるべきではありませんが)を利用するには:

def unmemoizable1(name) 
    memoizable1(name, Time.now.strftime('%Y-%m-%d')) 
end 

def memoizable1(name, time) 
    "#{name} was here #{time}" 
end 
memoize :memoizable1 

def unmemoizable2 
    memoizable2(@size) 
end 

def memoizable2(size) 
    find_by_shoe_size(size) 
end 
memoize :memoizable2 

find_by_shoe_sizeが持っている、または、任意の副作用を頼っていなかったと仮定して)

このトリックは、メソッドから純粋な関数を抽出し、その代わりにメモ化を適用することです。

+0

「同じ引数に対して同じ値を返すことが保証されています」と言えば、Rails ActiveRecordクエリにどのように適用されますか?たとえば、シティモデル内でアクティブとマークされたすべての都市を取得する方法をメモしました。例: '' 'def self.active @active_cities = || end '' 'アプリケーションが稼働しているときに新しい都市が管理者によってデータベースに追加されることがあるときは、このメモ化されたインスタンス変数をオーバーライドするためにサーバを再起動する必要がありますか?(" cities.active =(?) "、true) ?またはメモのインスタンス変数は、すべての要求の後に破棄され、再作成されますか? – Kelseydh

+1

上記に応答して: 私はmemoizationが**単一リクエストのライフサイクル中にインスタンス変数のみを保持することに気づきました。***これは私を混乱させるものです。初心者の方は、この重要なポイントを定義に追加することをお勧めします。 – Kelseydh

10

メソッドが複数のテーブルからデータをフェッチし、結果のオブジェクトを返す前にいくつかの計算を実行し、このメソッドが複数の要求である場合は、メモの作成が合理的かもしれません。

クエリキャッシングもアクティブなので、純粋なデータベースフェッチではなくRubyの計算を実行するメソッドのみをメモしてください。

+1

ActiveRecordオブジェクトの作成は計算としてカウントされますか?私が理解しているように、クエリキャッシュは、作成されたオブジェクト(作成プロセスがクエリ自体よりもしばしば時間がかかる)ではなく、mysql結果セットのみをキャッシュします。 – Gdeglin

+1

私が知る限り、クエリキャッシュは実際のActiveRecordオブジェクトを格納します。 –

2

おそらく私の経験は、メモを使用しない場合の良い例です。私の注文モデルでは、簡単な計算結果、すなわち注文番号小計、注文#税、モデルオブジェクト、すなわち注文番号most_recent_credit_card_usedを含む。後者では、クレジットカードオブジェクトを返すメソッドをメモするときに、メモオブジェクトの属性を更新しようとすると「フリーズハッシュ」エラーが発生します。注文番号most_recent_credit_card_used.frozen?メソッドがメモされたときにtrueを返しました。もちろん、私が意図したものではありません。

マイ持ち帰りは簡単だった:単純なデータ型(整数フロートなど)を返す高価な操作のためにmemoizeを使用しますが、ActiveRecordのモデルのような複雑なオブジェクトを返すとき、ESP、memoizeを使用しないでください。それらのオブジェクトをメモリ内で更新する予定がある場合

関連する問題