Python 代码性能随线程而降低

Posted

技术标签:

【中文标题】Python 代码性能随线程而降低【英文标题】:Python code performance decreases with threading 【发布时间】:2011-10-12 21:12:21 【问题描述】:

我用 Python 编写了一个工作程序,它基本上解析一批二进制文件,将数据提取到数据结构中。每个文件大约需要一秒钟的时间来解析,这意味着数千个文件需要几个小时。我已经成功实现了批处理解析方法的线程版本,线程数可调整。我用不同数量的线程在 100 个文件上测试了该方法,并为每次运行计时。以下是结果(0 个线程指的是我原来的预线程代码,1 个线程指的是新版本的运行,并产生了一个线程)。

0 threads: 83.842 seconds
1 threads: 78.777 seconds
2 threads: 105.032 seconds
3 threads: 109.965 seconds
4 threads: 108.956 seconds
5 threads: 109.646 seconds
6 threads: 109.520 seconds
7 threads: 110.457 seconds
8 threads: 111.658 seconds

虽然与让主线程完成所有工作相比,生成线程会带来小幅性能提升,但实际上增加线程数会降低性能。我本来希望看到性能提高,至少多达四个线程(一个用于我机器的每个内核)。我知道线程有相关的开销,但我认为这对于个位数的线程来说并不重要。

我听说过“全局解释器锁”,但是当我移动到四个线程时,我确实看到了相应数量的内核在工作——两个线程两个内核在解析期间显示活动,等等。

我还测试了一些不同版本的解析代码,看看我的程序是否是 IO 绑定的。似乎不是;仅仅读入文件花费的时间比例相对较小;处理文件几乎是全部。如果我不执行 IO 并处理文件的已读取版本,我添加第二个线程会损害性能,而第三个线程会稍微改善它。我只是想知道为什么我不能利用计算机的多核来加快速度。请发表任何问题或我可以澄清的方式。

【问题讨论】:

这里的 GIL 可能有问题。您可以查看多处理模块,作为线程模块的替代方案,因为它实现了真正的并发性,而 GIL 将阻止它进行线程化。 看看this。你已经遇到了唯一我讨厌 Python 的地方(好吧,反正是 CPython)。 多个核心会显示活动,但它只是在它们之间切换 - 一次只能运行一个 Python 线程。您需要多处理:docs.python.org/dev/library/multiprocessing 我将研究使用多处理;我正在运行 Python 2.4,所以我需要先升级,这就是我对线程感兴趣的原因。我认为多处理只是围绕线程/线程的更高级别的外壳。那么穿线有什么意义呢?而且我仍然不确定我是否理解为什么多个线程会减慢我的程序——这只是线程开销吗? 【参考方案1】:

线程库实际上并没有同时利用多个内核进行计算。您应该使用 multiprocessing 库来代替计算线程。

【讨论】:

第一个说法不正确。它确实使用多个内核。当时只有一个人可以获得 GIL。 啊,我漏了一个字。固定。 你没有抓住重点。阻止它的不是线程库本身。它使用 pthread 库,可以使用所有内核。这意味着可以修复线程库并解决问题。但问题远不止于此。 他的说法是正确的——他没有说它不能使用多核,他说它没有。 @Ikke:他指的是 Python 线程库,而不是底层实现(关于它我们什么都不需要知道,也不一定使用 POSIX 线程 API——它当然不使用它在 Windows 上!)。【参考方案2】:

可悲的是,这就是 CPython 中的情况,主要是由于全局解释器锁 (GIL)。受 CPU 限制的 Python 代码根本无法跨线程扩展(另一方面,受 I/O 限制的代码可能会在一定程度上扩展)。

David Beazley 提供了一个内容丰富的 presentation,他在其中讨论了与 GIL 相关的一些问题。视频可以在here 找到(感谢@Ikke!)

我的建议是使用multiprocessing 模块而不是多线程。

【讨论】:

Here 是该演示文稿的视频。 多处理模块运行良好。一旦我让它工作,我就看到了我一直期待的那种加速。谢谢。 此注释不适用于python与c++代码共享cpu绑定代码的情况。代码及说明:github.com/PaddlePaddle/Paddle/pull/1364#discussion_r101898833

以上是关于Python 代码性能随线程而降低的主要内容,如果未能解决你的问题,请参考以下文章

Python 协程

为啥 C++ 中的多线程会降低性能

openmp:线程数的增加会降低性能

多线程会降低 GPU 性能

挥发性使用会降低性能

对象与内存