使用Python的multiprocessing.pool,内存使用量不断增长

时间:2013-08-24 01:28:16

标签: python memory multiprocessing pool

以下是该计划:

#!/usr/bin/python

import multiprocessing

def dummy_func(r):
    pass

def worker():
    pass

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=16)
    for index in range(0,100000):
        pool.apply_async(worker, callback=dummy_func)

    # clean up
    pool.close()
    pool.join()

我发现内存使用(包括VIRT和RES)一直在增长,直到close()/ join(),有没有解决方案可以解决这个问题?我用2.7尝试了maxtasksperchild,但它也没有帮助。

我有一个更复杂的程序,调用apply_async()〜6M次,并且在~1.5M点我已经有6G + RES,为了避免所有其他因素,我将程序简化为以上版本。

修改

原来这个版本效果更好,感谢大家的意见:

#!/usr/bin/python

import multiprocessing

ready_list = []
def dummy_func(index):
    global ready_list
    ready_list.append(index)

def worker(index):
    return index

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=16)
    result = {}
    for index in range(0,1000000):
        result[index] = (pool.apply_async(worker, (index,), callback=dummy_func))
        for ready in ready_list:
            result[ready].wait()
            del result[ready]
        ready_list = []

    # clean up
    pool.close()
    pool.join()

我没有放任何锁,因为我认为主进程是单线程的(回调或多或少像我读过的每个文档的事件驱动的东西)。

我将v1的索引范围更改为1,000,000,与v2相同并进行了一些测试 - 这对我来说很奇怪v2甚至比v1快〜10%(33s vs 37s),也许v1做了太多的内部列表维护工作。 v2绝对是内存使用的赢家,它从未超过300M(VIRT)和50M(RES),而v1曾经是370M / 120M,最好的是330M / 85M。所有数字仅为3~4次测试,仅供参考。

5 个答案:

答案 0 :(得分:18)

我最近遇到了内存问题,因为我多次使用多处理功能,所以它不断产生进程,并将它们留在内存中。

以下是我现在使用的解决方案:

def myParallelProcess(ahugearray)
 from multiprocessing import Pool
 from contextlib import closing
 with closing( Pool(15) ) as p:
    res = p.imap_unordered(simple_matching, ahugearray, 100)
 return res

我❤

答案 1 :(得分:6)

使用map_async代替apply_async以避免过多的内存使用。

对于您的第一个示例,请更改以下两行:

for index in range(0,100000):
    pool.apply_async(worker, callback=dummy_func)

pool.map_async(worker, range(100000), callback=dummy_func)

它会在top中看到其内存使用情况之前闪烁。将列表更改为更大的列表以查看差异。但是注意map_async将首先将传递给它的可迭代转换为列表,以便在没有__len__方法的情况下计算其长度。如果你有一个包含大量元素的迭代器,你可以使用itertools.islice以较小的块来处理它们。

我在现实生活中有更多数据存在记忆问题,最后发现罪魁祸首是apply_async

P.S。,就内存使用而言,你的两个例子没有明显的区别。

答案 2 :(得分:5)

只需在循环中创建池,然后在循环结束时关闭它 pool.close()

答案 3 :(得分:4)

我正在处理一个非常大的三维点云数据集。我尝试使用多处理模块来加速处理,但我开始出现内存错误。经过一些研究和测试后,我确定我正在填充要处理的任务队列,这比子进程可以清空它要快得多。我确定通过分块,或使用map_async或其他我可以调整负载的东西,但我不想对周围的逻辑做出重大改变。

我遇到的愚蠢的解决方案是间歇性地检查pool._cache长度,如果缓存太大,则等待队列清空。

在我的主循环中,我已经有了一个计数器和一个状态代码:

# Update status
count += 1
if count%10000 == 0:
    sys.stdout.write('.')
    if len(pool._cache) > 1e6:
        print "waiting for cache to clear..."
        last.wait() # Where last is assigned the latest ApplyResult

因此,每隔10k插入池中,我检查是否有超过100万个操作排队(主进程中使用的内存大约为1G)。当队列已满时,我只等待最后插入的作业完成。

现在我的程序可以运行几个小时而不会耗尽内存。当工人继续处理数据时,主要过程偶尔会暂停。

BTW _cache成员记录在多处理模块池示例中:

#
# Check there are no outstanding tasks
#

assert not pool._cache, 'cache = %r' % pool._cache

答案 4 :(得分:1)

我认为这与the question I posted类似,但我不确定您是否有同样的延迟。我的问题是我从多处理池生成的结果比我消耗它们的速度更快,因此它们在内存中构建。为了避免这种情况,我使用了semaphore来限制池中的输入,因此它们并没有超出我正在消耗的输出。