使用 NumPy 在 Python 中进行简单的多处理
Posted
技术标签:
【中文标题】使用 NumPy 在 Python 中进行简单的多处理【英文标题】:Naive multiprocessing in Python with NumPy 【发布时间】:2013-04-10 16:26:43 【问题描述】:尽管我从关于这个主题的大量问题中得到了警告和困惑的感觉,尤其是在 *** 上,我并行化了一个令人尴尬的并行问题的幼稚版本(基本上是 read-image-do-stuff -return 获取许多图像的列表),为每个计算返回生成的 NumPy 数组,并通过 callback
参数更新一个全局 NumPy 数组,并立即获得 x5 加速 8核机。
现在,由于每次回调调用都需要锁定,我可能没有得到 x8,但我得到的结果令人鼓舞。
我正在尝试找出这是否可以改进,或者这是否是一个好的结果。问题:
我想返回的 NumPy 数组被腌制了? 底层 NumPy 缓冲区是复制还是通过引用传递? 如何找出瓶颈是什么?有什么特别有用的技术吗? 我可以对此进行改进吗?或者这种改进在这种情况下很常见吗?【问题讨论】:
很难对这些问题中的大多数给出任何可靠的答案...... @mgilson:嗯,它确实工作得很好。我只是想更多地了解它在引擎盖下的作用。我特别不确定哪些对象会被腌制、复制或其他任何东西。不幸的是,Python 分析器没有说明任何关于子进程的信息,所以我无法清楚地确定以这种方式使用multiprocessing
的开销!欢迎任何想法;)
【参考方案1】:
我在使用sharedmem
模块:https://bitbucket.org/cleemesser/numpy-sharedmem 在多个进程之间共享大型 NumPy 数组(当然是通过引用)取得了巨大成功。基本上它抑制了在 NumPy 数组周围传递时通常发生的酸洗。您所要做的就是,而不是:
import numpy as np
foo = np.empty(1000000)
这样做:
import sharedmem
foo = sharedmem.empty(1000000)
然后你就可以将foo
从一个进程传递到另一个进程,例如:
q = multiprocessing.Queue()
...
q.put(foo)
但是请注意,该模块在程序不正常退出时存在内存泄漏的已知可能性,此处在某种程度上进行了描述:http://grokbase.com/t/python/python-list/1144s75ps4/multiprocessing-shared-memory-vs-pickled-copies。
希望这会有所帮助。我使用该模块来加速多核机器上的实时图像处理(我的项目是https://github.com/vmlaker/sherlock。)
【讨论】:
啊,我必须承认我忘记了这个问题,对不起!由于评论有点太大,我添加了一个关于我最终如何做到这一点的答案,这可能对你或其他人来说很有趣。不确定我必须接受哪一个...?【参考方案2】:注意:这个答案是我最终解决问题的方式,但如果您在进程之间进行密集传输,Velimir 的答案更适合。我不需要,所以我不需要sharedmem
。
我是怎么做到的
事实证明,腌制我的 NumPy 数组所花费的时间可以忽略不计,我太担心了。本质上,我正在做的是 MapReduce 操作,所以我正在这样做:
首先,在 Unix 系统上,如果需要,您在生成进程之前实例化的任何对象都将存在(并复制)在进程的上下文中。这称为 copy-on-write (COW),由内核自动处理,因此速度非常快(对于我的目的来说绝对足够快)。文档包含很多关于需要酸洗的对象的警告,但在这里我根本不需要这些作为我的输入。
然后,我最终从磁盘加载我的图像,从内部每个进程。每张图片都由自己的worker单独处理(映射),所以我既不锁定也不发送大批量数据,也没有任何性能损失。
每个工作人员对其处理的映射图像进行自己的缩减,然后使用Queue
将结果发送到主进程。我从缩减函数得到的通常输出是具有 4 或 5 个通道的 32 位浮点图像,大小接近 5000 x 5000 像素(每个约 300 或 400MB 内存)。
最后,我从每个进程中检索中间归约输出,然后在主进程中进行最终归约。
使用队列传输图像时,我没有看到任何性能损失,即使它们消耗了几百兆字节。我在 6 核工作站上运行它(使用超线程,因此操作系统看到 12 个逻辑内核),使用 6 核 multiprocessing
比不使用 multiprocessing
快 6 倍。
(奇怪的是,在全部 12 个内核上运行它并不比 6 个快,但我怀疑它与超线程的限制有关。)
分析
我关心的另一个问题是分析和量化multiprocessing
产生的开销。以下是我学到的一些有用的技巧:
与内置的(至少在我的 shell 中)time
命令相比,time
可执行文件(Ubuntu 中的/usr/bin/time
)提供了更多信息,包括诸如平均 RSS,上下文切换,平均 %CPU,...我这样运行它以获得我能得到的一切:
$ /usr/bin/time -v python test.py
分析(在 IPython 中使用 %run -p
或 %prun
)仅分析主进程。您可以将 cProfile
挂接到您生成的每个进程,并将各个配置文件保存到磁盘,就像在 this answer 中一样。
我建议添加某种DEBUG_PROFILE
标志来打开/关闭此功能,您永远不知道何时需要它。
最后但并非最不重要的一点是,您可以从系统调用配置文件中获得一些或多或少有用的信息(主要是为了查看操作系统是否不需要很长时间在进程之间传输大量数据),方法是附加到一个正在运行的 Python像这样的过程:
$ sudo strace -c -p <python-process-id>
【讨论】:
所以听起来你的程序是磁盘绑定的。而且由于您仍然获得了 6 倍的加速,CPU 的内核必须能够并行访问磁盘。并且由于您仅获得了 6 倍而不是 12 倍的加速,这必然意味着硬件线程对(在每个内核上)共享一个磁盘访问资源。 而且,显然在 X5550 处理器上,即使您的程序受内存限制,您仍然只能获得 6 倍,根据此:superuser.com/a/279803 我正在使用一个相当快的 SSD 并且磁盘读取没有达到最大值,但我怀疑像你所说的那样。 6核对我来说绰绰有余,所以我不介意,但有趣的是,人们吹嘘的12核在某些条件下只能完成一半的工作;)以上是关于使用 NumPy 在 Python 中进行简单的多处理的主要内容,如果未能解决你的问题,请参考以下文章