http://qs1969.pair.com?node_id=467502


in reply to Making an Object faster?

Paraphrase:Is it better to use subroutines or objects?

The questions to ask yourself are:

  1. Do the three subroutines operate on a common piece or set of data?

    If not, there is no point in making your 3 subroutines (class) methods of an uninstantiable class.

  2. If so, is there any benefit to caching the data they process (as instance data)?

    If the data is passed in, processed, returned, then used and/or disposed of, there maybe no point in caching it.

    On the other hand, if the data is accumulate slowly externally, then processed through one sub, retrieved and retained unmodified, and then later passed again to one or more of the subs, it may make sense to accumulate the data into an object instance, call methods on that object instance and only retrieve it once the data has been completely processed.

  3. Will you need to maintain more that one piece or set of data used (separately) by the subs at once?

    A one-instance (at a time) class can be useful, but is less obviously desirable than one where mutliple instances exist at one time.

  4. How much work do the subroutines do when you call them?

    If they only do some small amount of processing each time, but are called many, many times, then any extra overheads involved in using OO rather than subroutines will be magnified.

    If the subs/methods do a reasonably substantial amount of processing at each call, then the (fixed) overheads will becomes insignificant, but if each call does only a small amount of processing, the overhead can become a substantial part of your processing time.

If you decide to go the OO route, then there are several flavours of OO construction possible. Which is appropriate will depend upon the performance of your hardware and the time constraints of your application, along with other more esoteric considerations like 'OO-purity', 'maintainance' and inheritability.

By way of crude demonstration, the following benchmark uses subroutines exported from a module ('subs'), and five flavours of OO, (OO1 .. OO5) with increasing levels of 'OO-ness', to do the same thing. The -N=nn parameter represents the amount of work done within each call, with low numbers being a small amount and larger numbers, more:

P:\test>467452 -N=1 Rate OO5 OO4 OO3 OO2 OO1 subs OO5 18408/s -- -56% -69% -76% -83% -91% OO4 41813/s 127% -- -29% -45% -60% -79% OO3 58681/s 219% 40% -- -23% -44% -71% OO2 76466/s 315% 83% 30% -- -27% -62% OO1 105426/s 473% 152% 80% 38% -- -48% subs 203292/s 1004% 386% 246% 166% 93% -- P:\test>467452 -N=100 Rate OO5 OO4 OO3 OO2 OO1 subs OO5 1886/s -- -7% -27% -29% -68% -69% OO4 2023/s 7% -- -21% -24% -66% -67% OO3 2568/s 36% 27% -- -3% -56% -58% OO2 2652/s 41% 31% 3% -- -55% -57% OO1 5886/s 212% 191% 129% 122% -- -4% subs 6100/s 223% 202% 138% 130% 4% -- P:\test>467452 -N=10000 Rate OO4 OO5 OO2 OO3 OO1 subs OO4 14.5/s -- -1% -26% -27% -68% -69% OO5 14.6/s 1% -- -26% -27% -68% -69% OO2 19.7/s 36% 35% -- -1% -56% -58% OO3 19.9/s 37% 36% 1% -- -56% -57% OO1 45.2/s 211% 209% 129% 127% -- -3% subs 46.8/s 222% 219% 138% 135% 4% -- P:\test>467452 -N=100000 Rate OO4 OO5 OO2 OO3 OO1 subs OO4 1.42/s -- -1% -25% -26% -68% -68% OO5 1.44/s 1% -- -24% -25% -67% -67% OO2 1.89/s 34% 32% -- -1% -57% -57% OO3 1.92/s 36% 34% 2% -- -56% -56% OO1 4.39/s 210% 206% 132% 129% -- -0% subs 4.41/s 212% 207% 133% 130% 0% --

As you can see, subroutines are quicker than even the simplest level of OO, though at the highest levels of work/call, the difference between subs and the simple forms of OO are negligable. And in these cases, the potential maintainance benefits derivable from OO may well be your deciding factor. Whether the OO-purity arguments should be weighted into you considerations will probably depend upon the performance requirements of your application.

You pay's your money and takes your choice.

Benchmark and Module code

#! perl -slw use strict; use Benchmark qw[ cmpthese ]; use Dummy1; use Dummy2; use Dummy3; use Dummy4; use Dummy5; use Dummy6; our $N ||= 100; our @data = 1 .. $N; cmpthese -3, { subs=> q[ my @modified = sumem( reversem( @data ) ); ], OO1 => q[ my $obj = new Dummy2; my @modified = $obj->sumem( $obj->reversem( @data ) ); ], OO2 => q[ my $obj = new Dummy3( @data ); $obj->reversem; my @modified = $obj->sumem; ], OO3 => q[ my $obj = new Dummy4( @data ); $obj->reversem; my @modified = $obj->sumem; ], OO4 => q[ my $obj = new Dummy5( @data ); $obj->reversem; my @modified = $obj->sumem; ], OO5 => q[ my $obj = new Dummy6( @data ); $obj->reversem; my @modified = $obj->sumem; ], };

Dummy1.pm

use strict; package Dummy1; our @ISA = 'Exporter'; our @EXPORT = qw[ sumem reversem ]; sub sumem { my $sum += $_ for @_; return $sum; } sub reversem { return map{ scalar reverse } @_; } 1;

Dummy2.pm

#! perl -slw use strict; package Dummy2; sub new { my $class = shift; return bless [], $class; } sub sumem { my $self = shift; my $sum += $_ for @_; return $sum; } sub reversem { my $self = shift; return map{ scalar reverse } @_; } 1;

Dummy3.pm

#! perl -slw use strict; package Dummy3; sub new { my $class = shift; return bless [ @_ ], $class; } sub sumem { my $self = shift; my $sum += $_ for @{ $self }; return $sum; } sub reversem { my $self = shift; @{ $self } = map{ scalar reverse } @{ $self }; } 1;

Dummy4.pm

#! perl -slw use strict; package Dummy4; sub new { my $class = shift; return bless { data => \@_ }, $class; } sub sumem { my $self = shift; my $sum += $_ for @{ $self->{ data } }; return $sum; } sub reversem { my $self = shift; @{ $self->{ data } } = map{ scalar reverse } @{ $self->{ data } } +; } 1;

Dummy5.pm

#! perl -slw use strict; package Dummy5; sub new { my $class = shift; my $self = bless {}, $class; $self->_setData( @_ ); return $self; } sub _getData { my $self = shift; return @{ $self->{ data } }; } sub _setData { my $self = shift; return @{ $self->{ data } } = @_; } sub sumem { my $self = shift; my $sum += $_ for $self->_getData(); return $sum; } sub reversem { my $self = shift; $self->_setData( map{ scalar reverse } $self->_getData() ); } 1;

Dummy6.pm

#! perl -slw use strict; package Dummy6; my %dummies; sub new { my $class = shift; my $self = bless {}, $class; $dummies{ $self } = {}; $self->_setData( @_ ); return $self; } sub _getData { my $self = $dummies{ +shift }; return @{ $self->{ data } }; } sub _setData { my $self = $dummies{ +shift }; return @{ $self->{ data } } = @_; } sub sumem { my $self = shift; my $sum += $_ for $self->_getData(); return $sum; } sub reversem { my $self = shift; $self->_setData( map{ scalar reverse } $self->_getData() ); } 1;

Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.

Replies are listed 'Best First'.
Re^2: Making an Object faster?
by Your Mother (Archbishop) on Jun 17, 2005 at 02:39 UTC

    When ++ isn't enough. Nice treatment of the kind of question that can sometimes get: "Code it and benchmark it yourself."