Perl reference vs. direct access vs. return Benchmark

This is a little Perl Script where I try to test what way is faster,  I test a normal reference(alias) in a sub vs. the direct access vs. normal access to a variable in a sub and then return the result, here is my Benchmark code:

use Benchmark qw(:all) ;
use strict;
# test variable
my $x = 0;
my $y = 0;
my $c = 0;
# run test
inc_ref(\$x) for(0..10);
inc_direct($y) for(0..10);
$c = inc_ret($c) for(0..10);
# print test
print "TEST:$x-$y-$c\n";
# run benchmark
cmpthese(-2, {
'ref'    => sub {inc_ref(\$x);},
'direct' => sub {inc_direct($y);},
'return' => sub {$c = inc_ret($c);},
});
 
# increment with reference
sub inc_ref {
    ++${$_[0]};
}
# increment with direct access
sub inc_direct {
    ++$_[0];
}
# increment with return
sub inc_ret{
    return(++$_[0]);
}

Our test result:

TEST:11-11-11                         
            Rate return    ref direct 
return 1976160/s     --   -11%   -36% 
ref    2216116/s    12%     --   -28% 
direct 3084046/s    56%    39%     --

write fast code in Perl

In this article I want to show how to write fast Perl-Code from the first to the last line, you could use it to speed up existing Programs but its better to make it right from beginning.

 

Turn off things you don’t need

In Perl it is important to use debug output, but only for developing, after your code is written down and tested you could disable it with a constant like Perl strict Benchmark or Perl use output only for debugging.For this take a look at Perl debug output and  Perl debug Benchmark, it is a simple way to switch debugging and other features off and on.The Interpreter knows that it could never be true and removes it completely, its faster than an If.Take a look at Perl print Benchmark to see how it works, and you could see that you should not modify the Stack.Another way is that the Interpreter could calculate things before they needed like in Perl constant Benchmark.Another thing is Bignum it is useful because it gives you a better precision but it costs a bit more time, fore more look at Perl Bignum Benchmark.

 

Don’t modify the Stack

For Perl its hard way to modify the stack with pop push shift unshift,its important for functions and arrays, try to access the items directly and not to shift them down like Perl shift iterate Benchmark.

 

Use pre-increment over post-increment

Lets have a look at the post increment operator, he stores the value in a variable then increments the original value and return the stored variable, the pre increment has just to increment and return the result, its much faster.For an Benchmark look at Perl increment Benchmark.

 

References

References are useful if you have big data, and you want to access them in a sub so you have only to move the reference and not the data, but be carefully if you have just scalars you could waste time like this: Perl reference vs. handing over.Another point is that arrays are smaller and faster than hashes so use them if you could: Perl array vs. hash handing over Benchmark.But in my last Test i get the best result with prototypes.

 

Replace and Regex

For Regex you should use the o flag or qr// to avoid a new check of the Regex compiler, but you cant interpolate the regex like Perl replace benchmark.For replace you should use the y// over the s//, because its faster.To see what the difference in replace look at Perl replace vs. sed, awk and bash.

 

Choose a multiplication over a division

In the most cases you could use a multiplication instead an division and save time like Perl multiplication vs. division Benchmark.

 

Use map

If you want to alter an array and could use map, then use it it speeds up your script: Perl map vs. for Benchmark.

 

Loop with fixed range

If you dont have to modify an pointer in the loop or have anoter increment you should use the for with a fixed range like Perl loop Benchmark.

 

remove double entries

To remove double entries in an array look at this: Perl remove double entries in an array Benchmark.

 

Make it by hand

The last stepp is to make it by hand like Inline C or a Preprocessor, or you could do it in Perl like in this example: Perl grep Benchmark, here I used an manual wy over an grep.

Perl strict Benchmark

This is a Example-Benchmark about to turn off things you don’t need in Perl:

use Benchmark qw(:all) ;
 
cmpthese(-1, {
'dev'      => sub {my($x,$y) = (4,3); $x = $x/$y;},
'strict'   => sub {my($x,$y) = (4,3); use strict; $x = $x/$y;},
});

Our Result:

            Rate    dev strict 
dev    3841286/s     --    -8%  
strict 4170472/s     9%     --

