繁体   English   中英

使用Pipe在进程之间传输Python对象时的字节限制?

[英]Byte limit when transferring Python objects between Processes using a Pipe?

我有一个使用64位Python 3.3.0 CPython解释器在64位Linux(内核版本2.6.28.4)机器上运行的自定义模拟器(用于生物学)。

因为模拟器依赖于许多独立实验来获得有效结果,所以我建立了并行处理来运行实验。 线程之间的通信主要发生在具有托管multiprocessing Queuedoc )的生产者 - 消费者模式下。 该体系结构的破坏如下:

  • 处理产生和管理Process es以及各种Queue的主进程
  • N个工作进程进行模拟
  • 1结果消费者过程消耗模拟结果并对结果进行分类和分析

主进程和工作进程通过输入Queue进行通信。 类似地,工作进程将其结果放在输出Queue中,结果使用者进程使用该Queue中的项目。 最终的ResultConsumer对象通过multiprocessing Pipedoc )传递回主进程。

一切正常,直到它试图通过Pipe将ResultConsumer对象传递回主进程:

Traceback (most recent call last):
  File "/home/cmccorma/.local/lib/python3.3/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/home/cmccorma/.local/lib/python3.3/multiprocessing/process.py", line 95, in run
    self._target(*self._args, **self._kwargs)
  File "DomainArchitectureGenerator.py", line 93, in ResultsConsumerHandler
    pipeConn.send(resCon)
  File "/home/cmccorma/.local/lib/python3.3/multiprocessing/connection.py", line 207, in send
    self._send_bytes(buf.getbuffer())
  File "/home/cmccorma/.local/lib/python3.3/multiprocessing/connection.py", line 394, in _send_bytes
    self._send(struct.pack("!i", n))
struct.error: 'i' format requires -2147483648 <= number <= 2147483647

我理解前两个跟踪( Process库中未处理的出口),第三个是我将ResultConsumer对象沿Pipe发送到主进程的代码行。 最后两条痕迹是它变得有趣的地方。 Pipe会发送任何发送给它的对象,并将生成的字节传递给另一端(匹配连接),在运行recv()时,它会被取消激活。 self._send_bytes(buf.getbuffer())正在尝试发送pickle对象的字节。 self._send(struct.pack("!i", n))试图打包一个长度为n的整数(network / big-endian)的结构,其中n是作为参数传入的缓冲区的长度( struct库处理Python值和表示为Python字符串的C结构之间的转换,请参阅doc )。

只有在尝试大量实验时才会出现此错误,例如,10个实验不会导致它,但1000个将是有意义的(所有其他参数都是恒定的)。 到目前为止,为什么抛出struct.error我最好的假设是,尝试按下管道的字节数超过2 ^ 32-1(2147483647),或者大约2 GB。

所以我的问题是双重的:

  1. 我因为struct.py基本上只是从_struct而陷入调查,我不知道它在哪里。

  2. 鉴于底层架构都是64位,字节限制似乎是任意的。 那么,为什么我不能通过比这更大的东西? 另外,如果我无法改变这个问题,那么这个问题是否有任何好的(阅读:简单)解决方法?

注意:我不认为使用Queue代替Pipe会解决问题,因为我怀疑Queue使用类似的酸洗中间步骤。 编辑:正如abarnert的回答所指出的,这个说明是完全错误的。

我因为struct.py基本上只是从_struct导入而陷入调查,我不知道它在哪里。

在CPython中, _struct是一个C扩展模块, _struct是从源树中的Modules目录中的_struct.c构建的。 您可以在这里找到在线代码。

每当foo.py执行import _foo ,它几乎总是一个C扩展模块,通常是从_foo.c 如果你根本找不到foo.py ,它可能是一个C扩展模块,由_foomodule.c

即使你没有使用PyPy,它也经常值得查看等效的PyPy源代码 他们重新实现纯Python中的几乎所有扩展模块 - 对于其余的(包括本例),底层的“扩展语言”是RPython,而不是C.

但是,在这种情况下,您不需要了解struct如何工作超出文档中的内容。


鉴于底层架构都是64位,字节限制似乎是任意的。

看看它调用的代码:

self._send(struct.pack("!i", n))

如果查看文档'i'格式字符明确表示“4字节C整数”,而不是“ ssize_t是什么”。 为此,你必须使用'n' 或者您可能希望明确使用长的'q'

您可以使用monkeypatch multiprocessing来使用struct.pack('!q', n) '!q' 或者以struct之外的某种方式编码长度。 当然,这将破坏与非修补multiprocessing兼容性,如果您尝试跨多台计算机或其他东西进行分布式处理,这可能是一个问题。 但它应该很简单:

def _send_bytes(self, buf):
    # For wire compatibility with 3.2 and lower
    n = len(buf)
    self._send(struct.pack("!q", n)) # was !i
    # The condition is necessary to avoid "broken pipe" errors
    # when sending a 0-length buffer if the other end closed the pipe.
    if n > 0:
        self._send(buf)

def _recv_bytes(self, maxsize=None):
    buf = self._recv(8) # was 4
    size, = struct.unpack("!q", buf.getvalue()) # was !i
    if maxsize is not None and size > maxsize:
        return None
    return self._recv(size)

当然,不能保证这种变化是充分的; 你会想要阅读周围代码的其余部分并测试它的地狱。


注意:我怀疑使用Queue代替Pipe不会解决问题,因为我怀疑Queue使用类似的酸洗中间步骤。

嗯,问题与酸洗无关。 Pipe没有使用pickle发送长度,它使用struct 您可以验证pickle不会出现此问题: pickle.loads(pickle.dumps(1<<100)) == 1<<100将返回True

(在早期版本中, pickle 存在大型物体的问题 - 例如,2G元素的list - 这可能导致问题的规模大约是目前正在击中的那么高的8倍。但是已经修正了3.3。)

与此同时......尝试看看它并不是更快,而不是通过挖掘源来试图弄清楚它是否会起作用?


另外,你确定你真的想通过隐式酸洗来传递2GB的数据结构吗?

如果我做了一些缓慢而且需要内存的东西,我宁愿将其显式化 - 例如,pickle到tempfile并发送路径或fd。 (如果您正在使用numpypandas或其他东西,请使用其二进制文件格式而不是pickle ,但同样的想法。)

或者,更好的是,共享数据。 是的,可变共享状态很糟糕......但共享不可变对象很好。 无论你有2GB,你可以把它放在一个multiprocessing.Array ,或者把它放在一个ctypes数组或结构(数组或结构......)中你可以通过multiprocessing.sharedctypes共享,或者ctypes它是一个你双面mmap file ,还是......? 有一些额外的代码来定义和分离结构,但是当这些好处可能很大时,值得尝试。


最后,当您认为在Python中发现了一个错误/明显缺失的功能/不合理的限制时,值得查看错误跟踪器。 看起来像问题17560:使用多处理真正大对象的问题? 这正是你的问题,并有很多信息,包括建议的解决方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM