python的新手,我想在下面的代码中进行并行编程,并希望在python中使用多处理来完成它。那么如何修改代码呢?我一直在使用Pool搜索方法,但发现了我可以遵循的有限示例。有人可以帮帮我吗?谢谢。
请注意,setinner和setouter是两个独立的函数,我想使用并行编程来减少运行时间。
def solve(Q,G,n):
i = 0
tol = 10**-4
while i < 1000:
inneropt,partition,x = setinner(Q,G,n)
outeropt = setouter(Q,G,n)
if (outeropt - inneropt)/(1 + abs(outeropt) + abs(inneropt)) < tol:
break
node1 = partition[0]
node2 = partition[1]
G = updateGraph(G,node1,node2)
if i == 999:
print "Maximum iteration reaches"
print inneropt
答案 0 :(得分:1)
很难并行化需要改变来自不同任务的相同共享数据的代码。所以,我将假设setinner
和setouter
是非变异函数;如果那不是真的,情况会更复杂。
第一步是决定你想要并行做什么。
一个显而易见的事情是同时执行setinner
和setouter
。他们完全相互独立,总是需要完成。所以,这就是我要做的。而不是这样做:
inneropt,partition,x = setinner(Q,G,n)
outeropt = setouter(Q,G,n)
...我们希望将两个函数作为任务提交到池中,然后等待两者完成,然后获取两者的结果。
concurrent.futures
模块(需要Python 2.x中的第三方反向端口)可以比multiprocessing
模块更容易执行“等待两者完成”之类的操作(这是在2.6+中的stdlib中,但在这种情况下,我们不需要任何花哨的东西;如果其中一个提前完成,我们无论如何都要做,直到另一个完成。所以,让我们坚持使用multiprocessing.apply_async
:
pool = multiprocessing.Pool(2) # we never have more than 2 tasks to run
while i < 1000:
# parallelly start both tasks
inner_result = pool.apply_async(setinner, (Q, G, n))
outer_result = pool.apply_async(setouter, (Q, G, n))
# sequentially wait for both tasks to finish and get their results
inneropt,partition,x = inner_result.get()
outeropt = outer_result.get()
# the rest of your loop is unchanged
您可能希望将池移动到函数外部,以便它永远存在,并且可以由代码的其他部分使用。如果没有,你几乎肯定想在功能结束时关闭池。 (multiprocessing
的更高版本允许您在with
语句中使用池,但我认为这需要Python 3.2+,因此您必须明确地执行此操作。)
如果您想要并行完成更多工作怎么办?好吧,没有重组循环,没有其他明显的事情要做。在您从updateGraph
和setinner
返回结果之前,您无法执行setouter
,此处没有其他任何内容。
但是,如果你可以重新组织事情,以便每个循环的setinner
独立于之前的所有内容(使用你的算法可能或不可能 - 不知道你在做什么,我无法猜测),您可以将2000个任务预先推送到队列中,然后根据需要抓取结果进行循环。例如:
pool = multiprocessing.Pool() # let it default to the number of cores
inner_results = []
outer_results = []
for _ in range(1000):
inner_results.append(pool.apply_async(setinner, (Q,G,n,i))
outer_results.append(pool.apply_async(setouter, (Q,G,n,i))
while i < 1000:
inneropt,partition,x = inner_results.pop(0).get()
outeropt = outer_results.pop(0).get()
# result of your loop is the same as before
当然,你可以让这位发烧友。
例如,假设您很少需要超过几百次迭代,因此总是计算1000次迭代会很浪费。你可以在启动时按下第一个N,然后每次循环推送一个N(或者每N次更多N次),这样你就不会做多于N次浪费的迭代 - 你无法获得理想在完美的并行性和最小的浪费之间进行权衡,但你通常可以很好地调整它。
此外,如果任务实际上没有那么久,但你有很多,你可能想要批量处理它们。一种非常简单的方法是使用map
变体之一而不是apply_async
;这可能会使您的提取代码变得更复杂,但它使排队和批处理代码完全无关紧要(例如,map
每func
个chunksize
列表包含{{1}}的100个参数10只是两行简单的代码。)