Maybe a good solution is to use it to check your code and then to comment it out, maybe with a debug variable, for this take a look at Perl Debug Output.

(This is just an Example, don’t turn of use strict.)

Perl sub Benchmark

This is a Benchmark about the subroutines in Perl:

use Benchmark qw(:all) ;
 
sub calc { return($_[0]*$_[1]);}

my %subs;
$subs{'calc1'} = eval 'sub { return($_[0]*$_[1]);}';
$subs{'calc2'} = sub { return($_[0]*$_[1]);};
print $subs{'calc1'}->(1234,1234).$/;
print $subs{'calc2'}->(1234,1234).$/;
print calc(1234,1234).$/;
 
cmpthese(-2, {
'normal'  => sub {calc(1234,1234)},
'comp'    => sub {$subs{'calc1'}->(1234,1234);},
'ref'     => sub {$subs{'calc2'}->(1234,1234);},
});

Result:

1522756
1522756
1522756
            Rate    ref   comp normal
ref    1920352/s     --    -4%   -17%
comp   1996495/s     4%     --   -14% 
normal 2313035/s    20%    16%     --

We see the normal way is the best solution.

Perl bignum Benchmark

This is a Benchmark in Perl about bignum :

use Benchmark qw(:all) ;

cmpthese(-1, {
'dev'      => sub {my($x,$y) = (4,3); $x = $x/$y;},
'bignum'   => sub {my($x,$y) = (4,3); use bignum; $x = $x/$y;},
});

Our result:

            Rate    dev bignum 
dev    4670926/s     --   -11% 
bignum 5224717/s    12%     --

We see the cost is about 12% performance, so if its not really necessary we should tun this feature off.

Perl Benchmark IN sub with grep

If you miss the IN function in Perl like the function in PL/SQL, then you could use grep and an array to check this:

grep {$var eq $_} qw(2 3 5 7 11 13 17 19)

I made a Benchmark to check how fast his solution is:

use strict;
use Benchmark qw(:all) ;

my @data = qw(2 3 5 7 11 13 17 19);
cmpthese(-2, {
'grep0'  => sub {
        for my $var (1..20){
            if(grep {$var == $_} qw(2 3 5 7 11 13 17 19)){
               # print "$var is prime!".$/;
            }else{
               # print "$var is not prime!".$/;
            }
        }
    },
'grep1'  => sub {
        for my $var (1..20){
            if(grep {$var == $_} @data){
               # print "$var is prime!".$/;
            }else{
               # print "$var is not prime!".$/;
            }
        }
    },
'or'   => sub {
        for my $var (1..20){
            if($var==2||$var==3||$var==5||$var==7||$var==11||$var==13||$var==17||$var==19){
               # print "$var is prime!".$/;
            }else{
               # print "$var is not prime!".$/;
            }
        }
    },
});

Result:

          Rate grep0 grep1    or  
grep0  74471/s    --   -7%  -61%  
grep1  79714/s    7%    --  -58%  
or    189402/s  154%  138%    --

We see the normal way is the fastest and you could use an defined array to speed up by 7%. The Code is short but much slower than an or comparison.

Perl && vs. and – || vs. or Benchmark

I want to compare the && vs. the and operator, and the || vs. or operator in Perl:

#!/usr/bin/perl
use strict;
use Benchmark qw(:all) ;
 
my $x = 100;
my $y = 0;
 
cmpthese(-1, {
'and'  => sub {if($y and $x){print;}print;},
'&&'   => sub {if($y &&  $x){print;}print;},
});
cmpthese(-1, {
'or'   => sub {if($y or  $x){print;}print;},
'||'   => sub {if($y ||  $x){print;}print;},
});
 
++$y;

cmpthese(-1, {
'and'  => sub {if($y and $x){print;}print;},
'&&'   => sub {if($y &&  $x){print;}print;},
});
cmpthese(-1, {
'or'   => sub {if($y or  $x){print;}print;},
'||'   => sub {if($y ||  $x){print;}print;},
});

 

First result (1 && 0) vs. (1 and 0):

         Rate  && and  
&&  4497569/s  -- -3%  
and 4626070/s  3%  --

