Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
美元比Perl'中的命名变量更有效吗;什么是foreach?_Perl_Variables_Loops_Performance - Fatal编程技术网

美元比Perl'中的命名变量更有效吗;什么是foreach?

美元比Perl'中的命名变量更有效吗;什么是foreach?,perl,variables,loops,performance,Perl,Variables,Loops,Performance,我是Perl新手,我想知道以下哪种循环更有效: my @numbers = (1,3,5,7,9); foreach my $current (@numbers){ print "$current\n"; } 或 我想知道这一点,以便知道$的使用是否更有效,因为它放在寄存器中是因为它是常用的还是不常用的。我已经写了一些代码,我正在尝试清理它,我发现我使用第一个循环的次数比使用第二个循环的次数要多。您可以看看,还有一章“对代码进行基准测试”您可以使用来比较这两种方法。您是否发现使用这些循

我是Perl新手,我想知道以下哪种循环更有效:

my @numbers = (1,3,5,7,9);
foreach my $current (@numbers){
    print "$current\n";
}


我想知道这一点,以便知道$的使用是否更有效,因为它放在寄存器中是因为它是常用的还是不常用的。我已经写了一些代码,我正在尝试清理它,我发现我使用第一个循环的次数比使用第二个循环的次数要多。

您可以看看,还有一章“对代码进行基准测试”您可以使用来比较这两种方法。

您是否发现使用这些循环的代码部分存在性能问题?如果没有,您希望选择可读性更好、更易于维护的版本。速度上的任何差异都可能可以忽略不计,尤其是与系统的其他部分相比。始终先编写可维护性代码,然后编写概要文件,然后编写性能代码

“过早的优化是万恶之源”[1]


[1] 克努特,唐纳德。带go to语句的结构化编程,ACM计算调查杂志,第6卷,第4期,1974年12月。p、 268.

我不知道,但是。。。首先,在循环的第二个版本中保存一个变量赋值。我可以想象,由于$经常被使用,它应该以某种方式进行优化。您可以尝试分析它,Tim Bunce编写了一个非常好的Perl分析器


那么,这个小东西真的值得优化吗?我不认为一个循环会有什么不同。我建议您使用profiler来衡量您的性能并确定真正的瓶颈。通常,速度问题出现在90%时间内运行的10%代码中(可能不是10-90,但这是“著名的”比率:P)。

使用

$\uu
是一种Perl习惯用法,它向经验丰富的程序员表明使用了“当前上下文”。此外,许多函数默认情况下将
$\uu
作为参数,从而使代码更加简洁

有些人可能会争辩说,“写起来很难,读起来也应该很难。”

甚至知道过早的优化是万恶之源

但有些期望可能是错误的。测试有点奇怪,因为输出可能会产生一些奇怪的副作用,并且顺序可能很重要

#!/usr/bin/env perl
use strict;
use warnings;
use Benchmark qw(:all :hireswallclock);

use constant Numbers => 10000;

my @numbers = (1 .. Numbers);

sub no_out (&) {
    local *STDOUT;
    open STDOUT, '>', '/dev/null';
    my $result  = shift()->();
    close STDOUT;
    return $result;
};

my %tests = (
    loop1 => sub {
        foreach my $current (@numbers) {
            print "$current\n";
        }
    },
    loop2 => sub {
        foreach (@numbers) {
            print "$_\n";
        }

    },
    loop3 => sub {
        local $\ = "\n";
        print foreach @numbers;
        }
);

sub permutations {
    return [
        map {
            my $a = $_;
            my @f = grep {$a ne $_} @_;
            map { [$a, @$_] } @{ permutations( @f ) }
            } @_
        ]
        if @_;
    return [[]];
}

foreach my $p ( @{ permutations( keys %tests ) } ) {
    my $result = {
        map {
            $_ => no_out { sleep 1; countit( 2, $tests{$_} ) }
            } @$p
    };

    cmpthese($result);
}
可以预期,loop2应该比loop1快

       Rate loop2 loop1 loop3
loop2 322/s    --   -2%  -34%
loop1 328/s    2%    --  -33%
loop3 486/s   51%   48%    --
       Rate loop2 loop1 loop3
loop2 322/s    --   -0%  -34%
loop1 323/s    0%    --  -34%
loop3 486/s   51%   50%    --
       Rate loop2 loop1 loop3
loop2 323/s    --   -0%  -33%
loop1 324/s    0%    --  -33%
loop3 484/s   50%   49%    --
       Rate loop2 loop1 loop3
loop2 317/s    --   -3%  -35%
loop1 328/s    3%    --  -33%
loop3 488/s   54%   49%    --
       Rate loop2 loop1 loop3
loop2 323/s    --   -2%  -34%
loop1 329/s    2%    --  -33%
loop3 489/s   51%   49%    --
       Rate loop2 loop1 loop3
