Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is $_ more efficient than a named variable in Perl's foreach?

I am quite new in Perl and I woud like to know which of the following loops is more efficient:

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

or

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

I want to know this in order to know if the use of $_ is more efficient because is place in a register because is commonly used or not. I have written some code and I'm trying to clean it up and I've found out that I'm using the first loop more often than the second one.

like image 532
mandel Avatar asked Jan 28 '09 08:01

mandel


2 Answers

Have you identified that there is a performance problem in sections of code that are making use of these loops? If not, you want to go for the one that is more readable and thus more maintainable. Any difference in speed will probably be negligible, especially compared to other parts of your system. Always code for maintainability first, then profile, then code for performance

"Premature optimisation is the root of all evil"[1]

[1] Knuth, Donald. Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974. p.268.

like image 181
tddmonkey Avatar answered Sep 20 '22 12:09

tddmonkey


Even know Premature optimisation is the root of all evil

{
  local $\ = "\n";
  print foreach @numbers;
}

but some expectations can be wrong. Test is little bit weird because output can make some weird side-effects and order can be important.

#!/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);
}

One can expect that loop2 should be faster than 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%    --

Sometimes I observed consistently loop1 about 15%-20% faster than loop2 but I can't determine why.

I was observed generated byte-code for loop1 and loop2 and there is difference only one when creating my variable. This variable interior is not allocated and also not copied thus this operation is very cheap. Difference comes I think only from "$_\n" construct which is not cheap. These loops should be very similar

for (@numbers) {
  ...
}

for my $a (@numbers) {
  ...
}

but this loop is more expensive

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

and also

print "$a\n";

is more expensive than

print $a, "\n";
like image 21
Hynek -Pichi- Vychodil Avatar answered Sep 19 '22 12:09

Hynek -Pichi- Vychodil