はじめに
同じ引数で何度も呼び出される関数がある場合、計算結果をキャッシュすることでパフォーマンスを改善できる可能性があります。
本記事では、Pythonの標準ライブラリfunctoolsモジュールに含まれる lru_cache
を使用し実装例を紹介します。
環境
Python 3.12.4
lru_cacheの使い方
@lru_cache (maxsize=None)
というデコレータを関数の直前に付けるだけで、その関数の結果がキャッシュされます。maxsizeパラメータは、キャッシュするアイテムの最大数を指定します。Noneを指定すると、キャッシュサイズに制限がなくなります。
from functools import lru_cache
@lru_cache(maxsize=None)
def hoge_calculation(n):
# 時間のかかる計算をシミュレート
result = 0
for i in range(n):
result += i * i
return result
使用例
重たい処理に対して lru_cache を使用した場合と使用しない場合の違いを見てみます。
関数を10000回呼び出してみて実行時間を比較します。
import time
from functools import lru_cache
# 時間のかかる計算をシミュレート(キャッシュなし)
def sample_calculation(n):
result = 0
for i in range(n):
result += i * i
return result
# 時間のかかる計算をシミュレート(キャッシュあり)
@lru_cache(maxsize=None)
def cached_calculation(n):
return sample_calculation(n)
# 処理時間を計測
def measure_time(func, n, iterations):
start = time.time()
# 10000回呼び出し
for _ in range(iterations):
func(n)
end = time.time()
print(f"{func.__name__}: {iterations} 回呼び出し完了")
print(f"Total execution time: {end - start:.8f} seconds")
print(f"Average time per call: {(end - start) / iterations:.12f} seconds")
# キャッシュなしで10000回呼び出し
print("\n--- キャッシュなし ---\n")
measure_time(sample_calculation, 1000, 10000)
# キャッシュありで10000回呼び出し
print("\n--- キャッシュあり ---\n")
measure_time(cached_calculation, 1000, 10000)
実行結果
キャッシュなしとありで違いを見てみます。
合計処理時間と1回あたりの処理時間は次のようになりました。
--- キャッシュなし ---
sample_calculation: 10000 回呼び出し完了
Total execution time: 0.32430840 seconds
Average time per call: 0.000032430840 seconds
--- キャッシュあり ---
cached_calculation: 10000 回呼び出し完了
Total execution time: 0.00099969 seconds
Average time per call: 0.000000099969 seconds
キャッシュなしの場合は10000回の呼び出しに約0.32秒(1回あたり約0.000032秒)かかっているのに対し、キャッシュありの場合は同じ10000回の呼び出しが約0.001秒(1回あたり約0.0000001秒)で完了しています。lru_cacheを使用することで処理時間が改善されたことを確認できました。