2013-02-07 53 views
8

我已经writen,可以总结如下的程序:的Python多的内存使用情况

def loadHugeData(): 
    #load it 
    return data 

def processHugeData(data, res_queue): 
    for item in data: 
     #process it 
     res_queue.put(result) 
    res_queue.put("END") 

def writeOutput(outFile, res_queue): 
    with open(outFile, 'w') as f 
     res=res_queue.get() 
     while res!='END': 
      f.write(res) 
      res=res_queue.get() 

res_queue = multiprocessing.Queue() 

if __name__ == '__main__': 
    data=loadHugeData() 
    p = multiprocessing.Process(target=writeOutput, args=(outFile, res_queue)) 
    p.start() 
    processHugeData(data, res_queue) 
    p.join() 

真正的代码(尤其是'writeOutput()')是一个复杂得多。 'writeOutput()'只使用这些值作为它的参数(意思是它没有引用对象)

基本上,它将一个巨大的数据集加载到内存中并对其进行处理。写入输出被委托给一个子进程(它实际上写入多个文件,这需要很多时间)。 因此,每次处理一个数据项时,都会将其发送到子进程槽res_queue,然后根据需要将结果写入文件。

子进程不需要以任何方式访问,读取或修改由'loadHugeData()'加载的数据。子流程只需要使用主流程通过'res_queue'发送的内容。这导致我的问题和疑问。

在我看来,子进程得到它的巨大数据集的副本(当检查内存使用'顶')。这是真的?如果是这样,那么我怎么能避免id(本质上使用双内存)?

我正在使用Python 2.6并且程序在linux上运行。

+0

你可以重构你的代码来使用迭代器,而不是加载所有loadHugeData?看起来你可以,如果它看起来像加载/进程/入队/出队/写 – sotapme

+0

“hugeData”不幸是一个制表符分隔的txt文件,基本上包含一个稀疏的数组。我需要在处理过程中根据行号“随机访问”这些数据。因此将其加载到内存中(使用稀疏阵列特定的优化)可以使处理速度更快。 – FableBlaze

+0

这可能是大规模的过度工程,建议使用'[beanstalkd](https://github.com/earl/beanstalkc/blob/master/TUTORIAL.mkd)之类的东西进行流程整合,但知道知道如果它帮助/缩放/执行。像往常一样,其他人的问题总是更有趣。 – sotapme

回答

13

multiprocessing模块实际上基于系统调用fork,该系统调用创建当前进程的副本。由于您在加载fork(或创建multiprocessing.Process)之前的大量数据,子进程会继承数据的副本。但是,如果您正在运行的操作系统实现COW(写时复制),则实际上只有物理内存中的数据的一个副本,除非您修改父进程或子进程中的数据(父母和孩子都将共享相同的物理内存页面,尽管在不同的虚拟地址空间中);即使如此,只会为更改分配额外内存(以pagesize为增量)。

在加载大量数据之前,您可以通过调用multiprocessing.Process来避免这种情况。然后,当您在父级中加载数据时,额外的内存分配将不会反映在子进程中。

+1

比我做得好。 Linux是COW,所以父进程写入数据的时刻,数据将被复制。如果父进程只读取数据,那么只有一个数据实例** BUT ** top(我几乎可以肯定)会显示数据属于这两个进程。 meminfo应该为内存使用提供更准确的数字。 –

+0

确实。我认为现在最常见的操作系统是COW(我只是试图尽可能通用)。很棒的功能,但是在解释基于进程的内存报告工具(例如top,ps等)的输出时经常会引起混淆。Linux上的'meminfo'将正确报告,就像Solaris上的'pmap'一样;没有关于Windows的想法:) – isedev

+5

还要注意,每个Python对象都包含一个引用计数,只要访问该对象就会被修改。所以,只要读取数据结构就可以导致COW复制。 –