Python 引入多处理队列时执行时间的增加

Python 引入多处理队列时执行时间的增加,python,performance,python-2.7,parallel-processing,multiprocessing,Python,Performance,Python 2.7,Parallel Processing,Multiprocessing,我试图测量我已经使用Python的多处理包“并行化”的一段代码,特别是使用Process函数 我想并行运行两个函数:function1和function2函数1不返回值,函数2返回值。function2的返回值是一个相当大的类实例 下面是我的现有代码,用于使用队列并行化和获取返回值: import multiprocessing as mpc ... def Wrapper(self,...): jobs = [] q = mpc.Queue() p1 = mpc.Process(t

我试图测量我已经使用Python的多处理包“并行化”的一段代码,特别是使用Process函数

我想并行运行两个函数:
function1
function2
<代码>函数1不返回值,
函数2
返回值。function2的返回值是一个相当大的类实例

下面是我的现有代码,用于使用队列并行化和获取返回值:

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()
的调用,执行包装函数所需的时间会显著减少,因为它不会从
function2
返回类,但是我显然无法从函数中获取所需的数据。如果我把它放回去,运行时间会显著增加,从而表明并行化实际上比顺序执行这两个函数需要更长的时间

以下是包装器的一些平均执行时间,以供参考:

  • 顺序代码(即
    function1(timestep)
    res=function2(timestep,a1,a2,a3,…,无)
    ):10秒

  • 不使用队列的并行代码:8秒

  • 与队列并行的代码:60秒

我编写这段代码的目的是展示并行化一段代码如何能够提高在不必要的并行函数中执行所需的时间。作为参考,我正在使用cProfile包,生成代码的概要文件,并查看包装器运行所需的时间

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

如果我看一下程序的总体执行时间,并行化代码的运行速度要快得多。然而,当我再深入一点时,我的并行化代码似乎需要更长的时间


现在,我的想法是队列正在执行某种深度复制操作,但是我找不到一个引用来说明这一事实,所以我假设它正在返回一个浅层副本,对我来说,这不需要如此大的开销。

当您将一个对象传递到
多处理.Queue
<,需要在
put
侧对其进行pickle,然后必须将pickle的字节刷新到管道中。在
get
端,需要从管道读取经过pickle处理的字节,然后将它们解pickle回Python对象。所以在现实中,
多处理.Queue
做的事情甚至比深度复制还要慢

您看到的开销几乎可以肯定是取消拾取大型对象所需开销的结果。这是并行编程的一个领域,Python真的很难做到这一点——如果您正在执行CPU限制的操作(因此无法使用线程来获得并行性)并且需要共享状态,那么您将付出性能代价。如果您共享大型对象,则可能会受到很大的惩罚。Python中的并行性是通过并行化一些CPU限制的操作而获得的性能提升与由于必须在进程之间共享状态而导致的性能损失之间的折衷。因此,您的目标需要是最小化共享状态的数量,并最大化并行化的工作量

不幸的是,一旦您这样做了,您进一步减轻性能影响的选项就有点有限了。您可以尝试将类转换为
ctypes
对象,这将允许您使用在共享内存中创建对象。这应该比通过
队列
返回对象更快,但您必须处理
ctypes
的所有限制

另一个想法是在服务器中创建对象。如果您这样做,您的实际对象将位于服务器进程中,并且您的父进程和子进程都将通过服务器访问该对象。但是,这会使对象的每次读/写速度变慢,因此最终它的性能可能不会比您现在使用的
队列
实现更好


这两种方法都不是很好,而且可能都不适用于您的用例,在这种情况下,Python可能不是解决这个特定问题的最佳语言。别误会我;我喜欢Python,并在任何可能的时候使用它,但这是一个非常困难的领域。

虽然听到这个消息有些沮丧(因为这是我害怕的事情),但这是一个很好的答案。在我试图进行比较时,您认为为并行和非并行代码创建Manager服务器值得吗?我认为,如果两者都使用相同类型的共享对象,那么当我对应用程序计时时,性能影响可能就不那么明显了。@espais,好吧,如果您只是为了演示目的而这样做,您可以对并行代码和顺序代码使用
管理器。但是,如果您试图对“并行化此代码如何影响其性能”进行准确比较,那么实际情况是顺序代码不需要
管理器,而并行代码需要。如果您只是想演示并行化的功能,而这段代码是任意选择的,那么理想情况下,您会有一个需要较少共享状态的示例,这样并行化的速度就更快了…(续)@espais是因为现在您实际上发现,由于Python的局限性,并行化这段特定代码实际上会损害性能。理想情况下,你会发现你的代码库中有一部分真的会从并行中受益,但你可能没有任何适合的东西。有趣的一点。我认为我在这里的主要限制肯定是基于Python的。之前,我分析了我的代码库,发现