Python 为Ray actor函数实现缓存

Python 为Ray actor函数实现缓存,python,caching,ray,functools,toolz,Python,Caching,Ray,Functools,Toolz,我的目标是让下面的代码大约在0.3秒而不是0.5秒内执行。我尝试过使用functools.lru_cache、toolz.functoolz.memoize和kids.cache.cache上的装饰程序,但这些都不起作用(错误消息或未正确执行)。我能做些什么来让它工作 import ray @ray.remote class Foo: def foo(self, x): print("executing foo with {}".format(x)

我的目标是让下面的代码大约在0.3秒而不是0.5秒内执行。我尝试过使用
functools.lru_cache
toolz.functoolz.memoize
kids.cache.cache
上的装饰程序,但这些都不起作用(错误消息或未正确执行)。我能做些什么来让它工作

import ray


@ray.remote
class Foo:
    def foo(self, x):
        print("executing foo with {}".format(x))
        time.sleep(0.1)


ray.init()
f = Foo.remote()
s = time.time()
ray.get([f.foo.remote(x=i) for i in [1, 2, 1, 4, 1]])
print(time.time()-s)
ray.shutdown()

一般警告:如果函数产生副作用,缓存任意函数调用可能是危险的

在这种情况下,您可能希望程序输出

executing foo with 1 
executing foo with 2 
executing foo with 4 
您提到的其他缓存工具往往无法与Ray配合使用,因为它们试图以某种全局状态存储缓存,而不是将该状态存储在可以以分布式方式访问的位置。因为您已经有了一个actor,所以您可以将全局状态存储在actor中

@ray.remote
class Foo:
    def __init__(self):
        self.foo_cache = {}

    def foo(self, x):
        def real_foo(x):
            print("executing foo with {}".format(x))
            time.sleep(0.1)
        if x not in self.foo_cache:
            self.foo_cache[x] = real_foo(x)
        return self.foo_cache[x]
这是一种非常通用的缓存技术,唯一重要的区别是我们必须将状态存储在actor中

@ray.remote
class Foo:
    def __init__(self):
        self.foo_cache = {}

    def foo(self, x):
        def real_foo(x):
            print("executing foo with {}".format(x))
            time.sleep(0.1)
        if x not in self.foo_cache:
            self.foo_cache[x] = real_foo(x)
        return self.foo_cache[x]
更广义的方法 通过定义通用函数缓存,我们还可以将此方法推广到任何光线函数:

@ray.remote
class FunctionCache:
    def __init__(self, func):
        self.func = ray.remote(func)
        self.cache = {}

    def call(self, *args, **kwargs):
        if (args, kwargs) not in cache:
            cache[(args, kwargs)] = self.func(*args, **kwargs)
        return cache[(args, kwargs)]
然后,为了清理我们使用它的方式,我们可以定义一个装饰器:

class RemoteFunctionLookAlike:
    def __init__(self, func):
        self.func = func

    def remote(self, *args, **kwargs):
        return self.func(*args, **kwargs)


def ray_cache(func):
    cache = FunctionCache.remote(func)
    def call_with_cache(*args, **kwargs):
        return cache.call.remote(*args, **kwargs)
    return RayFunctionLookAlike(call_with_cache)
最后,要使用此缓存,请执行以下操作:

@ray_cache
def foo(x):
    print("Function called!")
    return abc

ray.get([foo.remote("constant") for _ in range(100)]) # Only prints "Function called!" once.

一般警告:如果函数产生副作用,缓存任意函数调用可能是危险的

在这种情况下,您可能希望程序输出

executing foo with 1 
executing foo with 2 
executing foo with 4 
您提到的其他缓存工具往往无法与Ray配合使用,因为它们试图以某种全局状态存储缓存,而不是将该状态存储在可以以分布式方式访问的位置。因为您已经有了一个actor,所以您可以将全局状态存储在actor中

@ray.remote
class Foo:
    def __init__(self):
        self.foo_cache = {}

    def foo(self, x):
        def real_foo(x):
            print("executing foo with {}".format(x))
            time.sleep(0.1)
        if x not in self.foo_cache:
            self.foo_cache[x] = real_foo(x)
        return self.foo_cache[x]
这是一种非常通用的缓存技术,唯一重要的区别是我们必须将状态存储在actor中

@ray.remote
class Foo:
    def __init__(self):
        self.foo_cache = {}

    def foo(self, x):
        def real_foo(x):
            print("executing foo with {}".format(x))
            time.sleep(0.1)
        if x not in self.foo_cache:
            self.foo_cache[x] = real_foo(x)
        return self.foo_cache[x]
更广义的方法 通过定义通用函数缓存,我们还可以将此方法推广到任何光线函数:

@ray.remote
class FunctionCache:
    def __init__(self, func):
        self.func = ray.remote(func)
        self.cache = {}

    def call(self, *args, **kwargs):
        if (args, kwargs) not in cache:
            cache[(args, kwargs)] = self.func(*args, **kwargs)
        return cache[(args, kwargs)]
然后,为了清理我们使用它的方式,我们可以定义一个装饰器:

class RemoteFunctionLookAlike:
    def __init__(self, func):
        self.func = func

    def remote(self, *args, **kwargs):
        return self.func(*args, **kwargs)


def ray_cache(func):
    cache = FunctionCache.remote(func)
    def call_with_cache(*args, **kwargs):
        return cache.call.remote(*args, **kwargs)
    return RayFunctionLookAlike(call_with_cache)
最后,要使用此缓存,请执行以下操作:

@ray_cache
def foo(x):
    print("Function called!")
    return abc

ray.get([foo.remote("constant") for _ in range(100)]) # Only prints "Function called!" once.

非常感谢你!我现在使用的是第一个简单的解决方案。我用的不是一本词典,而是一本非常好用的词典。非常感谢!我现在使用的是第一个简单的解决方案。我用的不是一本字典,而是一本非常好用的字典。