2012-01-08 21 views
3

我加载在Python脚本12个XML文件(30-80MB每个):与multiprocessing.Pool内存泄露甚至收盘后()

import xml.etree.ElementTree as ET 
files = ['1.xml', '2.xml', ..., '11.xml', '12.xml'] 
trees = [ET.parse(f) for f in files] 

这需要大约50秒的运行。我会跑了几次,所以我想我会尝试多以加快其速度:

import multiprocessing  
trees = [None] * len(files) 

def _parse_(i): 
    return (i, ET.parse(files[i])) 

def _save_((i, tree)): 
    trees[i] = tree 

def concurrent_parse(): 
    pool = multiprocessing.Pool() 
    for i in range(len(files)): 
     pool.apply_async(func=_parse_, args=(i,), callback=_save_) 
    pool.close() 
    pool.join() 

这现在运行在30多岁,这是一个很好的改善。但是,我正在从shell运行所有这些,然后交互式地处理数据。在第一个非并发版本完成之后,Python的内存使用率降至1.73GB。并发之后,内存使用量为2.57GB。

我是使用多处理器的新手,所以请原谅我,如果我错过了一些基本的东西。但是使用Pool之后失去内存的所有其他问题都指向我正在执行的close()失败。

PS - 如果这是一种非常愚蠢的方式来加载12个XML文件,请随时这么说。

+1

我明白这个问题是关于'multiprocessing',它对我很感兴趣(upvoted和订阅)。但是如果可以的话,请考虑使用'lxml.etree'。我有4个生成的测试文件,每个20 MB。测试结果'lxml/xml'(没有多重处理):时间 - 1.47/27.95秒;内存 - 411/640 MB。 – reclosedev 2012-01-08 14:29:28

回答

2

我不确定这实际上是一个泄漏,并行实现将需要更多的内存来同时保存所有文件。然后python可能会删除对象,但不会将内存返回到操作系统,这将看起来像使用更多的内存比现有的对象所需的内存。
那么如果你多次运行concurrent_parse()会发生什么?如果内存使用量不变,那么这不是泄漏。如果内存在每次运行后都上升,那么这是一个问题,您可能需要查看此线程以获取有关跟踪泄漏的信息 - Python memory leaks

+0

这是一个吸引人的解释,但我并不完全相信这些文件是由单独的Python进程同时保存的,因此解析中使用的内存应该返回到操作系统。 重新运行concurrent_parse()将我的机器停下来(我给它约10分钟),因为内存最大化,它开始分页所有内容。如果我重新运行它,但只有2-4个文件,那么内存似乎稳定在2GB左右。但是,重新运行4-6个文件有时可以正常工作,其他时间会达到内存限制。 无论哪种方式,'multiprocessing'可能不是我期待的魔法弹! – 2012-01-09 09:48:21

+0

您是否将所有树都重置为None?正如我已经发现的,这很重要,因为子进程从主进程中获取对象的副本,所以如果您的树有大量数据乘以进程数量。经过一番实验后,只要运行之间的树重置(至少在CentOS 5上使用python 2.7),重复运行concurrent_parse()后,内存看起来没有增加。我猜想使用多进程时增加的内存使用量是由于IPC序列化造成的。 – user1013341 2012-01-10 17:14:05

+0

嗯,我明白你的意思。我认为你的答案可能是正确的,因为它不是内存泄漏。但是我并不完全满意为什么当所有的实例拷贝进入不同的进程时,原始进程为什么最终会使用额外的700MB内存。无论哪种方式,我都会放弃它,因为Python垃圾系统超出了问题的范围。谢谢! – 2012-01-12 21:30:02