我在python代码中使用多处理来异步运行一个函数:
import multiprocessing
po = multiprocessing.Pool()
for elements in a_list:
results.append(po.apply_async(my_module.my_function, (some_arguments, elements, a_big_argument)))
po.close()
po.join()
for r in results:
a_new_list.add(r.get())
a_big_argument
是一本字典。我把它作为一个论点。它在10到100 Mo之间的意义上很大。它似乎对我的代码的性能有很大的影响。
我可能在这里做了一些愚蠢而且效率不高的事情,因为我的代码的性能确实因这个新论点而失败。
处理大字典的最佳方法是什么?我不想每次都在我的函数中加载它。它是一个创建数据库并连接到它的解决方案吗?
以下是您可以运行的代码:
'''
Created on Mar 11, 2013
@author: Antonin
'''
import multiprocessing
import random
# generate an artificially big dictionary
def generateBigDict():
myBigDict = {}
for key in range (0,1000000):
myBigDict[key] = 1
return myBigDict
def myMainFunction():
# load the dictionary
myBigDict = generateBigDict()
# create a list on which we will asynchronously run the subfunction
myList = []
for list_element in range(0,20):
myList.append(random.randrange(0,1000000))
# an empty set to receive results
set_of_results = set()
# there is a for loop here on one of the arguments
for loop_element in range(0,150):
results = []
# asynchronoulsy run the subfunction
po = multiprocessing.Pool()
for list_element in myList:
results.append(po.apply_async(mySubFunction, (loop_element, list_element, myBigDict)))
po.close()
po.join()
for r in results:
set_of_results.add(r.get())
for element in set_of_results:
print element
def mySubFunction(loop_element, list_element, myBigDict):
import math
intermediaryResult = myBigDict[list_element]
finalResult = intermediaryResult + loop_element
return math.log(finalResult)
if __name__ == '__main__':
myMainFunction()
答案 0 :(得分:3)
import multiprocessing
manager = multiprocessing.Manager()
a_shared_big_dictionary = manager.dict(a_big_dictionary)
po = multiprocessing.Pool()
for elements in a_list:
results.append(po.apply_async(my_module.my_function, (some_arguments, elements, a_shared_big_dictionary)))
po.close()
po.join()
for r in results:
a_new_list.add(r.get())
现在,它要快得多。
答案 1 :(得分:1)
查看Shared-memory objects in python multiprocessing问题的答案。
它建议使用multiprocessing.Array将数组传递给子进程或使用fork()。
答案 2 :(得分:1)
您传递给Pool
方法之一的任何参数(例如apply_async
)都需要进行pickle,通过管道发送到工作进程,并在工作进程中进行unpickled。这个pickle / pass / unpickle进程在时间和内存上都很昂贵,特别是如果你有一个大的对象图,因为每个工作进程必须创建一个单独的副本。
根据问题的确切形状,有许多不同的方法可以避免这些泡菜。由于您的工作人员只是正在阅读您的字典并且没有写入字典,因此您可以直接从您的函数中引用它(即不将其传递给apply_async
)并依赖fork()
避免在工作进程中创建副本。
更好的是,您可以更改mySubFunction()
,使其接受intermediaryResult
作为参数,而不是使用list_element
和myBigDict
进行查找。 (您可以使用闭包来执行此操作,但我并非100%确定pickle也不会尝试复制已关闭的myBigDict
对象。)
或者,您可以将myBigDict
放在所有进程可以安全共享的位置,例如one of the simple persistance methods, such as dbm or sqlite,让工作人员从那里访问它。
不幸的是,所有这些解决方案都要求您更改任务功能的形状。避免这种“改变形状”是人们喜欢“真正的”cpu线程的一个原因。