Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

I understand the call for not falling into the trap of premature optimisation, but there are times when performance is critical. The example I will use is in a graphics app like rbc's recent Z Buffer Demo code. (Nice code BTW rbc)

When he first posted this (as a SoPW) I downloaded it and took a look. I've written several 3D graphics libraries down the years, though I've usually opted for hidden line removal rather than zbuffer, which has it limitations, so I was interested. Unfortunately, trying to help debug the code was hampered by it's rather poor performance. So I profiled in in the hope of reducing the edit debug cycle.

One of the major causes of the slow performance is his (correct to OO principles) use of accessor methods within the Vector2D and Vector3D classes. Within these he is using the 'right way', shifting or list assigning his parameters into named vars. This means that you end up with lots of small subs doing

sub setx { my ($self, $value) = @_; $self->{_x}=$value; }

As there are some 250,000 calls to routines of this nature in a single draw cycle, modifying these to do

sub setx { $_[0]->{_x} = $_[1] }

has a substantial effect on the performance of the code. Reducing the draw time of the cube, from around 2 minutes (on my box) to around 20 secs. Still not fast enough to allow the dynamic rotation of the cube (though the major bottleneck is now the Tk; module rather than the vector code itself), but still substantial in this case to be worth having. Of course, the downside is that readability suffers greatly with $_[0] representing a vector in one place a constant in the next and something completely different elsewhere, which doesn't make for readable code.

However, I thought about this and think there is a simple solution (that I haven't seen elsewhere, but may have been done before) to using speed gains of the direct parameter access, without loosing the clarity of named parameters. The clue is in the word I used above. Constants.

Instead of using either of the versions above, I think that I will adopt this

use constant SELF => 0; use constant VALUE=> 1; sub setx{ $_[SELF]->{_x} = $_[VALUE]; }

Which I believe gives the best of both worlds of clarity and self documentation, and speed.

The use of constants appears to have negligable impact on the performance of the direct access, as one would expect, but on simple accessor functions renders around a 25% performance boost over named parameters using my. Possibly not worth the effort for non-performance critical apps, but worth having where it is needed.

I actually don't see any real reasons (yet) for not using this in place of shifting to my variables where the side effects are potentially beneficial, or at least not detrimental.

Any thoughts?

My benchmark for the interested.

#! perl -sw use strict; package Things1; sub new { my $class = shift; my $self = { _thing=>0 }; return bless $self, $class; } sub get_it { my $self = shift; return $self->{_thing}; } sub set_it { my $self = shift; my $value = shift; return $self->{_thing} = $value; } 1; package Things2; sub new { my ($class) = @_; my $self = { _thing=>0 }; return bless $self, $class; } sub get_it { my ($self) = @_; return $self->{_thing}; } sub set_it { my ($self, $value) = @_; return $self->{_thing} = $value; } 1; package Things3; sub new { return bless { _thing=>0 }, $_[0]; } sub get_it { return $_[0]->{_thing}; } sub set_it { return $_[0]->{_thing} = $_[1]; } 1; package Things4; use constant CLASS => 0; use constant SELF => 0; use constant VALUE => 1; sub new { return bless { _thing=>0 }, $_[CLASS]; } sub get_it { return $_[SELF]->{_thing}; } sub set_it { return $_[SELF]->{_thing} = $_[VALUE]; } 1; package main; use strict; use vars qw/$COUNT $RUNS/; use Benchmark qw/cmpthese/; $\=$/; print $COUNT,,$RUNS; my $thing1 = new Things1; print $thing1; print "Value:", $thing1->get_it(); $thing1->set_it(99999); print "Value:", $thing1->get_it(); my $thing2 = new Things2; print $thing2; print "Value:", $thing2->get_it(); $thing2->set_it(99999); print "Value:", $thing2->get_it(); my $thing3 = new Things3; print $thing3; print "Value:", $thing3->get_it(); $thing3->set_it(99999); print "Value:", $thing3->get_it(); my $thing4 = new Things4; print $thing4; print "Value:", $thing4->get_it(); $thing4->set_it(99999); print "Value:", $thing4->get_it(); cmpthese( $::RUNS, { shift => sub{ my $thing = new Things1; $thing->set_it( 1 + $th +ing->get_it() ) for 1 .. $::COUNT; }, assign => sub{ my $thing = new Things2; $thing->set_it( 1 + $th +ing->get_it() ) for 1 .. $::COUNT; }, direct => sub{ my $thing = new Things3; $thing->set_it( 1 + $th +ing->get_it() ) for 1 .. $::COUNT; }, constant=> sub{ my $thing = new Things4; $thing->set_it( 1 + $thin +g->get_it() ) for 1 .. $::COUNT; }, } ); __END__ c:\test>212101 -COUNT=10000 -RUNS=10 1000010 Things1=HASH(0x1bdf190) Value:0 Value:99999 Things2=HASH(0x1bdf1b4) Value:0 Value:99999 Things3=HASH(0x1bd51c8) Value:0 Value:99999 Things4=HASH(0x1bd51f8) Value:0 Value:99999 Benchmark: timing 10 iterations of assign, constant, direct, shift ... assign: 4 wallclock secs ( 3.64 usr + 0.00 sys = 3.64 CPU) @ 2 +.75/s (n=10) constant: 3 wallclock secs ( 3.18 usr + 0.00 sys = 3.18 CPU) @ 3 +.15/s (n=10) direct: 3 wallclock secs ( 3.18 usr + 0.00 sys = 3.18 CPU) @ 3 +.14/s (n=10) shift: 4 wallclock secs ( 3.96 usr + 0.00 sys = 3.96 CPU) @ 2 +.53/s (n=10) Rate shift assign direct constant shift 2.53/s -- -8% -20% -20% assign 2.75/s 9% -- -12% -13% direct 3.14/s 24% 14% -- -0% constant 3.15/s 25% 14% 0% -- c:\test>

Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for cyclone or a hurricane, in which case 16 hour shifts are mandatory.
Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

SELF

In reply to Micro optimisations can pay off, and needn't be a maintenance problem by BrowserUk

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2024-04-19 07:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found