Second result (1 or 0) vs. (1 || 0):

        Rate   or   || 
or 2683073/s   -- -15% 
|| 3163806/s  18%   --

Third result (1 && 1) vs. (1 and 1):

         Rate  && and  
&&  2525239/s  -- -1%  
and 2548621/s  1%  --

Fourth result (1 or 1) vs. (1 || 1):

        Rate   or   || 
or 2912710/s   -- -33% 
|| 4324590/s  48%   --

It seems the “and” and “&&” don’t make really different, but the “or” and “||” operator seems different.I don’t know why the “||” are 18-48% faster than the “or” …. and the “and” is only about 1-3% faster.

 

Now we check if they work as they should:

if(false() and true()){print;}print"END\n";
if(false() &&  true()){print;}print"END\n";
if(false() or  true()){print;}print"END\n";
if(false() ||  true()){print;}print"END\n";

 
if(true() and true()){print;}print"END\n";
if(true() &&  true()){print;}print"END\n";
if(true() or  true()){print;}print"END\n";
if(true() ||  true()){print;}print"END\n";


sub true() {print "sub true";return(1);}
sub false() {print "sub false";return(0);}

result looks good they abort if a true is impossible:

sub falseEND        
sub falseEND        
sub falsesub trueEND
sub falsesub trueEND
sub truesub trueEND 
sub truesub trueEND 
sub trueEND         
sub trueEND

That is more a check up and not really a Benchmark.

Perl reference vs. handing over Benchmark

I want to know if there is a different between a shift and normal handing over to a sub in Perl.This is only for scalar, for big data(array,hash) there is of course a different.

#!/usr/bin/perl
use strict;
use Benchmark qw(:all) ;
my $a = 100;
my $x = 100;
print "".sub_ref(\$a,\$x)."\n";
print "".sub_normal($a,$x)."\n";


cmpthese(-1, {
'ref'     => sub {sub_ref(\$a,\$x)},
'normal'  => sub {sub_normal($a,$x)},
});

sub sub_ref {
    return(${$_[0]}*${$_[1]});
}

sub sub_normal {
    return($_[0]*$_[1]);
}

Our result:

10000     
10000                          
            Rate    ref normal 
ref    2014189/s     --   -10% 
normal 2234181/s    11%     --

If its not necessary we should avoid references for scalar types, and try to iterate and not shift: Perl shift iterate Benchmark.But for Skalar values it is the fastest way to use prototypes.

perl map vs for Benchmark

I want to test what’s the different between a map function and a for iteration with Perl is:

#!/usr/bin/perl
use strict;
use Benchmark qw(:all) ;
my @arr = (1..10);


print sub_map(@arr).$/;
print sub_for(@arr).$/;



cmpthese(-1, {
'sub_map'  => sub {sub_map(@arr)},
'sub_for'  => sub {sub_for(@arr)},
});

sub sub_map {
    return join",", map {$_ * 2} @_;
}
sub sub_for {
    for (@_){
        $_ = $_*2;
    }
    return(join",",@_);
}

Our Benchmark result:

2,4,6,8,10,12,14,16,18,20        
2,4,6,8,10,12,14,16,18,20        
            Rate sub_for sub_map 
sub_for 129153/s      --    -10% 
sub_map 142717/s     11%      --

map is about 11% faster than a simple for loop.

Perl multiplication vs. division Benchmark

Today I want to compare the multiplication and division operation in Perl both could do the same, as example “100*2” is the same as “100/0.5”. And “100*0,5” is the same as “100/2”. Now let’s see what’s the Benchmark says:

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

my $x = 100;
my $y = 0;

cmpthese(-1, {
'div'  => sub {$y = $x/2},
'mul'  => sub {$y = $x*0.5},
});

cmpthese(-1, {
'div'  => sub {$y = $x/0.5},
'mul'  => sub {$y = $x*2},
});

We see in bout cases the multiplication is much faster:

          Rate  div  mul
div 14234063/s   -- -48%
mul 27235740/s  91%   --
          Rate  div  mul
div 16834936/s   -- -28%
mul 23301688/s  38%   --

To change the divisor to a factor calculate : factor = 1/divisor.This could be also useful if you have the same divisor for some calculations.