pythonprocess多核更慢

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pythonprocess多核更慢相关的知识,希望对你有一定的参考价值。

由于Python全局解释器锁的存在,导致在执行多线程的时候实际上只有一个线程在运行,这使得多核CPU无法发挥它真正的效率。而多进程就可以很好的解决这个问题。如果你打开多进程的姿势不对,会导致它比单进程更慢。
写出了正确的多线程代码,运行速度反而比单线程慢很多,原来是由于GIL(Global Interpreter Lock)!
GIL 是Cpython(Python语言的主流解释器)特有的全局解释器锁(其他解释器因为有自己的线程调度机制,所以没有GIL机制),GIL锁定Python线程中的CPU执行资源。线程在执行代码时,必须先获得这把锁,才获得CPU执行代码指令。如果这把锁被其他线程占用,该线程就只能等待,等到占有该锁的线程释放锁。
在Cpython解释器中,线程要想执行CPU指令需要2个条件:

被操作系统调度出来(操作系统允许它占用CPU)
获取到GIL(Cpython解释器允许它执行指令)
如果写出正确的多线程代码,执行的情况就是会有线程满足条件1不满足条件2,这时只能等待。
在单核CPU机器上,多线程与单线程在本质上并无不同,因为所有线程都是轮流占用CPU。多个线程慢于一个线程,因为其他线程还要先调度出来,再等待。
在多核CPU机器上,多线程代码运行性能会非常糟糕,比单核更糟糕。因为这时候多一个步骤,不同的CPU再竞争GIL,GIL只有一个。Python在多核CPU上的多核CPU也只有单线程在跑程序。

解决办法:【绕开GIL的影响】

使用多进程(多进程之间没有GIL限制)
使用Jython, IronPython等无GIL的解释器
使用协程(高效的单线程模式)
GIL的设置有其优点和可取之处,在Cpython解释器框架之下难以绕过这一限制。可以用PyPy解释器,麻烦之处在于很多第三方库在PyPy下无法使用,或者重新安装第三方库的PyPy版本。运行时候,PyPy **.py即可。Cpython下是Python **.py。
I/O的多线程还是快于单线程,因为优先级在获取GIL之上,I/O并行运算的时候,GIL处于release状态
参考技术A python多核更慢是因为python本身的一些性能瓶颈。python有一个全局解释器锁(GIL),这个锁会在多线程情况下限制其最大效率,所以它不能充分利用多核的优势,导致多核比单核更慢。另外,python的并行性能也受到I/O操作的限制,使得它的最大效率被拉低。 参考技术B 1 是的,Python进程在多核处理器上可能会变慢。

2 这是因为Python的全局解释锁(GIL)只允许一个线程同时执行Python代码,即使使用多个核心也无法并行处理Python代码。

3 但是,在某些情况下,使用多个进程而不是线程可以提高Python代码的性能,因为每个进程都有自己的解释器和GIL。
此外,使用一些第三方库,如multiprocessing和joblib,也可以帮助Python代码在多核处理器上并行运行。
参考技术C 根据我所学的知识,是的,一般情况下,多核的Python进程会比单核的Python进程更慢,原因是多核的Python进程需要进行并发处理多个任务,这会带来额外的开销,从而导致性能变慢。 参考技术D Python多核处理可能会比单核处理慢,因为它必须在多个核之间共享数据和资源。Python的多线程也会导致资源竞争,这会导致更低的性能。另外,Python的标准库也不是专为多核处理而设计的,它们也会拖累性能。因此,Python的多核处理可能会比单核处理慢。

Perl:为啥在循环中声明(我的)变量会更慢?

【中文标题】Perl:为啥在循环中声明(我的)变量会更慢?【英文标题】:Perl: Why is it slower to declare (my) variables inside a loop?Perl:为什么在循环中声明(我的)变量会更慢? 【发布时间】:2011-03-20 07:00:20 【问题描述】:

从解释器的 POV 来看,以下程序之间有什么区别:

#!/usr/bin/perl -w

use strict;

for (1..10000000) 
    my $jimmy = $_**2;

#!/usr/bin/perl -w

use strict;

my $jimmy;
for (1..10000000) 
    $jimmy = $_**2;

第一个节目的“时间”报告:

real    0m1.519s
user    0m1.513s
sys     0m0.004s

第二个:

real    0m1.023s
user    0m1.012s
sys     0m0.002s

【问题讨论】:

除了 Robert Greiner 的回答之外,my 在您的代码中的两个位置在语义上是不同的,这在非玩具程序中可能很重要。 请记住,仅仅因为您可以优化一段代码,并不意味着您应该这样做。分析整个应用程序,看看您尝试优化的代码是否占处理时间的很大一部分。此外,尝试设置全局和本地性能目标,一旦达到目标,就停止优化:) 如果您想查看在循环内声明my 时发生的额外操作的列表,您可以像这样运行每个脚本并比较不同的输出:perl -MO=Concise,-exec script.pl 鉴于有关重新初始化的答案,我很惊讶没有人提到使用 state 而不是我的 【参考方案1】:

