Python numpy与多处理和mmap

Python numpy与多处理和mmap,python,numpy,multiprocessing,mmap,Python,Numpy,Multiprocessing,Mmap,我正在使用Python的多处理模块并行处理大型numpy数组。在主进程中使用numpy.load(mmap\u mode='r')对数组进行内存映射。在这之后,multiprocessing.Pool()fork进程(我猜) 一切似乎都很好,除了我听到的台词如下: AttributeError(“'NoneType'对象没有属性'tell'”) 在`` 忽略 在unittest日志中。尽管如此,测试还是通过得很好 知道那里发生了什么吗 使用Python 2.7.2、OS X、NumPy 1.6

我正在使用Python的
多处理
模块并行处理大型numpy数组。在主进程中使用
numpy.load(mmap\u mode='r')
对数组进行内存映射。在这之后,
multiprocessing.Pool()
fork进程(我猜)

一切似乎都很好,除了我听到的台词如下:

AttributeError(“'NoneType'对象没有属性'tell'”)
在``
忽略
在unittest日志中。尽管如此,测试还是通过得很好

知道那里发生了什么吗

使用Python 2.7.2、OS X、NumPy 1.6.1


更新:

经过一些调试后,我找到了一个代码路径的原因,该路径使用了这个内存映射的numpy数组的一小部分作为
Pool.imap
调用的输入

显然,“问题”在于
multiprocessing.Pool.imap
将其输入传递给新进程的方式:它使用pickle。这不适用于
mmap
ed numpy数组,内部的某些内容会导致错误

我发现了罗伯特·克恩的作品,它似乎解决了同样的问题。他建议为
imap
输入来自内存映射数组时创建一个特殊的代码路径:内存在派生过程中手动映射同一数组

这将是如此复杂和丑陋,我宁愿生活在错误和额外的内存副本。有没有其他方法可以更轻松地修改现有代码?

我通常的方法(如果您可以使用额外的内存拷贝)是在一个进程中完成所有IO,然后将内容发送到工作线程池。要将memmapped数组的一个片段加载到内存中,只需执行
x=np.array(data[yoursicle])
data[yoursicle].copy()
实际上不会执行此操作,这可能会导致一些混乱。)

首先,让我们生成一些测试数据:

import numpy as np
np.random.random(10000).tofile('data.dat')
您可以通过以下方式重现错误:

import numpy as np
import multiprocessing

def main():
    data = np.memmap('data.dat', dtype=np.float, mode='r')
    pool = multiprocessing.Pool()
    results = pool.imap(calculation, chunks(data))
    results = np.fromiter(results, dtype=np.float)

def chunks(data, chunksize=100):
    """Overly-simple chunker..."""
    intervals = range(0, data.size, chunksize) + [None]
    for start, stop in zip(intervals[:-1], intervals[1:]):
        yield data[start:stop]

def calculation(chunk):
    """Dummy calculation."""
    return chunk.mean() - chunk.std()

if __name__ == '__main__':
    main()
如果您只是切换到generating
np.array(data[start:stop])
,您将解决这个问题:

import numpy as np
import multiprocessing

def main():
    data = np.memmap('data.dat', dtype=np.float, mode='r')
    pool = multiprocessing.Pool()
    results = pool.imap(calculation, chunks(data))
    results = np.fromiter(results, dtype=np.float)

def chunks(data, chunksize=100):
    """Overly-simple chunker..."""
    intervals = range(0, data.size, chunksize) + [None]
    for start, stop in zip(intervals[:-1], intervals[1:]):
        yield np.array(data[start:stop])

def calculation(chunk):
    """Dummy calculation."""
    return chunk.mean() - chunk.std()

if __name__ == '__main__':
    main()
当然,这会为每个块创建一个额外的内存副本

从长远来看,您可能会发现从memmapped文件切换到类似HDF的文件更容易。如果您的数据是多维的,则尤其如此。(我建议使用
h5py
,但是如果您的数据是“类似于表的”,那么
pyTables
就很好了。)


无论如何,祝你好运

乔:你的答案总是那么棒。我只是想弄明白,谢谢你的HDF提示。看起来是一个巨大的变化,但它可能是值得的,我来看看。HDF5在多处理环境中也有类似的问题。他们有一些基于mpi的解决方案,但imho是重量级的。检查