2009-07-08 15 views
2

計算を高速化するために比較的小さい(< 10000エントリ< 1kb)キャッシュが必要なことがよくあります。私の通常のコードは次のようになります:小型でシンプルなキャッシュに最適なデータ構造は何ですか

cache = {} 
def calculate_caches(parms): 
    if parms not in cache: 
     cache[parms] = calculate(parms) 
    return cache[parms] 

正常に動作しますが、長時間実行されるプロセス私はメモリリークを恐れています。だから私は、多くの場合、このようにクランプ強引なメモリを実装:

if len(cache) > 1000: 
    cache = {} 

は、ほとんどのケースでも、合理的作品と、まだきれいな、簡単なコードです。しかし、私が本当にしたい場合LRU strategy私はキャッシュエントリと一緒にタイムスタンプが必要です。これに対してdictを使用する際の問題は、キャッシュが期限切れになるということは、エレガントで効率的ではないキャッシュ全体をトラバースすることを意味します。

cache = {} 
def calculate_caches(parms): 
    if parms not in cache: 
     cache[parms] = (time.time(), calculate(parms)) 
    expire() 
    return cache[parms][1] 

def expire() 
    if len(cache) > 1000: 
     mintime = time.time() 
     time2key = {} 
     for key, (timestamp, val) in cache.items(): 
      mintime = min([mintime, timestamp]) 
      time2key[timestamp] = key 
     if mintime in time2key: 
      del cache[time2key[mintime]] 

アドホックキャッシングを実装するための適切なアプローチ/データ構造はありますか?

私の問題はthis questionと非常によく似ていますが、リストを時間順に並べる必要はありません。私は二重引用符を付けたくありません。

答えて

1

タイムスタンプを使用しないでこれを行う簡単な方法は、末尾にMRUがあるところでordereddictionaryにすることです(これは、同じオブジェクトの要求が2回目に来るときに削除し、あなたが期限を切る必要があるときには、大きさが限界より大きい場合は、注文した辞書の最初からサイズXのスライスを削除するだけです。

効率は、順序付き辞書がどのように実装されるかによって異なります。

0

私は疑問に思っています。最適な戦略は、キャッシュミスのコストと計算のためのパラメータの時間的分布に強く依存します。

ガベージコレクションの方法があなたにインスピレーションを与えるかもしれません。あなたのキャッシュをヒープと考えてヒットを参照としてキャッシュすると、という低い数値のヒット数でキャッシュされた結果を効率的に収集するという問題があります。問題は、あなたが何かを再計算することができるので、GCよりもはるかに寛容です。

この方法では、頻繁にヒットしたパラメータに追加のキャッシュを導入することになります。キャッシュヒット時にインクリメントされる各キャッシュ値にカウンタを追加します。いくらかのしきい値が渡されると、キャッシュされた値は追加のキャッシュに昇格されます。両方の世代のキャッシュをサイズクランプすることができますので、メモリ使用量にはまだ厳しい制限があります。キャッシュミスの(可能な)減少がオーバーヘッド(2つのキャッシュ、ヒットカウンター、コピーなど)を正当化するならば、経験的な質問です...

1

インスピレーションのためにJavaのLinkedHashMapとLinkedHashSetを見ることができます。基本的には、挿入とオプションのアクセス順序の二重リンクリストを維持します。

LRUを維持するには、新しいエントリを挿入しながら最も古いエントリ(リンクされたリストの先頭に近いもの)を削除する戦略を定義できます。

関連する問題