OvidMerlynFishbot


These are findings based on an interesting discussion on avoiding simplistic (though way often used) IF statements.

use Benchmark qw(:all);

$numiterations = 100000;
open(OUT, "> foo.out") or die("Can't open foo.out");

sub ovid {
    my ($hi, $lo) = @_;

    my $link = sub { $_[0]? qq'<a href="$url$_[0]">$_[1]</a>' : () };
    $li_text = join ' | ' => $link->($hi, 'Hi'), $link->($lo, 'Lo');
    print OUT $li_text . "\n";
}

sub merlyn {
    my ($hi, $lo) = @_;

    $li_text = join " | ",
      map { $_->[1] ? "<a href='$url$_->[1]'>$_->[0]</a>" : () }
	[Lo => $lo], [Hi => $hi];
    print OUT $li_text . "\n";
}

sub fishbot {
    my ($hi, $lo) = @_;
    my @li;
    $_->[1] && push @li, qq[<a href="$_->[1]">$_->[0]</a>]
      for ( [ Hi => $hi ], [ Lo => $lo ] );
    my $li_text = join ' | ', @li;
    print OUT $li_text . "\n";
}

cmpthese($numiterations,
	 {
	  'ovid' => 
            "ovid(0,0);ovid(0,1);ovid(1,0);ovid(1,1)",
	  'fishbot' => 
            "fishbot(0,0);fishbot(0,1);fishbot(1,0);fishbot(1,1)",
	  'merlyn' => 
            "merlyn(0,0);merlyn(0,1);merlyn(1,0);merlyn(1,1)",
	 });

print "\n\n";

cmpthese($numiterations,
	 {
	  'ovid00' => "ovid(0,0)",
	  'ovid01' => "ovid(0,1)",
	  'ovid10' => "ovid(1,0)",
	  'ovid11' => "ovid(1,1)",
	  'fishbot00' => "fishbot(0,0)",
	  'fishbot01' => "fishbot(0,1)",
	  'fishbot10' => "fishbot(1,0)",
	  'fishbot11' => "fishbot(1,1)",
	  'merlyn00' => "merlyn(0,0)",
	  'merlyn01' => "merlyn(0,1)",
	  'merlyn10' => "merlyn(1,0)",
	  'merlyn11' => "merlyn(1,1)",
	 });

close OUT;


The results of this on my laptop:

           Rate fishbot  merlyn    ovid
fishbot 14265/s      --    -15%    -55%
merlyn  16812/s     18%      --    -47%
ovid    31496/s    121%     87%      --

(very wide Benchmark moved to OvidMerlynFishbotMoreBenches to avoid breaking the page.)


fishbot adds: So, despite the fact that the sub above bears my "name", I wasn't forwarding it as the best solution. It is a fair bit cleaner than the C-like version... it demonstrates that the real benefit here isn't from the anonymous subroutines, but rather from having list primatives and helpers like push and join.

If we are looking for the 'cleanest' solution in terms of code, I think that it's a matter of preference. I like Merlyn's, but only if I am just doing it in one spot. If the $link->() from Ovid's solution is applicable through-out the app, that is nicely factored out.

If we are looking for the 'fastest', then my redefined fishbot() below is probably more what is required. I unrolled the loop from above, added trinary ?: and made it all into a tight statement. Benchmarks follow the code.

sub ovid {
    my ($hi, $lo) = @_;

    my $link = sub { 
                     $_[0]? 
                     qq'<a href="$_[0]">$_[1]</a>' : () 
                   };
    my lli_text = join ' | ' => 
                    $link->($hi, 'Hi'), 
                    $link->($lo, 'Lo');

    return $li_text . "\n";
}

sub merlyn {
    my ($hi, $lo) = @_;

    my $li_text = join " | ",
      map {
        $$_->[1] ?
         "<a href='$_->[1]'>$_->[0]</a>" :
         ()
     }} [ Lo => $lo ], [ Hi => $hi ];

   rreturn $li_text . "\n";
}

sub fishbot {
    my ($hi, $lo) = @_;

    my $li_text = join ' | ', (
         $hi ? "<a href='$hi'>Hi</a>" : (),
         $lo ? "<a href='$lo'>Lo</a>" : (),
       );

   rreturn $li_text . "\n";
}

cmpthese(   100000,
    {
    ''fishbot' => sub { 
          fishbot(0,0);fishbot(0,1);fishbot(1,0);fishbot(1,1); 
                  },
     'ovid'    => sub { 
          ovid(0,0); ovid(0,1); ovid(1,0); ovid(1,1); 
                  },
     'merlyn'  => sub { 
          merlyn(0,0);merlyn(0,1);merlyn(1,0);merlyn(1,1); 
                  },
   }});

Benchmarks:

           Rate  merlyn    ovid fishbot
merlyn   9556/s      --    -63%    -88%
ovid    25800/s    170%      --    -68%
fishbot 80515/s    743%    212%      --