使用Python多处理池时系统内存不足?

时间:2016-05-01 16:38:09

标签: python parallel-processing ipython python-multiprocessing

我正在尝试并行化我的代码,以便在Python中使用多处理模块找到相似性矩阵。当我使用带有10 X 15元素的小np.ndarray时,它工作正常。但是,当我将我的np.ndarray扩展到3613 X 7040元素时,系统会耗尽内存。

下面是我的代码。

import multiprocessing 
from multiprocessing import Pool
## Importing Jacard_similarity_score
from sklearn.metrics import jaccard_similarity_score

# Function for finding the similarities between two np arrays
def similarityMetric(a,b):
    return (jaccard_similarity_score(a,b))

## Below functions are used for Parallelizing the scripts
 # auxiliary funciton to make it work
def product_helper1(args):
    return (similarityMetric(*args))

def parallel_product1(list_a, list_b):
    # spark given number of processes
    p = Pool(8) 
    # set each matching item into a tuple
    job_args = getArguments(list_a,list_b)    
    # map to pool
    results = p.map(product_helper1, job_args)
    p.close()
    p.join()
    return (results)

## getArguments function is used to get the combined list 
def getArguments(list_a,list_b):
    arguments = []
    for i in list_a:
        for j in list_b:
            item = (i,j)
            arguments.append(item)
    return (arguments)

现在,当我运行以下代码时,系统会耗尽内存并被挂起。我正在传递两个numpy.ndarrays testMatrix1和testMatrix2,它们的大小(3613,7040)

resultantMatrix = parallel_product1(testMatrix1,testMatrix2)

我是新手在Python中使用这个模块并试图了解我的错误。任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:2)

可能性很大,问题只是组合爆炸。您试图预先在主进程中实现所有对,而不是直接生成它们,因此您需要存储大量内存。假设ndarray包含double值,这些值变为Python float,那么list返回的getArguments的内存使用量大致是tuple的成本。每对{{}}和两个float,或约:

3613 * 7040 * (sys.getsizeof((0., 0.)) + sys.getsizeof(0.) * 2)

在我的64位Linux系统上,这意味着在Py3上大约2.65 GB的RAM,或在Py2上大约2.85 GB,然后工人们才会做任何事情。

如果您可以使用生成器以流方式处理数据,那么参数会在不再需要时延迟生成并丢弃,您可能会大大减少内存使用量:

import itertools

def parallel_product1(list_a, list_b):
    # spark given number of processes
    p = Pool(8) 
    # set each matching item into a tuple
    # Returns a generator that lazily produces the tuples
    job_args = itertools.product(list_a,list_b)    
    # map to pool
    results = p.map(product_helper1, job_args)
    p.close()
    p.join()
    return (results)

这仍然要求所有结果都适合记忆;如果product_helper返回float s,则64位计算机上result list的预期内存使用量仍然约为0.75 GB左右,这相当大;如果你可以以流方式处理结果,迭代p.imap或更好的结果,p.imap_unordered(后者返回计算结果,而不是生成器生成参数的顺序)并写入它们磁盘或以其他方式确保它们在内存中快速释放会节省大量内存;以下只是将它们打印出来,但是以一些可重新格式化的格式将它们写入文件也是合理的。

def parallel_product1(list_a, list_b):
    # spark given number of processes
    p = Pool(8) 
    # set each matching item into a tuple
    # Returns a generator that lazily produces the tuples
    job_args = itertools.product(list_a,list_b)    
    # map to pool
    for result in p.imap_unordered(product_helper1, job_args):
        print(result)
    p.close()
    p.join()

答案 1 :(得分:0)

map方法通过进程间通信将所有数据发送给worker。如目前所做,这会消耗大量资源,因为您正在发送

我建议修改getArguments以在矩阵中列出需要合并的索引的元组。这只是两个必须发送到工作进程的数字,而不是两行矩阵!然后,每个工作人员都知道要使用矩阵中的哪些行。

在调用map之前加载两个矩阵并将它们存储在全局变量中。这样每个工作者都可以访问它们。只要他们没有在工作人员中进行修改,操作系统的虚拟内存管理器就不会复制相同的内存页面,从而降低内存使用率。