ロバストPythonでも出てきたlru_cacheを試してみる。 キャッシュをデコレータだけで実現でき、標準で備わっているのでいい。
lru_cacheとは
関数をメモ化用の呼び出し可能オブジェクトでラップし、呼び出し最大maxsize
回まで保存するデコレータ
maxsize
を指定しない場合、上限なしで保存する。
どんなときに使えるのか
処理に時間がかかる場合に使える。 キャッシュを利用した場合、キャッシュしたデータ元が更新され、キャッシュが更新されていない場合は古いデータが返却されてしまう。 そのため、常に最新データを返す必要がある場合は注意が必要。
使ってみる
呼び出し最大2回まで保存するように設定して実行してみる。
@lru_cache(maxsize=2) def calculate(a): print("call calculate") r = 0 for i in range(10000000): r += i return r def main_call(): for i in range(5): calculate(1) print(calculate.cache_info()) if __name__ == '__main__': main_call()
5回実行してprint文は1回だけなのでキャッシュが効いている。
しかし、説明によれば最大maxsize
まで保存すると書いてあるので、3回目でprint文がでるのかなと思ったが、違うみたい。
call calculate
CacheInfoを確認してみる
ラップされた関数には、cache_info()関数が追加されるようなので、cache_info()関数をコールして、CacheInfoを確認する。 currsizeは、常に1になっている。どうやら、キャッシュで保持しているデータの数みたい。
call calculate 49999995000000 CacheInfo(hits=0, misses=1, maxsize=2, currsize=1) 49999995000000 CacheInfo(hits=1, misses=1, maxsize=2, currsize=1) 49999995000000 CacheInfo(hits=2, misses=1, maxsize=2, currsize=1) 49999995000000 CacheInfo(hits=3, misses=1, maxsize=2, currsize=1) 49999995000000 CacheInfo(hits=4, misses=1, maxsize=2, currsize=1)
追加で以下のプログラムで動作を確認してみる。
@lru_cache(maxsize=2) def calculate(a): print("-- Call calculate function") r = 0 for i in range(10000000): r += i return r def main_call(): print("calculate 1") calculate(1) print(calculate.cache_info()) print("calculate 2") calculate(2) print(calculate.cache_info()) print("cacheが効いているはず -------") print("calculate 1") calculate(1) print(calculate.cache_info()) print("calculate 2") calculate(2) print(calculate.cache_info()) print("1のキャッシュが削除 -------") print("calculate 3") calculate(3) print(calculate.cache_info()) print("2と3のcacheが効いているはず -------") print("calculate 2") calculate(2) print(calculate.cache_info()) print("calculate 3") calculate(3) print(calculate.cache_info()) print("1のcacheがない -------") print("calculate 1") calculate(1) print(calculate.cache_info())
実行結果からcalculate 3
でキャッシュミスが発生し、calculate 3
のキャッシュが登録されて、再度calculate 1
の実行で、キャッシュミス(misses)が発生していることがわかる。
デコレータの説明では、最大maxsize
回まで保存するとなっているけど、最大maxsize
個まで保存するの方が説明としては正しいのかなと。(文章の理解力の問題か・・・)
calculate 1 -- Call calculate function CacheInfo(hits=0, misses=1, maxsize=2, currsize=1) calculate 2 -- Call calculate function CacheInfo(hits=0, misses=2, maxsize=2, currsize=2) cacheが効いているはず ------- calculate 1 CacheInfo(hits=1, misses=2, maxsize=2, currsize=2) calculate 2 CacheInfo(hits=2, misses=2, maxsize=2, currsize=2) 1のキャッシュが削除 ------- calculate 3 -- Call calculate function CacheInfo(hits=2, misses=3, maxsize=2, currsize=2) 2と3のcacheが効いているはず ------- calculate 2 CacheInfo(hits=3, misses=3, maxsize=2, currsize=2) calculate 3 CacheInfo(hits=4, misses=3, maxsize=2, currsize=2) 1のcacheがない ------- calculate 1 -- Call calculate function CacheInfo(hits=4, misses=4, maxsize=2, currsize=2)
まとめ
functools.lru_cache
は簡単にキャッシュを実現でき、標準でPythonに組み込まれているので、OSSを改めてインストールする必要がないので、プロジェクトの関係でOSSが利用できないプロジェクトでも導入がしやすいと考える。
自分自身も使う機会があれば、積極的に使ってみたい。