0

標準の memoize デコレータのキャッシュはプロセスセーフですか?

たとえば、次のデコレータを定義するとします。

import functools

def memoize(func):
    cache = {}
    @functools.wraps(func)
    def memoized(*args):
        result = None
        if args in cache:
            result = cache[args]
        else:
            result = func(*args)
            cache[args] = result
        return result
    return memoized

そして、再帰関数の計算を高速化するためにそれを使用しようとしているとします。

@memoize
def fib(n):
    result = 1
    if n > 1:
        result = fib(n-1) + fib(n-2)
    return result

ここで、fib() を計算する 2 つのプロセスが衝突する可能性があるのだろうか? 例えば:

if __name__ == "__main__":
    from multiprocessing import Process
    p1 = Process(target=fib, args=(19,))
    p2 = Process(target=fib, args=(23,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

私の最初の考えは、キャッシュは fib のコンテキストで保存されるため、プロセス間で共有され、競合状態につながる可能性があるということでした。しかし、起こりうる最悪の事態は、たとえば、 fib(17) が計算されていないと両方が考え、両方が先に進んで並行して計算し、同じ結果を次々に保存することだと思います-理想的ではありませんが、恐ろしいことではないと思います。しかし、プロセスセーフな方法でそれを行う方法があるかどうかはまだ疑問です.

編集: memoized() の各ブランチに print ステートメントを追加しました。各プロセスがキャッシュ内のすべての fib 値を再計算するようです。結局、キャッシュが共有されていないのではないでしょうか? 共有されていない場合は、それを共有するためのプロセスセーフな方法があるかどうかを気にします(さらに計算を節約するため)。

4

1 に答える 1

0

デフォルトでは、Python のマルチプロセス プログラムはプロセス間でほとんど共有しません。共有されるいくつかのものはpickled、それ自体にいくつかの制限があります。あなたの例のfib関数は名目上共有されていますがpickle、値ではなく名前で関数を保存します。そのため、そのキャッシュは共有されません。

デコレーター用に同期キャッシュが必要な場合は、やmemoizeなどの同期をキャッシュに追加する必要があります。ただし、プロセスが更新をやり取りするときに多くのオーバーヘッドが発生するため、各プロセスに値を単純に再計算させるよりも遅くなる可能性があります。multiprocessing.Queuemultiprocessing.Array

別の方法として、実行中に個別のプロセスを厳密に同期する必要がない場合は、プロセスの開始時と停止時にプロセスとの間でキャッシュをやり取りする方法を考え出すことができます (たとえば、追加の引数と戻り値を使用します)。値)、並列呼び出しがメモ化されていなくても、順次呼び出しがメモ化の恩恵を受けることができるようにします。

于 2013-02-06T23:38:42.807 に答える