loop2 325/s    --   -1%  -33%
loop1 329/s    1%    --  -32%
loop3 488/s   50%   48%    --
有时我观察到持续的
loop1
loop2
快15%-20%,但我无法确定原因

观察到我为loop1和loop2生成了字节码,在创建
my
变量时只有一个不同。此变量内部未分配也未复制,因此此操作非常便宜。我认为差异只来自于不便宜的
“$\n”
构造。这些循环应该非常相似

for (@numbers) {
  ...
}

for my $a (@numbers) {
  ...
}
但是这个回路更贵

for (@numbers) {
  my $a = $_;
  ...
}
而且

print "$a\n";
它比其他任何东西都贵

print $a, "\n";
基准:

use Benchmark qw(timethese cmpthese);

my $iterations = 500000;     

cmpthese( $iterations,
  {
    'Loop 1' => 'my @numbers = (1,3,5,7,9);
    foreach my $current (@numbers)
    {
      print "$current\n";
    }', 

    'Loop 2' => 'my @numbers = (1,3,5,7,9);
    foreach (@numbers)
    {
      print "$_\n";
    }'
  }
);
输出:

         Rate     Loop 2 Loop 1
Loop 2  23375/s     --    -1%
Loop 1  23546/s     1%     --
我已经运行了几次,结果各不相同。我认为可以肯定地说,两者没有太大区别


我对总的想法更感兴趣 使用$而不是打印


作为旁注,如果您想开始学习要避免哪些习惯用法以及为什么要避免,那么Perl最佳实践是一个好去处。我不同意他写的每一件事,但他在大多数情况下都是正确的。

通过“
perl-MO=concrete,-terse,-src test.pl
”运行这两个选项,会产生以下两个操作树:

我的$n(@num){…}

LISTOP (0x9c08ea0) leave [1] OP (0x9bad5e8) enter # 5: my @num = 1..9; COP (0x9b89668) nextstate BINOP (0x9b86210) aassign [4] UNOP (0x9bacfa0) null [142] OP (0x9b905e0) pushmark UNOP (0x9bad5c8) rv2av SVOP (0x9bacf80) const [5] AV (0x9bd81b0) UNOP (0x9b895c0) null [142] OP (0x9bd95f8) pushmark OP (0x9b4b020) padav [1] # 6: for my $n (@num){ COP (0x9bd12a0) nextstate BINOP (0x9c08b48) leaveloop LOOP (0x9b1e820) enteriter [6] OP (0x9b1e808) null [3] UNOP (0x9bd1188) null [142] OP (0x9bb5ab0) pushmark OP (0x9b8c278) padav [1] UNOP (0x9bdc290) null LOGOP (0x9bdc2b0) and OP (0x9b1e458) iter LISTOP (0x9b859b8) lineseq # 7: say $n; COP (0x9be4f18) nextstate LISTOP (0x9b277c0) say OP (0x9c0edd0) pushmark OP (0x9bda658) padsv [6] # <=== OP (0x9b8a2f8) unstack LISTOP (0x8cdbea0) leave [1] OP (0x8c805e8) enter # 5: my @num = 1..9; COP (0x8c5c668) nextstate BINOP (0x8c59210) aassign [4] UNOP (0x8c7ffa0) null [142] OP (0x8ccc1f0) pushmark UNOP (0x8c805c8) rv2av SVOP (0x8c7ff80) const [7] AV (0x8cab1b0) UNOP (0x8c5c5c0) null [142] OP (0x8cac5f8) pushmark OP (0x8c5f278) padav [1] # 6: for (@num){ COP (0x8cb7f18) nextstate BINOP (0x8ce1de8) leaveloop LOOP (0x8bf1820) enteriter OP (0x8bf1458) null [3] UNOP (0x8caf2b0) null [142] OP (0x8bf1808) pushmark OP (0x8c88ab0) padav [1] PADOP (0x8ca4188) gv GV (0x8bd7810) *_ # <=== UNOP (0x8cdbb48) null LOGOP (0x8caf290) and OP (0x8ce1dd0) iter LISTOP (0x8c62aa8) lineseq # 7: say $_; COP (0x8cade88) nextstate LISTOP (0x8bf12d0) say OP (0x8cad658) pushmark UNOP (0x8c589b8) null [15] # <=== PADOP (0x8bfa7c0) gvsv GV (0x8bd7810) *_ # <=== OP (0x8bf9a10) unstack
我已经添加了“
我正在努力使代码看起来更”Perly“与其优化。。。我注意到Perl开发人员通常使用$,这是我的主要原因之一……此外,这不是问题所在。OP没有问他是否应该优化?他只是问哪个更快。@mandel:在这种情况下,你应该问哪个更完美:P真的。。。但我也对Perlish是否高效感兴趣;)@曼德尔:在Perl中有一些成语值得使用,但人们过分看重“Perlish”的概念。坚持使用常用习惯用法的唯一目的是让其他程序员更容易阅读和理解您的代码,但“Perlish”代码通常更难让其他人理解。我更感兴趣的是使用$而不是打印的一般想法。。。无论如何,我希望您能对上面的代码多解释一点:使用$的主要想法是避免我的变量创建和分配。无论如何,这些操作都会消耗一些时间。使用
for my$a(@numbers){}
不会为数组成员分配内存(请注意,更改$a将影响@numbers)。如果希望在循环内更改循环变量而不影响数组,则可以使用
for(@numbers){my$a=$}
这当然会导致复制和分配。另一个优化技巧是通过“$\避免创建字符串”。另一种方法是打印$\n,“\n”;我已经在“为MSWin32-x86-multi-thread构建的perl,v5.10.0”上运行了您的测试。循环之间没有区别(我认为
print
消除了所有的差异)。对于这个特定的例子,最快的是
say foreach@numbers一般来说,访问全局文件总是比访问词法文件慢。需要从名称空间哈希中提取全局值。$\u是一个神奇的全局函数,存储在PL_defgv中,但perl必须按名称获取它。词法访问只是数组的编译时固定索引。只有@u在shift和pop上进行了优化。对于仅使用$的循环来说,仅使用5次迭代的循环中的1%是相当多的,每个循环是一个可能的差异…基准测试不像一些人认为的那么容易。看,你的结果是每秒10万个数字,而我的结果是每秒3220个。您主要衡量的是终端IO速度:)您使用的是Perl,一种脚本语言,那么您为什么要关心一个简单循环的效率呢?好吧,这个循环只是一个例子来说明我的疑问(可能有30到40个循环)
LISTOP (0x9c08ea0) leave [1] 
    OP (0x9bad5e8) enter 
