4

我试图测量一段代码,我使用Python的多处理程序包“并行化”,特别是使用Process函数。引入多处理队列时执行时间增加

我有,我想并行运行两个功能:function1function2function1不返回值,function2。函数2的返回值是一个相当大的类实例。

这里是我现有的使用队列并行并获得返回值码:

import multiprocessing as mpc 
... 
def Wrapper(self,...): 
    jobs = [] 
    q = mpc.Queue() 
    p1 = mpc.Process(target=self.function1,args=(timestep,)) 
    jobs.append(p1) 

    p2 = mpc.Process(target=self.function2,args=(timestep,arg1,arg2,arg3,...,q)) 
    jobs.append(p2) 

    for j in jobs: 
    j.start() 
    result = q.get() 

    for j in jobs: 
    j.join() 

所以,这里是我看到的问题。如果我删除了对result = q.get()的调用,执行Wrapper函数所需的时间显着减少,因为它不是从function2返回类,但是我显然没有从函数中获取我需要的数据。如果我把它放回来,运行时间会大大增加,从而表明并行化实际上比顺序执行这两个函数花费的时间更长。

下面是一些意味着包装的执行时间,以供参考:

  • 顺序码(即,function1(timestep)res = function2(timestep,a1,a2,a3,...,None)):10秒

  • 并行化的代码,而无需使用一个队列:8秒

  • 带队列的并行码:60秒

此代码的目标是展示如何并行化一段代码可以提高执行不必要的并行函数所需的时间。作为参考,我使用cProfile包,生成我的代码配置文件,并查看Wrapper运行所需的时间。

我开始对整个过程感到沮丧。它的目的是基本上加速我已经添加到内部开发的现有自定义框架中的部分程序,但是我无法实际表明我没有增加太多开销。

如果我看节目的总执行时间,并行化的代码运行得更快。但是,当我深入挖掘时,我的并行代码开始显得需要更长的时间。

现在,我的想法是,队列正在做某种深层复制操作,但是我找不到引用来说明这一事实,所以我假设它正在返回一个浅拷贝,对我而言,不应该要求这样的开销。

回答

3

当您将对象传递到multiprocessing.Queue时,它需要在put一侧进行酸洗,然后酸洗的字节必须刷新到管道。在get一侧,需要从管道中读取pickled字节,然后需要将它们取消放回到Python对象中。所以在现实中,multiprocessing.Queue的做法甚至比深度复制还要慢。

你看到的开销几乎可以肯定是要unpickle一个大对象所需的开销的结果。这是一个并行编程领域,在这个领域中,Python真的很挣扎 - 如果你正在进行CPU绑定操作(因此不能使用线程来获得并行性)并且需要共享状态,那么你会付出性能损失。如果你分享大型物品,惩罚可能会很大。 Python中的并行性是通过并行化某些CPU绑定操作获得的性能提升和在进程之间共享状态所获得的性能之间的折中。因此,您的目标需要尽量减少共享状态的数量,并最大限度地提高并行化的工作量。

一旦你这样做了,不幸的是,你进一步减少性能的选择是有限的。您可以尝试将您的课程转换为​​对象,该对象允许您使用multiprocessing.sharedctypes在共享内存中创建对象。这应该比通过Queue返回对象更快,但是您必须处理​​的所有限制。

另一个想法是在multiprocessing.Manager服务器中创建对象。如果你这样做,你的实际对象将存在于一个服务器进程中,并且父进程和子进程都将通过Proxy来访问该对象。但是,这会使对象的每次读取/写入都变得更慢,因此最终可能不会比现在执行的Queue执行得更好。

这些替代方法都不是很好,它们可能都不适用于你的用例,在这种情况下,Python可能不是解决这个特定问题的最佳语言。别误会我的意思;我喜欢Python并随时使用它,但这是一个真正挣扎的领域。

+0

虽然有些令人沮丧听到(因为这是我害怕的东西),这是一个很好的答案。正如我试图展示一个比较,你认为为并行和非并行代码创建Manager服务器是值得的吗?我认为如果两者都使用相同类型的共享对象,那么在计算我的应用程序时,性能下降可能不太明显。 – espais 2015-02-18 14:56:34

+0

@espais,好吧,如果你只是为了演示的目的而这样做,那么你可以为并行和顺序代码使用'Manager'。但是如果你想对“这里是如何平行化代码影响其性能”做一个准确的比较,现实情况是顺序代码不需要'Manager',而并行代码则需要。如果你只是试图展示并行化的力量,而这段代码是一个随心所欲的选择,理想情况下你会有一个需要较少共享状态的例子,所以并行化实际上更快......(续) ) – dano 2015-02-18 16:48:48

+0

@espais因为现在你实际上发现,由于Python的限制,并行化这些特定的代码实际上会伤害性能。理想情况下,你会发现你的代码库真的会从并行获益,但你可能没有任何合适的东西。 – dano 2015-02-18 16:50:02