Perl 中的my 声明有两个主要效果;一个编译时的(其中它在包含子的暂存器上分配一个插槽,并确保在适当范围内对该名称的所有引用都解析到该特定的暂存器插槽)和一个运行时的(其中它重置值那个焊盘插槽到undef,或者如果你写了my $var = foo,则到某个特定值。

编译时部分当然具有零摊销运行时成本,但运行时部分在每次执行通过 my 声明时运行一次。正如其他人所指出的那样,您的两个示例具有不同的性能,因为它们通常具有不同的语义——一个在每次循环时都会清除变量,而另一个则不会。

【讨论】:

【参考方案2】:

由于您给出的示例程序并没有真正做任何事情,因此很难给您一个具体的理由说明为什么一种声明会比另一种更好。正如许多其他海报所指出的那样,在循环中声明变量每次都会创建一个新变量。在您的示例中,创建是多余的,但请考虑以下使用闭包的示例。

my @closures;
my $jimmy;

for (1 .. 10) 
    $jimmy = $_** 2;
    push @closures, sub print "$jimmy\n";

还有这个:

my @closures;

for (1 .. 10) 
    my $jimmy = $_** 2;
    push @closures, sub print "$jimmy\n";

在每种情况下,代码都会构建一系列代码引用,但在第一个示例中,因为所有代码引用都引用相同的$jimmy,所以每个引用都会在调用时打印 100。在第二个示例中,每个代码 ref 将打印不同的数字 (1, 4, 9, 16, 25, ...)

所以在这种情况下,时间差并不重要,因为两个代码块做的事情非常不同。

【讨论】:

【参考方案3】:

第一个循环尝试为循环的每次迭代进行变量声明,并可能导致不必要的处理时间。

当然,它并不多,但这些东西会随着时间的推移而增加,而且技术上更慢,因为每次迭代执行的指令更多。

【讨论】:

权衡是您可以从在循环中声明它获得更多的隔离(如果您需要它)。这与应该在有意义的最小范围内声明变量的原则相一致。当然,如果您要调用 1000 万次,则不需要对标准一味地投入。 我想我很困惑,因为我认为解释器在执行之前为每个变量分配了内存? 我想我要问的是,编译器做了什么工作来“进行变量声明” @flies 任何语言都有什么作用?在内存的某个地方为它腾出空间,并在某个地方记录变量的名称。该分配与您的示例中采用的任何一种方法都是共同的。很明显,多次声明变量会导致执行时间有所不同。 @George 如果您对 Perl 正在做什么有所了解,这只是“显而易见的”。一个可能的,也许不明智的实现是让 $jimmy 静态绑定到一个内存位置。在这种情况下,my $jimmy for ... 将执行与 for... my $jimmy 相同的操作【参考方案4】:

首先,问题是您在每次迭代时都声明了一个新变量。

第二,还有一个更大的范围问题。

尝试在每个 for 之后添加这一行,看看会发生什么:

print $jimmy;

而且,也试试这个:

my $jimmy;
for (1..10000000) 
    my $jimmy = $_**2;

print $jimmy;

更多细节:

A my 将列出的变量声明为 本地(词法上)封闭 块、文件或评估。如果不止一个 值列出,列表必须是 放在括号里。

http://perldoc.perl.org/functions/my.html

您可能会发现这也是一本有用的读物​​:

http://perldoc.perl.org/perlsub.html#Private-Variables-via-my%28%29

【讨论】:

【参考方案5】:

    在循环外声明my 会导致声明发生一次。在声明期间,perl 会为该变量保留内存。

    在循环内声明 my 会导致声明在循环的每个间隔发生。

my 是 Perl 对在本地声明变量的回答 - local 用于其他用途,与在 C 中的含义不同。当您在循环中声明变量时,它被声明在循环块的本地范围内,块在每个间隔开始/结束。不仅声明了变量,而且还可以在块的末尾清除(取消引用和/或设置为undef)(尽管这与 Perl 版本不同)。

在循环块之外声明的变量被认为是“全局的”(不是字面意思,而是在循环块的意义上)。这些变量重复使用它们的内存位置,而不必搜索新地址。

【讨论】:

以上是关于pythonprocess多核更慢的主要内容,如果未能解决你的问题,请参考以下文章

进程Process

多线程文件复制比多核CPU上的单个线程慢得多

多进程架构 child_process

node child_process模块

配置Nginx多核CPU,worker_cpu_affinity使用方法和范例

如何缓冲multiprocessing.Process内部和外部函数的所有print()?