【问题标题】:Overhead in Python multiprocessing modulePython 多处理模块中的开销
【发布时间】:2018-01-20 00:18:05
【问题描述】:

我在 Python 中使用 multiprocessing 模块,预计启动进程、创建队列以及将值放入队列或从队列中获取值会产生一些开销。但是,如果子流程有足够的工作要做,我希望开销最终会被淘汰。运行一个简单的示例(如下所述),我生成的进程的运行时间大约是从父进程启动的同一进程的 10 倍,即使对于非常大的作业也是如此。

在下面的代码中,我计算了一系列越来越大的数组的平均值。我比较了从父进程调用numpy.mean 与从单个衍生进程调用相同的平均函数以及在衍生进程中什么都不做(以了解开销成本)。

最初,结果如我所料。当从父进程调用mean 时,总运行时间比从衍生进程调用时快得多。对于小型作业,生成进程的运行时间主要由开销决定。

然而,令人惊讶的是,对于较大的作业,衍生进程的运行时间始终超过从父进程调用的成本大约 10 倍。

谁能解释一下?这是由于子进程中的内存限制吗?我测试的最大阵列是 125MB、500MB 和 2GB。

代码如下:

%matplotlib 
import numpy, multiprocessing, pandas

def do_nothing(x,q):
    q.put(x[-1])

def my_mean(x,q):
    q.put(numpy.mean(x))

def test_mp(f,x):
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=f,args=(x,q))
    p.start()
    p.join()
    s = q.get()
    return s

ndata = 2**numpy.arange(10,29,2)
tr1,tr2,tr3 = [[],[],[]]
for n in ndata:
    x = numpy.random.rand(n)
    tresults = %timeit -n 1 -r 5 -o -q test_mp(do_nothing,x)
    tr1.append(tresults)

    tresults = %timeit -n 1 -r 5 -o -q test_mp(my_mean,x)
    tr2.append(tresults)

    tresults = %timeit -n 1 -r 5 -o -q numpy.mean(x)
    tr3.append(tresults)

print("All done")

t1,t2,t3 = map(lambda tr : pandas.Series([1000*t.best for t in tr]),[tr1,tr2,tr3])
df = pandas.DataFrame({'n' : ndata, 't1 (do nothing)' : t1, 
                       't2 (my_mean)' : t2, 
                       't3 (mean)'    : t3})
display(df)
df.plot(x='n',style='.-',markersize=10,logx=True,logy=True)

这是结果。所有计时结果都以毫秒为单位。

【问题讨论】:

  • 每个进程都有自己的内存空间。队列通过pickling(在第一个进程的内存空间中)和再次unpickling(在第二个进程的内存空间中)在进程之间传输数据。对于庞大的数据结构,这将产生相应的巨大开销。如果可能,您需要初始化数据并在同一进程中处理它。 subprocess 包中还有一些方法(例如 Array),用于跨进程共享某些类型的数据。
  • 令我惊讶的是do_nothing 的时间安排:由于您不是多线程的,因此数据必须从一个进程传递到另一个进程,这与n 的开销成正比。如果我重复这个实验,我会得到不同的结果,在第 8 行显示 do_nothing 略有增加,然后......我的电脑填满了它的 RAM。
  • @Matteo T. 我试图设置“什么都不做”,以便将与将数组传递给子进程相关的任何开销都考虑在内(注意我将最后一个条目放在队列中)。因此,除非 Python 解释器真的很聪明,并且认识到我在 do_nothing 中根本没有使用 x,否则我看不出酸洗成本是如何反映的。
  • 你能试试q.put(x[int(numpy.random.uniform(0,len(x)-1))]) 代替q.put(x[-1]) 吗?这对 CPU 来说仍然不算什么,但 python 永远不会预测它;)
  • 刚刚尝试过,我得到了基本相同的结果。

标签: python numpy multiprocessing


【解决方案1】:

这里有一些关于正在发生的事情的观察:

  • %timeit 中使用-c 标志仅显示父进程的CPU 时间,在这种情况下,my_meando_nothing 显示基本相同的平坦时间。所以父进程在这两种情况下都使用相同的 CPU 时间。

  • 没有-c 标志,酸洗过程中的时间被计算在内,至少在对my_mean 的调用中。为什么在do_nothing 中没有考虑到它仍然是一个谜。 Python 解释器是否足够聪明,可以识别 do_nothing 真的什么都不做?

  • 还有什么有点神秘的:谁在做酸洗?父进程?如果是这样,它不会使用任何 CPU 时间。所以它必须是产生的过程。

  • 使用线程模块(使用queue.Queue()threading.Thread(),结果更符合预期,即对于足够大的问题,运行时间主要取决于计算平均值所需的时间,并且对numpy.mean() 的调用和衍生进程中的相同调用所花费的时间基本相同。

以下是相同问题的时间,使用线程模块:

【讨论】:

    猜你喜欢
    • 2011-04-04
    • 2017-12-11
    • 2021-10-23
    • 2019-01-16
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多