Python进程之间的共享内存

18
我正在尝试找到一种在Python进程之间共享内存的方法。基本上,有些对象存在,多个Python进程需要能够读取(仅限读取)和使用(不可变)。目前,这是通过redis +字符串+cPickle实现的,但cPickle占用了宝贵的CPU时间,所以我不想使用它。我在互联网上看到的大多数Python共享内存实现似乎需要文件和pickle,这基本上就是我已经在做的事情,也正是我试图避免的。

我想知道是否有一种方法来编写类似于内存中的Python对象数据库/服务器以及相应的C模块来与数据库进行交互?

基本上,C模块将请求服务器提供要写入对象的地址,服务器将返回一个地址,然后模块将写入对象,并通知服务器给定键的对象已在指定位置写入磁盘。 然后,当任何进程需要检索给定键的对象时,它们只需向数据库询问该键的内存位置,服务器会响应位置,而模块将知道如何加载该内存空间并将Python对象传输回Python进程。

这是否完全不合理或者实现起来非常困难?我是在追求不可能的东西吗?欢迎任何建议。谢谢互联网。


2
你的 CPU 时间到底有多宝贵,以至于值得放弃一个比你提出的方案更不容易同步的工作解决方案?你所要求的可以做到,但要做对却是一件极其痛苦的事情。 - millimoose
CPU时间是最宝贵的。基本上,反序列化对象可能需要20毫秒(对于小对象)到60毫秒(对于大对象)。我个人认为这两个时间都太长了。编辑:太长的意思是必须有更好的方法,而不是我认为cPickle没有尽力。 - nickneedsaname
共享内存是可行的,但共享对象将会非常困难... 相关问题可以在这里找到:https://dev59.com/IXM_5IYBdhLWcg3wslbs(有一个很好的Alex Martelli的解释为什么这很难)。 - ChristopheD
@f34r 你的代码库中是否已知 pickling 是主要或至少是一个重要的瓶颈?如果不是,那么你的 CPU 时间实际上是充足的。如果你的数据并不真正需要持久化,你可能可以考虑放弃 Redis,并用直接发送 pickled 值来替换它。但我的直觉告诉我,你不能在没有序列化消息的情况下拥有“无共享”的分布式架构,而且它们比共享内存系统更容易理解。 - millimoose
我想你也可以研究一下multiprocessing模块的共享机制。当然,该段落的第一句话建议不要这样做。 - millimoose
4个回答

10
从Python 3.8开始,您可以使用multiprocessing.shared_memory.SharedMemory。请参阅文档了解更多信息。

6

并不是不合理的想法。

使用内存映射文件即可进行IPC。Python内置了此功能:

http://docs.python.org/library/mmap.html

只需在两个进程中都映射该文件,就可以共享文件了。当然,您需要在两个进程中轮询以查看更改。您还需要协调两者之间的写入,并决定要将数据放入哪种格式中。但这是解决您问题的常见方法。


8
但这仍需要将其串行化为字节序列,是吗?原帖的作者说他想要避免这种情况。 - Ned Batchelder
这将是某种序列化,是的。如果已知对象类型,则自定义序列化可能会更好地完成工作。或者包括哈希代码以避免两次重新序列化对象。无论如何,都需要进行序列化。 - Joe
你确定吗?Network+Redis会比较昂贵,为什么不进行性能分析? - Joe
Redis正在机器上运行,它不是一个网络redis服务器。 - nickneedsaname
但是,你要么在域套接字上进行操作,要么在本地TCP套接字上进行操作,对吧? - Joe

3
如果你不想使用pickle,那么可能更适合。但它有些低级,你只能得到指定类型的单个值或数组。
另一种向子进程分发数据的方式(一种方式)是。它可以处理Python对象,并且它是用C实现的,所以我无法告诉你它是否使用了pickle。

1

Python 不支持在独立进程间共享内存。你可以用C语言实现自己的共享内存,或者使用 SharedArray 如果你正在使用 libsvm、numpy.ndarray 或者 scipy.sparse。

pip install SharedArray

def test ():
    def generateArray ():
        print('generating')
        from time import sleep
        sleep(3)
        return np.ones(1000)
    a = Sarr('test/1', generateArray)

    # use same memory as a, also work in a new process
    b = Sarr('test/1', generateArray) 
    c = Sarr('test/1', generateArray)

import re
import SharedArray
import numpy as np

class Sarr (np.ndarray):
    def __new__ (self, name, getData):
        if not callable(getData) and getData is None:
            return None
        self.orig_name = name
        shm_name = 'shm://' + re.sub(r'[./]', '_', name)
        try:
            shm = SharedArray.attach(shm_name)
            print('[done] reuse shared memory:', name)
            return shm
        except Exception as err:
            self._unlink(shm_name)
            data = getData() if callable(getData) else getData
            shm = SharedArray.create(shm_name, data.size)
            shm[:] = data[:]
            print('[done] loaded data to shared memory:', name)
            return shm

    def _unlink (name):
        try:
            SharedArray.delete(name[len('shm://'):])
            print('deleted shared memory:', name)
        except:
            pass


if __name__ == '__main__':
    test()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接