Which form of subroutine call is more
efficient: object methods or function calls? Let's
look at the overhead.
13.6.1. The Overhead with Light Subroutines
Let's do some benchmarking.
We will start by using empty methods, which will allow us to measure
the real difference in the overhead each kind of call introduces. We
will use the code in Example 13-15.
Example 13-15. bench_call1.pl
package Book::LightSub;
use strict;
use Benchmark;
sub bar { };
timethese(1_000_000, {
method => sub { Book::LightSub->bar( ) },
function => sub { Book::LightSub::bar('Book::LightSub');},
});
The two calls are equivalent, since both pass the class name as their
first parameter; function does this explicitly,
while method does this transparently.
We see that the function call is almost twice as fast as the method
call: 1.41 CPU clocks compared to 2.54. Why is this? With a function
call we give Perl the fully qualified function name and set up its
call stack ourselves by passing in the package (class) name. With a
method call Perl must work out the package (class) name for itself,
then search the inheritance tree to find the required method, then
set up the call stack. So in the case of a method call Perl must do a
lot more work and is therefore slower.
Perl 5.6.0 and higher do better method caching than older Perl
versions. Book::LightSub->method( ) is a little
bit faster (as it does better constant-folding magic), but not
Book::LightSub->$method( ). The improvement
does not address the @ISA lookup that still
happens in either case.
13.6.2. The Overhead with Heavy Subroutines
The above results don't mean that you
shouldn't use methods. Generally your functions do
something, and the more they do the less significant the overhead of
the call itself becomes. This is because the calling time is
effectively fixed and usually creates a very small overhead in
comparison to the execution time of the method or function itself.
This is demonstrated by the next benchmark (see Example 13-16).
Example 13-16. bench_call2.pl
package Book::HeavySub;
use strict;
use Benchmark;
sub bar {
my $class = shift;
my ($x, $y) = (100, 100);
$y = log ($x ** 10) for (0..20);
};
timethese(100_000, {
method => sub { Book::HeavySub->bar( ) },
function => sub { Book::HeavySub::bar('Book::HeavySub');},
});
You can see that in the first and second benchmarks the difference
between the function and method calls is almost the same: 0.22 and
0.26 CPU clocks, respectively.
In cases where functions do very little work, the overhead might
become significant. If your goal is speed you might consider using
the function form, but if you write a large and
complicated application, it's much better to use the
method form, as it will make your code easier to
develop, maintain, and debug. Saving programmer time over the life of
a project may turn out to be the most significant cost factor.
13.6.3. Are All Methods Slower Than Functions?
Some modules'
APIs are
misleading—for example, CGI.pm allows you to
execute its subroutines as functions or as methods. As you will see
in a moment, its function form of the calls is slower than the method
form because it does some voodoo behind the scenes when the function
form call is used:
use CGI;
my $q = new CGI;
$q->param('x', 5);
my $x = $q->param('x');
versus:
use CGI qw(:standard);
param('x', 5);
my $x = param('x');
Let's benchmark some very light calls (see Example 13-17) and compare. We would expect the methods to
be slower than functions, based on the previous benchmarks.
Example 13-17. bench_call3.pl
use Benchmark;
use CGI qw(:standard);
$CGI::NO_DEBUG = 1;
my $q = new CGI;
my $x;
timethese(2_000_000, {
method => sub {$q->param('x',5); $x = $q->param('x'); },
function => sub { param('x',5); $x = param('x'); },
});
The benchmark is written in such a way that all initializations are
done at the beginning, so that we get as accurate performance figures
as possible:
As you can see, methods are faster than functions, which seems to be
wrong. The explanation lies in the way CGI.pm is
implemented. CGI.pm uses some fancy tricks to make
the same routine act both as a method and as a plain function. The
overhead of checking whether the arguments list looks like a method
invocation or not will mask the slight difference in time for the way
the function was called.
If you are intrigued and want to investigate further by yourself, the
subroutine you should explore is called
self_or_default. The first line of this function
short-circuits if you are using object methods, but the whole
function is called if you are using the function-call forms.
Therefore, the function-call form should be slightly slower than the
object form for the CGI.pm module, which you
shouldn't be using anyway if you have
Apache::Request and a real templating system.
13.5. Keeping a Small Memory Footprint
13.7. Using the Perl stat( ) Call's Cached Results