perl会优化foreach吗?

Ole*_*kov 4 iteration perl

给出一个简单的程序:

use strict;
use warnings;

my $count = 0;

foreach ('a' .. 'zzzzzzzzzz') {
    $count++;
}

print "$count\n";
Run Code Online (Sandbox Code Playgroud)

是否会在内存中perl创建整个1.4+e14元素范围,然后迭代它们或者是否有任何内部优化来简单地一次跨越一个范围运算符状态?

好吧,我实际上已经运行了,所以经验数据top显示它是后者(并且它还没有完成从中查看数据time),但它是否记录在任何地方或在哪里找到源中的确认?

Bot*_*tje 7

来自perldoc perlop:

在当前实现中,当范围运算符用作foreach循环中的表达式时,不会创建临时数组,但是当您编写类似[所写内容]的内容时,旧版本的Perl可能会烧掉大量内存

  • 此优化至少已达到5.6 (2认同)

ike*_*ami 6

是的,以下内容优化为计数循环:

 for (EXPRX..EXPRY) { ... }
Run Code Online (Sandbox Code Playgroud)

perlop提到这一点.

在当前实现中,当范围运算符用作foreach循环中的表达式时,不会创建临时数组,但是[Perl早于5.6的版本]可能会在您编写如下内容时烧掉大量内存:

for (1 .. 1_000_000) {
    # code
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下内容查看此内容:

$ perl -e'
   my $n = 10_000_000;
   system("ps h -o rss $$");
   for (1..$n) { system("ps h -o rss $$"); last; }
'
1876
1944
Run Code Online (Sandbox Code Playgroud)

即使在循环内,也只使用了1944 KiB.这与在循环之前分配的数量相同,并且远远不足以容纳10,000,000个标量.另一方面,以下略有不同的代码显示循环中使用的385 MiB:

$ perl -e'
   my $n = 10_000_000;
   system("ps h -o rss $$");
   for ((), 1..$n) { system("ps h -o rss $$"); last; }
'
 1876
394724
Run Code Online (Sandbox Code Playgroud)

在后一版本中,分配了所有1000万个标量.

事实上,这不仅仅是EXPR..EXPR特别的.以下所有内容的实现方式不同:

  • for (EXPR; EXPR; EXPR)("C-style for loop",一个增强的while循环.)
    功能不同.
  • for (EXPRX..EXPRY)(一个范围,没有别的.)
    递增计数循环.
  • for (reverse CONSTX..CONSTY)(一个常量范围,前面是反向.)
    在编译时构建的数组的索引下降计数循环.[1]
  • for (reverse EXPRX..EXPRY)(变量范围,前面是反向.)
    降序计数循环.
  • for (@ARRAY)(一个数组,没有别的.)
    计算数组索引的循环.
  • for (reverse @ARRAY)(数组的反转,没有别的.)
    降序计数循环数组的索引.
  • for (reverse LIST)(任何不符合上述模式的
    列表.)从头到尾迭代的展平列表.
  • for (LIST)(任何不符合上述模式的
    列表.)从开始到结束迭代的展平列表.

  1. 无处不在for (CONSTX..CONSTY),

    CONSTX..CONSTY
    
    Run Code Online (Sandbox Code Playgroud)

    被优化为

    my @anon;
    BEGIN { @anon = CONSTX..CONSTY; }
    @anon
    
    Run Code Online (Sandbox Code Playgroud)

    你可以在这里看到:

    $ perl -e'
       BEGIN { system("ps h -o rss $$"); }
       system("ps h -o rss $$");
       exit(0);
       for (reverse 1..10_000_000) {  }
    '
     1868
    709540
    
    Run Code Online (Sandbox Code Playgroud)