damian1301 has asked for the wisdom of the Perl Monks concerning the following question:

NOTE: I know many of you may think of this is a Meditation but I wasn't quite sure where it would fit.

Well in a while of being here I've noticed that many of you have loops after what whats it supposed to do. Example.
print "Hey" if $response = "hi";
I was just thinking/wondering if this is faster or slower than the traditional..
if($response = "hi"){ print "Hey"; }
Im wondering if the first example slower because it grabs the first argument (print "Hey") and stores it then moves on to validate if the second argument is true (if $response = "Hi";) if its false then throws it out. While, the other validates if its true and if not then skips it (or the opposite if you use the unless loop). I was just wondering what your thoughts were on this. thanks, bye

Wanna be perl hacker.
Dave AKA damian

Replies are listed 'Best First'.
Re: Loops and speed
by repson (Chaplain) on Dec 17, 2000 at 08:32 UTC
    Time for the trusty old Benchmark.pm
    But first check with B::Deparse
    print "Hey" if $responce eq "hi"; # is optimised/parsed to: (($responce eq "hi") and print("Hey"));
    So our favourite construct doesn't do what we think it will....

    I couldn't get a definate answer out of Benchmark, but generally the fastest method is:
    if ($responce eq "hi") { print "Hey" }
    But the others aren't far behind

    print "Hey" if $responce eq "hi"; (($responce eq "hi") and print("Hey");
    You would expect the second one of those to be faster if anything since that is what the first one is changed to, but maybe my tests were screwy.

    Tests run on linux 5.005_03

    Update: Okay I had another look at my Deparse results in relation to Adam's reply below. I generally use perl -MO=Deparse,-p -e ... since it adds extra bracketing and things so I can tell exactly how things are working in terms of precedence and stuff. It seems that using -p rearranges the foo if blah; to blah and foo but without -p, Deparse leaves it alone. So my question is, which result is more correct, does perl do the full transformation like Deparse,-p or does it just do the equivilent to plain Deparse before the next stage of complimation?

    Here's where my benchmarks came from:

    my @foo = map {int rand 3} 1..1000; timethese(-15, { one => 'for (@foo) { print "$_\n" if $_; }', two => 'for (@foo) { if ($_) {print "$_\n";} }', three => 'for (@foo) { ($_ and print("\n")); }', } ); __END__ Result: one: 15 wallclock secs (14.99 usr + 0.01 sys = 15.00 CPU) @ 9421.60 +/s (n=141324) two: 16 wallclock secs (15.01 usr + -0.01 sys = 15.00 CPU) @ 9217.53 +/s (n=138263) three: 14 wallclock secs (15.43 usr + 0.01 sys = 15.44 CPU) @ 8842.75 +/s (n=136532) another run one: 6 wallclock secs ( 7.00 usr + 0.01 sys = 7.01 CPU) @ 9369.33 +/s (n=65679) three: 7 wallclock secs ( 7.57 usr + 0.00 sys = 7.57 CPU) @ 9237.78 +/s (n=69930) two: 7 wallclock secs ( 7.44 usr + 0.00 sys = 7.44 CPU) @ 8758.33 +/s (n=65162) and another two: 9 wallclock secs ( 7.00 usr + 0.00 sys = 7.00 CPU) @ 9757.14 +/s (n=68300) one: 6 wallclock secs ( 7.00 usr + 0.01 sys = 7.01 CPU) @ 9133.24 +/s (n=64024) three: 5 wallclock secs ( 7.06 usr + 0.00 sys = 7.06 CPU) @ 8828.47 +/s (n=62329)
    Like I said it wasn't totally constant, so what did I do wrong in creating those benchmarks?
      This Benchmark exhibits some of the same problems I discussed in Re: Re: Craftier. In particular, @foo is a lexical variable which is not accessible in the Benchmark package, where your code snippets are being compiled. All of your snippets are working on an empty @foo. (You might have noticed that, despite the three print statements, your code never actually printed anything.)

      Since you're passing quoted strings, all you're really testing is the time to compile each snippet, which is not a very useful measure. Also note that the third snippet omitted $_ in the print statement.

      Here's an improved benchmark.

      #!/usr/local/bin/perl -w use strict; use Benchmark; my @foo = map {int rand 3} 1..100; open(OUT, ">bm.out") or die "$!\n"; # Note: bm.out will be a large file when this is done. :) timethese(-10, { mod => sub { for (@foo) { print OUT "$_" if $_; } print OUT "\n"; }, block => sub { for (@foo) { if ($_) { print OUT "$_"; } } print OUT "\n"; }, and => sub { for (@foo) { $_ and print OUT "$_"; } print OUT "\n"; }, } ); __END__ Benchmark: running and, block, mod, each for at least 10 CPU seconds.. +. and: 3554.42/s block: 3530.86/s mod: 3511.90/s
      With a re-structured benchmark, all three options are roughly equivalent, as you would expect.

      However, there's also a lot of extra code in each snippet, which could be obscuring actual differences between the code we're really trying to test. In particular, I'd expect the print to be slow relative to the rest of the code.

      Here's another benchmark.

      #!/usr/local/bin/perl -w use strict; use Benchmark; my $foo = 0; # $foo will be toggled between true and false my $x; timethese(-10, { mod => sub { $x = 1 if $foo ^= 1 }, block => sub { if ($foo ^= 1) { $x = 1 } }, and => sub { $foo ^= 1 and $x = 1 }, } ); __END__ Benchmark: running and, block, mod, each for at least 10 CPU seconds.. +. and: 233020.67/s block: 230307.69/s mod: 234463.20/s Benchmark: running and, block, mod, each for at least 10 CPU seconds.. +. and: 237002.40/s block: 236647.46/s mod: 226841.12/s
      This benchmark also shows no significant difference between the three forms.

      We can draw two conclusions from these results: first, that it is important to have a well-constructed benchmark; and second, that the three forms of if/then are equivalent in terms of efficiency.

Re: Loops and speed
by Adam (Vicar) on Dec 17, 2000 at 08:49 UTC
    I too tried the Deparser first, but found that it wasn't changing the code on my machine:
    C:\>perl -MO=Deparse -we "$a = 'doit'; print \"hi\n\" if $a eq 'doit'" $a = 'doit'; print "hi\n" if $a eq 'doit'; -e syntax OK C:\>perl -MO=Deparse -we "$a = 'doit'; if ($a eq 'doit'){print \"hi\n\ +"}" $a = 'doit'; if ($a eq 'doit') { print "hi\n"; } -e syntax OK
    Then I tried the ol' benchmark and found that the two are more or less equivelent, which is what I expected. If anything the reverse method is slightly faster.
    #!perl -w use strict; use Benchmark; sub Time { use vars qw( $clause ); $clause = shift; timethese( -5, { normal => sub { my $i = 0; if( $clause ) { ++$i } }, reverse => sub { my $i = 0; ++$i if $clause }, } ); } Time(1); Time(0); __END__ Output: Benchmark: running normal, reverse, each for at least 5 CPU seconds... normal: 6 wallclock secs ( 5.00 usr + 0.00 sys = 5.00 CPU) @ 97 +5640.40/s (n=4878202) reverse: 5 wallclock secs ( 5.21 usr + 0.00 sys = 5.21 CPU) @ 10 +03193.28/s (n=5226637) Benchmark: running normal, reverse, each for at least 5 CPU seconds... normal: 5 wallclock secs ( 5.27 usr + 0.00 sys = 5.27 CPU) @ 15 +65443.64/s (n=8249888) reverse: 4 wallclock secs ( 5.11 usr + 0.00 sys = 5.11 CPU) @ 15 +85628.38/s (n=8102561)
Re: Loops and speed
by merlyn (Sage) on Dec 17, 2000 at 09:43 UTC

      *grin* That's too funny. Of course, a simple -w flag would have caught that right away...

Re: Loops and speed
by Anonymous Monk on Dec 18, 2000 at 03:36 UTC
    i was under the (perhaps misguided) impression that the
    'do_something() if $condition'
    form would be marginally faster than the
    'if ($condition) { do_something() }'
    as the second form opens a block, and thereby a new scope, which requires extra work.

    while it seems to be the case that the first form is indeed marginally faster, it really is marginal. thanks for the benchmarks guys!

    d_i_r_t_y