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

I was doing some research for a talk I'm doing here at work on how the using 'use constant' works at compile time and how the interpreters does substitutions. I've always been under the assumption that when you have a boolean constant say
use constant DEBUG => 0;
and if you use that in an if statement, then the entire if block will get compiled out if its false with no penalty at run time. I wanted to show how this works in my presentation and decided to run this sample code through B::Deparse to show how its optimized
use strict; use warnings; use constant DEBUG => 0; print "Hello World\n"; print "This is a debug Statement\n" if DEBUG; print "Goodbye Cruel Word\n";
I was suprised when I got this from the output of perl -MO=Deparse
use constant ('DEBUG', 0); use warnings; use strict 'refs'; print "Hello World\n"; '???'; print "Goodbye Cruel Word\n";
I thought to myself, "WTF is this '???'; string in void context, I assumed it would just remove the line". Now this isn't a huge deal because a string in void context is infinitely faster that an if statement, but I was surprised to see it. So surprised that I decided to bench it.
use strict; use warnings; use Benchmark qw( cmpthese ); use constant DEBUG => 0; my $b = 100; cmpthese -10, { constant => sub { my $a = 10 * $b; $a = 10 * $b if DEBUG; }, noconstant => sub { my $a = 10 * $b; }, };
And was surprised by the results...
Rate constant noconstant constant 1498536/s -- -17% noconstant 1796946/s 20% --
I mentioned my findings at our Perl Monger meeting last night and Rocco Caputo was also under the same assumption that there was zero penalty for using constants and they were compiled out.

Of course the penalty is _very_ small, but what's the point of the '???'; and why not just compile it out as I've heard over and over - the big benefit of using optimized subs as constants (use constant DEBUG => 1; or sub DEBUG () { 1 })

I also noticed that instead of ???; sometimes it gets compiled down to 0;

-biz-

Replies are listed 'Best First'.
Re: use constant and Compiler Optimizations
by almut (Canon) on Jul 18, 2008 at 17:05 UTC

    I think the '???' is just the default string printed "for the value of a constant that can't be determined because it was optimized away" (the latter is quoted from the docs).  You can change it with option -sv, e.g.

    $ perl -MO=Deparse,-sv"'UNKNOWN'." 698666.pl use constant ('DEBUG', 0); use warnings; use strict 'refs'; print "Hello World\n"; 'UNKNOWN'; print "Goodbye Cruel Word\n"; 698666.pl syntax OK
Re: use constant and Compiler Optimizations
by Joost (Canon) on Jul 18, 2008 at 19:58 UTC
    Stuff that gets optimized away does not necessarily take no time at all. They just should take less time than the unoptimized version in the general case (and on my system not optimizing is actually faster than optimizing for this code, which frankly surprised me). See the code below.

    Your example uses extremely fast constructs; the difference between doing a single multiplication and one "no-op" vs doing two multiplications (while still doing a relatively expensive function call each iteration for both constructs) is not very informative or useful.

    use strict; use warnings; use Benchmark qw( cmpthese ); use constant DEBUG => 0; my $debug = 0; my $b = 100; cmpthese -10, { constant => sub { my $a = 10 * $b; $a = 10 * $b if DEBUG; }, noconstant => sub { my $a = 10 * $b; }, var => sub { my $a = 10 * $b; $a = 10 * $b if $debug; }, };
    Update: as an aside, a really hard-core optimizer would eliminate ALL of the code in these examples.

      Stuff that gets optimized away does not necessarily take no time at all.

      If you look at the tree the compiler generates, you'll see the call to bar isn't even present in it. The only thing left is the nextstate which is an incredibly fast operation.

      >perl -MO=Concise,-exec -e"foo(); bar() if 0; baz();" 1 <0> enter 2 <;> nextstate(main 1 -e:1) v 3 <0> pushmark s 4 <#> gv[*foo] s/EARLYCV 5 <1> entersub[t2] vKS/TARG,1 6 <;> nextstate(main 1 -e:1) v <--- 7 <;> nextstate(main 1 -e:1) v 8 <0> pushmark s 9 <#> gv[*baz] s/EARLYCV a <1> entersub[t4] vKS/TARG,1 b <@> leave[1 ref] vKP/REFC -e syntax OK
        Yes, but funnily enough, doing an actual boolean test on an integer appears to sometimes be faster on my machine than doing the optimization. I'm not sure why, and the results vary. The output of my test script above with -10 and -2 as the first argument to timethis() cmpthese() respectively is:

        Rate var constant noconstant var 5014619/s -- -10% -40% constant 5551194/s 11% -- -33% noconstant 8292829/s 65% 49% --
        Rate constant var noconstant constant 5505020/s -- -17% -23% var 6604512/s 20% -- -7% noconstant 7120433/s 29% 8% --
Re: use constant and Compiler Optimizations
by ikegami (Patriarch) on Jul 18, 2008 at 20:21 UTC

    Deparse recreates the source from the tree the compiler generates. However, print "This is a debug Statement\n" if DEBUG; is optimized out of the tree. There are still traces of it which Deparse notices, but Deparse has no way of knowing what was originally in the source, so it displays '???'.