【问题标题】: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 而不是我的

标签: perl interpreter strict


【解决方案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】:
        1. 在循环外声明my 会导致声明发生一次。在声明期间,perl 会为该变量保留内存。

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

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

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

        【讨论】:

          猜你喜欢
          • 2018-01-06
          • 1970-01-01
          • 1970-01-01
          • 2021-02-13
          • 2018-10-13
          • 1970-01-01
          • 1970-01-01
          • 2015-11-30
          • 2011-09-02
          相关资源
          最近更新 更多