【问题标题】:Shared memory between python processespython进程之间的共享内存
【发布时间】:2012-07-03 09:32:33
【问题描述】:

我正在尝试找出一种在 python 进程之间共享内存的方法。基本上存在多个python进程需要能够读取(仅读取)和使用(无突变)的对象。现在这是使用 redis + strings + cPickle 实现的,但是 cPickle 占用了宝贵的 CPU 时间,所以我不想使用它。我在互联网上看到的大多数 python 共享内存实现似乎都需要文件和泡菜,这基本上是我已经在做的并且正是我想要避免的。

我想知道是否有办法编写类似...基本上是内存中的python对象数据库/服务器和相应的C模块来与数据库接口?

基本上,C 模块会向服务器询问要写入对象的地址,服务器会以地址响应,然后模块会写入对象,并通知服务器具有给定键的对象已写入磁盘在指定位置。然后,当任何进程想要使用给定键检索对象时,它们只需向数据库询问给定键的内存位置,服务器将响应该位置,模块将知道如何在内存中加载该空间并将python对象传回python进程。

这完全不合理还是真的很难实施?我在追求不可能的事情吗?欢迎大家提出意见。感谢互联网。

【问题讨论】:

  • 您的 CPU 时间到底有多么宝贵,值得放弃一个工作解决方案,该解决方案在保持同步方面比您建议的要简单得多?您所要求的可以完成,但要做到正确将是一个巨大的痛苦。
  • CPU时间是最宝贵的。基本上 unpickling 对象可能需要 20 毫秒(对于小对象)到 60 毫秒(对于大对象)。我个人觉得这两个时间都太长了。编辑:太长了,因为必须有更好的方法,而不是我认为 cPickle 不够努力。
  • 共享内存是可行的,但共享对象将非常困难......可以在这里找到一个相关的问题:*.com/questions/1268252/…(Alex Martelli 的一篇很好的文章解释了为什么这很难)。
  • @f34r 是否已知酸洗是您当前代码库中的主要瓶颈或至少是一个重要的瓶颈?如果没有,您的 CPU 时间实际上供应充足。如果您的数据不是真正持久的,并且仅直接发送腌制值来替换它,您也许可以提出转储 Redis 的理由。但我的直觉是,如果没有序列化消息,就不可能有一个“无共享”的分布式架构,而且它们比共享内存系统更容易推理。
  • 我想你也可以看看sharing mechanisms of the multiprocessing module。 (该段的第一句话当然建议不要这样做。)

标签: python c shared-memory


【解决方案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()

【讨论】:

    【解决方案2】:

    从 Python 3.8 起,您可以使用 multiprocessing.shared_memory.SharedMemory

    【讨论】:

      【解决方案3】:

      如果您不想酸洗,multiprocessing.sharedctypes 可能适合。不过,这有点低级。你得到单个值或指定类型的数组。

      将数据分发给子进程的另一种方式(一种方式)是multiprocessing.Pipe。它可以处理 Python 对象,并且它是用 C 实现的,所以我不能告诉你它是否使用酸洗。

      【讨论】:

        【解决方案4】:

        不无道理。

        IPC 可以通过内存映射文件来完成。 Python 具有内置功能:

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

        只需在两个进程中映射文件,嘿-presto 你有一个共享文件。当然,您必须在两个进程中轮询它以查看发生了什么变化。而且您必须在两者之间进行合作写入。并决定您要将数据放入的格式。但这是解决您问题的常见方法。

        【讨论】:

        • 但这仍然需要序列化为字节,是吗? OP 说他试图避免这种情况。
        • 这会是某种序列化,是的。如果对象类型已知,自定义序列化可能会做得更好。或者包括一个哈希码以避免重复反序列化一个对象两次。不管怎样,都需要序列化。
        • 你确定吗?网络 + redis 会比较贵。为什么不对其进行剖析?
        • 本机运行Redis,不是网络redis服务器
        • 不过,您是通过域套接字或本地 TCP 套接字进行的,对吧?