【问题标题】:How to share a cache between multiple processes?如何在多个进程之间共享缓存?
【发布时间】:2021-01-22 07:41:54
【问题描述】:

我正在使用a LRU cache 来加快一些相当繁重的处理。它运行良好并且大大加快了速度。不过……

当我使用多进程时,每个进程都会创建自己的单独缓存,并且同一事物有 8 个副本。这似乎不是问题,直到盒子内存不足并因此发生坏事......

理想情况下,我的应用程序只需要大约 300 个项目的缓存大小,1*300 将适合我必须使用的 7GB,但 8*300 不适合。

如何让所有进程共享同一个缓存?

【问题讨论】:

  • Python multiprocessing 模块在 sharing state between processes 上有一个部分。
  • 您可以选择进程外缓存吗?您可以在缓存状态上使用 pickle 并将其存储在 redis 中吗?
  • @sam 实际上,Manager 似乎可以共享dict,我怀疑这是内部的 lfu 缓存。我想我希望以前有人破解过这个问题来解决这个问题,所以我不必这样做;-)
  • @TokenMacGuy 喜欢你的想法,但它的强度很高(每毫秒数千次),所以提到酸洗让我立即预判它太慢了。
  • 有没有办法用 gunicorn + bottle 启动的不同进程来做到这一点?

标签: python multiprocessing


【解决方案1】:

我相信您可以使用Manager 在进程之间共享字典。理论上,这应该让您对所有功能使用相同的缓存。

但是,我认为更明智的逻辑是让 一个 进程通过在缓存中查找查询来响应查询,如果它们不存在,则将工作委托给子进程,并且在返回结果之前缓存结果。你可以很容易地做到这一点

with concurrent.futures.ProcessPoolExecutor() as e:
    @functools.lru_cache
    def work(*args, **kwargs):
        return e.submit(slow_work, *args, **kwargs)

注意work 将返回Future 对象,消费者必须等待。 lru_cache 将缓存未来的对象,以便它们自动返回;我相信您可以多次访问他们的数据,但现在无法对其进行测试。

如果您不使用 Python 3,则必须安装 concurrent.futuresfunctools.lru_cache 的反向移植版本。

【讨论】:

    【解决方案2】:

    将共享缓存传递给每个进程。父进程可以实例化单个缓存并将其作为参数引用到每个进程...

    @utils.lru_cache(maxsize=300)
    def get_stuff(key):
        """This is the routine that does the stuff which can be cached.
        """
        return Stuff(key)
    
    def process(stuff_obj):
        """This is the routine which multiple processes call to do work with that Stuff
        """
        # get_stuff(key) <-- Wrong; I was calling the cache from here
        stuff_obj.execute()
    
    def iterate_stuff(keys):
        """This generates work for the processses.
        """
        for key in keys:
            yield get_stuff(key)  # <-- I can call the cache from the parent
    
    def main():
        ...
        keys = get_list_of_keys()
        for result in pool.imap(process, iterate_stuff(keys)):
             evaluate(result)
        ...
    

    这个例子很简单,因为我可以在调用进程之前查找缓存。有些场景可能更喜欢将指针传递给缓存而不是值。例如:

            yield (key, get_stuff)
    

    Katriel 让我走上了正确的道路,我会执行那个答案,但是,愚蠢的我,我的错误比他建议的更容易解决。

    【讨论】:

    • 这只会在get_stuff(key)返回小对象时提高性能,将大对象传递给子进程需要很长时间。
    猜你喜欢
    • 2021-02-25
    • 1970-01-01
    • 2012-07-22
    • 2010-10-10
    • 1970-01-01
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    相关资源
    最近更新 更多