アメリエフの技術ブログ

Amelieff Staff Blog

pythonでキャッシュをとる

こんにちは、hr-kです。今回はpythonで、同じ関数を使い倒す時に、より早く計算する手法をご紹介します。

pythonで以下のようなコード(test.py)を書くとします。

import time

def test(text):
    # なんか重たい処理
    texts = ''
    for i in range(1000):
        texts = texts + text + str(i)
    texts = texts.split(sep=text)
    return(texts)

# 1回目のtest関数の時間計測
print('First')
start = time.time()
print(test('hello'))
end = time.time() - start
print(end)

# 2回目のtest関数の時間計測
print('Second')
start = time.time()
print(test('hello'))
end = time.time() - start
print(end)

実行すると1回目と2回目の実行にかかる時間がそれぞれ吐き出されます。

$ python test.py
First
['', '0', ....., '999']
0.0010828971862792969
Second
['', '0', ....., '999']
0.0010406970977783203

test.pytest関数には同じ引数'hello'を渡しているのに二回も同じ計算をしています。2回目の計算は無駄ですね。

今回紹介するのは、この二度目以降の計算を圧倒的に早くできる方法です。 具体的には今ある関数にmemoizeという関数を付け加えて以下の形にします。

import time

def memoize(f):
    cache = {}
    def helper(*args):
        if args not in cache:
            cache[args] = f(*args)
        return cache[args]
    return helper

@memoize
def test(text):
    texts = ''
    for i in range(1000):
        texts = texts + text + str(i)
    texts = texts.split(sep=text)
    return(texts)

# 1回目のtest関数の時間計測
print('First')
start = time.time()
print(test('hello'))
end = time.time() - start
print(end)

# 2回目のtest関数の時間計測
print('Second')
start = time.time()
print(test('hello'))
end = time.time() - start
print(end)

これで実行してみると〜

$ python test.py
First
['', '0', ....., '999']
0.001146554946899414
Second
['', '0', ....., '999']
0.0001671314239501953

2回目の計算が圧倒的に早くなりました。 これはtest関数に'hello'という文字列が入った結果をmemoizeが記憶(キャッシュ)して、そこから結果を引き出しているためです。 つまりは、2回目は'計算していない'のです。 より詳しくはこのページが参考になります。

これは、関数に同じ引数を入れることがある計算を大量に行う時にすごく役立つ手法です。 機会があれば試してみてくださいね。