Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

OOP - Constant Vs. Subroutine

by 2xlp (Sexton)
on May 11, 2007 at 19:41 UTC ( [id://615001]=perlmeditation: print w/replies, xml ) Need Help??

I'm standardizing some libs for CPAN release and amazed at how much I switched between subs and 'use constant' for class variables

I know people are peculiar to each one, but thats not my question...

I decided to do a quick benchmark between the two and was surpised -- 'use constant' was significantly faster for use as class methods , 1/3 the time of calling a sub.

Does anyone know why? I was under the impression that use constant just creates a sub in the namespace -- so I expected it to have the same performance as using an explicitly defined subrouting. I was kind of shocked to see the difference in execution time.

The speed difference is negligble for most uses -- it took 10k calls to show the difference beyond a margin of error. but at 10 , 1k 10k, 100k and 1MM calls , the difference still is roughly 3x longer for an explicit sub than a 'use constant' defined sub.

Replies are listed 'Best First'.
Re: OOP - Constant Vs. Subroutine
by merlyn (Sage) on May 11, 2007 at 20:00 UTC
    From perldoc constant:
    When a constant is used in an expression, perl replaces it with its value at compile time, and may then optimize the expression further. In particular, any code in an "if (CONSTANT)" block will be optimized away if the constant is false.
    In other words, it's not a sub call any more.
      ah, that makes perfect sense.
Re: OOP - Constant Vs. Subroutine ()
by tye (Sage) on May 12, 2007 at 06:17 UTC

    Many reading this thread can probably already figure this out, but it hasn't been stated directly so...

    Note that you can get your subs to be just as fast1 by adding "()" after each's name. That is, replace something like:

    sub DoesNotChange { 5; }

    with

    sub DoesNotChange() { 5; }

    1 But who cares? If you couldn't notice the speed difference w/o resorting to Benchmark...

    - tye        

      by adding "()" after each's name
      ...which is exactly the reason why perl can optimize the sub away at compile time and makes them behave like use constant- note the two invocations
      qwurx [shmem] ~ > perl -MO=Concise,-exec -e 'use constant foo=>"5"; $c +=4+foo;' 1 <0> enter 2 <;> nextstate(main 70 -e:1) v 3 <$> const[IV 9] s 4 <#> gvsv[*c] s 5 <2> sassign vKS/2 6 <@> leave[1 ref] vKP/REFC -e syntax OK
      qwurx [shmem] ~ > perl -MO=Concise,-exec -e 'sub foo(){"5"}; $c=4+foo; +' 1 <0> enter 2 <;> nextstate(main 2 -e:1) v 3 <$> const[IV 9] s 4 <#> gvsv[*c] s 5 <2> sassign vKS/2 6 <@> leave[1 ref] vKP/REFC -e syntax OK

      are identical (except for the code of constant.pm loaded in the first), while

      qwurx [shmem] ~ > perl -MO=Concise,-exec -e 'sub foo{"5"}; $c=4+foo;' 1 <0> enter 2 <;> nextstate(main 2 -e:1) v 3 <$> const[IV 4] s 4 <0> pushmark s 5 <#> gv[*foo] s 6 <1> entersub[t3] sKS/TARG,1 7 <2> add[t4] sK/2 8 <#> gvsv[*c] s 9 <2> sassign vKS/2 a <@> leave[1 ref] vKP/REFC -e syntax OK

      leaves the sub as a sub since it doesn't have a null prototype list.

      --shmem

      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

        While I have no idea of Perl's internals, it also seems that it depends what the sub contains. Of course, this is logical, since you can't inline everything. Seems to be that things that can be reduced to constant values will be inlined, which also makes it easier to remember :) So, "4" will be inlined, "4+4" too, since it can also be optimized away. "@INC" won't, for example.


        Ordinary morality is for ordinary people. -- Aleister Crowley

      If you'd used constant instead of doing it manually and were also using perl 5.10, you'd also avoid the memory overhead for the function DoesNotChange. Anyone doing sub BAR () { FOO } won't get the optimization.

      ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      agreed -- who cares. this in no way, shape, or form will noticably affect any sort of perl code.

      I just brought this topic up, because I wanted to understand exactly why the speed difference existed. all i really care for is being able to stash & access class variables. I find using subs/constants is much easier than using 'our' scoped package variables for stashing info -- its just a quick $self->name instead of inspecting the object and moving on from there

      In any event, I did the following benchmark below to test the speed difference between the various options in writing & calling these methods

      Results vary on each run, but fall into 2 general categories...

      faster:

      sub get() { 'a'; } use constant get_constant=> 'a';

      slower:

      sub get { 'a'; } sub get { return 'a'; } sub get() { return 'a'; }

      and the code...
      package class; use strict; sub new { my ( $proto )= @_; my $class= ref($proto) || $proto; my $self= bless ( {} , $class ); return $self; } sub get_sub { 'a'; } sub get_sub_nullproto () { 'a'; } sub get_sub_return { return 'a'; } sub get_sub_return_nullproto () { return 'a'; } use constant get_constant=> 'a'; package main; use strict; use Benchmark qw[ cmpthese ]; my $object= class->new(); cmpthese -1, { get_sub => sub { my $a= $object->get_sub; }, get_sub__paren => sub { my $a= $object->get_sub(); }, get_sub_nullproto => sub { my $a= $object->get_sub_nullproto; }, get_sub_nullproto__paren => sub { my $a= $object->get_sub_nullprot +o(); }, get_sub_return => sub { my $a= $object->get_sub_return; }, get_sub_return__paren => sub { my $a= $object->get_sub_return(); } +, get_sub_return_nullproto => sub { my $a= $object->get_sub_return_n +ullproto; }, get_sub_return_nullproto__paren => sub { my $a= $object->get_sub_r +eturn_nullproto(); }, get_constant => sub { my $a= $object->get_constant; }, get_constant__paren => sub { my $a= $object->get_constant(); }, };

Re: OOP - Constant Vs. Subroutine
by perrin (Chancellor) on May 11, 2007 at 19:59 UTC
    The constant pragma creates a sub with a specific prototype, allowing it to be in-lined. For some uses in OO code, you're probably better off using class methods, since subclasses can override them.
      subclasses can override constants too.
        How would you do that? Would you have to call it like a method?
        Class->constant;
        That would prevent the in-lining behavior, but I guess it's no worse than a normally defined simple method.
Re: OOP - Constant Vs. Subroutine
by princepawn (Parson) on May 11, 2007 at 21:01 UTC
    Isn't Best Practice to use Readonly not constant?


    Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality

      You pays your money and takes your choice:

      #! perl -slw use strict; use Benchmark qw[ cmpthese ]; use constant { X => 0, Y => 1, Z => 2, P => 3, Q => 4, }; use Readonly; Readonly::Hash my %Fields => { X => 0, Y => 1, Z => 2, P => 3, Q => 4, }; sub prioritisedSort1 { return $a->[ P ] <=> $b->[ P ] or $a->[ Z ] <=> $b->[ Z ] or $a->[ Y ] <=> $b->[ Y ] or $a->[ X ] <=> $b->[ X ] or $a->[ Q ] <=> $b->[ Q ]; } sub prioritisedSort2 { return $a->[$Fields{P}] <=> $b->[$Fields{P}] or $a->[$Fields{Z}] <=> $b->[$Fields{Z}] or $a->[$Fields{Y}] <=> $b->[$Fields{Y}] or $a->[$Fields{X}] <=> $b->[$Fields{X}] or $a->[$Fields{Q}] <=> $b->[$Fields{Q}]; } our $N ||= 1e5; my @AoA1 = map { [ map{ int rand $_ } 1000, 100, 10, 500, 50 ] } 1 .. $N; my @AoA2 = map{ [ @$_ ] } @AoA1; my( @sorted1, @sorted2 ); cmpthese -1, { constant => sub{ @sorted1 = sort prioritisedSort1 @AoA1 }, Readonly => sub{ @sorted2 = sort prioritisedSort2 @AoA2 }, }; print "\nFirst and last 5 for constant"; print "[@$_]" for @sorted1[ 0 .. 4 ], [ qw[ - - - - - ] ], @sorted1[ -5 .. -1 ]; print "\nFirst and last 5 for Readonly"; print "[@$_]" for @sorted2[ 0 .. 4 ], [ qw[ - - - - - ] ], @sorted2[ -5 .. -1 ]; __END__ C:\test>junk8 -N=1e2 Rate Readonly constant Readonly 137/s -- -96% constant 3459/s 2427% -- First and last 5 for constant [226 44 0 8 3] [425 64 7 10 2] [790 25 8 20 27] [461 37 0 27 47] [151 51 3 31 35] [- - - - -] [734 15 9 482 6] [90 40 2 489 5] [957 21 9 491 45] [17 99 3 494 44] [585 50 7 495 22] First and last 5 for Readonly [226 44 0 8 3] [425 64 7 10 2] [790 25 8 20 27] [461 37 0 27 47] [151 51 3 31 35] [- - - - -] [734 15 9 482 6] [90 40 2 489 5] [957 21 9 491 45] [17 99 3 494 44] [585 50 7 495 22]

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
      Yes, per TheDamian advice. And see also merlyn's article. However, for my personal need, I don't see any advantages other than interpolating purpose.

      Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

        Oh?

        use constant FOO => { x => 0 }; print FOO->{x}; # 0 ++ FOO->{x}; print FOO->{x}; # 1

        Readonly also makes sure your entire data structure is readonly. constant only does that to the topmost level.

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      I should have clarified my intent...

      If you're looking for 'read only' variables, then yes-- use Readonly , not constant

      AFAIK : Readonly creates package variables , while constant creates inheritable subroutines. For my needs, constant was more appropriate -- though a sub with a null prototype seems to be even more appropriate.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://615001]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2024-03-29 11:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found