# 5: my @num = 1..9;
    COP (0x9b89668) nextstate 
    BINOP (0x9b86210) aassign [4] 
        UNOP (0x9bacfa0) null [142] 
            OP (0x9b905e0) pushmark 
            UNOP (0x9bad5c8) rv2av 
                SVOP (0x9bacf80) const [5] AV (0x9bd81b0) 
        UNOP (0x9b895c0) null [142] 
            OP (0x9bd95f8) pushmark 
            OP (0x9b4b020) padav [1] 
# 6: for my $n (@num){
    COP (0x9bd12a0) nextstate 
    BINOP (0x9c08b48) leaveloop 
        LOOP (0x9b1e820) enteriter [6] 
            OP (0x9b1e808) null [3] 
            UNOP (0x9bd1188) null [142] 
                OP (0x9bb5ab0) pushmark 
                OP (0x9b8c278) padav [1] 
        UNOP (0x9bdc290) null 
            LOGOP (0x9bdc2b0) and 
                OP (0x9b1e458) iter 
                LISTOP (0x9b859b8) lineseq 
# 7:   say $n;
                    COP (0x9be4f18) nextstate 
                    LISTOP (0x9b277c0) say 
                        OP (0x9c0edd0) pushmark 
                        OP (0x9bda658) padsv [6] # <===
                    OP (0x9b8a2f8) unstack
LISTOP (0x8cdbea0) leave [1] 
    OP (0x8c805e8) enter 
# 5: my @num = 1..9;
    COP (0x8c5c668) nextstate 
    BINOP (0x8c59210) aassign [4] 
        UNOP (0x8c7ffa0) null [142] 
            OP (0x8ccc1f0) pushmark 
            UNOP (0x8c805c8) rv2av 
                SVOP (0x8c7ff80) const [7] AV (0x8cab1b0) 
        UNOP (0x8c5c5c0) null [142] 
            OP (0x8cac5f8) pushmark 
            OP (0x8c5f278) padav [1] 
# 6: for (@num){
    COP (0x8cb7f18) nextstate 
    BINOP (0x8ce1de8) leaveloop 
        LOOP (0x8bf1820) enteriter 
            OP (0x8bf1458) null [3] 
            UNOP (0x8caf2b0) null [142] 
                OP (0x8bf1808) pushmark 
                OP (0x8c88ab0) padav [1] 
            PADOP (0x8ca4188) gv  GV (0x8bd7810) *_ # <===
        UNOP (0x8cdbb48) null 
            LOGOP (0x8caf290) and 
                OP (0x8ce1dd0) iter 
                LISTOP (0x8c62aa8) lineseq 
# 7:   say $_;
                    COP (0x8cade88) nextstate 
                    LISTOP (0x8bf12d0) say 
                        OP (0x8cad658) pushmark 
                        UNOP (0x8c589b8) null [15] # <===
                            PADOP (0x8bfa7c0) gvsv  GV (0x8bd7810) *_ # <===
                    OP (0x8bf9a10) unstack