如何在python中使用多处理

时间:2013-12-12 23:26:31

标签: python multiprocessing

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

1 个答案:

答案 0 :(得分:1)

很难并行化需要改变来自不同任务的相同共享数据的代码。所以,我将假设setinnersetouter是非变异函数;如果那不是真的,情况会更复杂。

第一步是决定你想要并行做什么。


一个显而易见的事情是同时执行setinnersetouter。他们完全相互独立,总是需要完成。所以,这就是我要做的。而不是这样做:

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+,因此您必须明确地执行此操作。)


如果您想要并行完成更多工作怎么办?好吧,没有重组循环,没有其他明显的事情要做。在您从updateGraphsetinner返回结果之前,您无法执行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;这可能会使您的提取代码变得更复杂,但它使排队和批处理代码完全无关紧要(例如,mapfuncchunksize列表包含{{1}}的100个参数10只是两行简单的代码。)