How perl compiles subs and methods
This was just added to perloptree.pod in B::C
Call a subroutine
subname(args...) =>
pushmark
args ...
gv => *subname
entersub
Call a method
Here we have several combinations to define the package and the method name, either compile-time (static as constant string), or dynamic as GV (for the method name) or PADSV (package name).
method_named holds the method name as sv if known at compile time. If not gv (of the name) and method is used. The package name is at the top of the stack. A call stack is added with pushmark.
Static compile time package and method
Class->subname(args...) =>
pushmark
const => PV "Class"
args ...
method_named => PV "subname"
entersub
Run-time package and compile-time method
$obj->subname(args...) =>
pushmark
padsv => GV *packagename
args ...
method_named => PV "subname"
entersub
Run-time package and run-time method
$obj->$meth(args...) =>
pushmark
padsv => GV *packagename
args ...
gvsv => GV *meth
method
entersub
Compile-time package and run-time method
Class->$meth(args...) =>
pushmark
const => PV "Class"
args ...
gvsv => GV *meth
method
entersub
Possible Optimizations
Optimization of static methods, e.g. Class->new
The purely static case is of course lame and can be massively optimized. See http://www.perl.com/pub/2000/06/dougpatch.html for the discussion of the old patch
The case
Class->subname(args...) =>
pushmark
const PV => "Class"
args ...
method_named => PV "subname"
entersub
can be optimized if &Class::subname is defined. Otherwise we'd need to ensure a complle-time const @ISA with some sort of new package attribute. :locked
for subs and packages is free. Only a package attribute make sense here really.
The optimization would get rid of the run-time stash lookup for the string "Class", use the string as first arg to entersub, and the method_named op can be replaced by gv => &Class::subname.
pushmark
const PV => "Class"
args ...
gv => *Class::subname
entersub
Voila, static method calls almost as fast as function calls, and no ops have to be added.
Benchmark
method calls get +3.95 % faster, and only 6.21% slower than sub calls. Without this optimization method calls are 10.87% slower than subs.
- Single Patch https://github.com/rurban/perl/commit/7a305aae0b335
- Full branch with more fixes: https://github.com/rurban/perl/commits/opt_methods
- Benchmark: https://gist.github.com/1113238
Optimization of methods with typed objects
A similar optimization can be done for typed objects if the object class contains that method, or the found method or defined package is locked. A Class declaration binds the package to $obj, and can therefore be optimized.
my Class $obj;
$obj->method(); # and `&Class::method` is defined,
# or `package Child :locked; `
# or sub `Child::method :locked {}` and `@Class::ISA` is Readonly
Easy enough.
Locked classes
Moose came up with immutable classes. If CORE needs to optimize such semantics, either we would need to come up with such a type hint, best for packages.
package NAME :locked # or
package NAME :immutable #or
package NAME :const #or
package NAME :readonly #or
package NAME :final
would make sense.
Or Readonly %{NAME::}; Readonly @NAME::ISA;
for all packages in the inheritance list until the method is found.
Or Moose could just implement those fixes in a CHECK
block via B::Generate or XS.
This would be much faster than inlining methods or caching for most methods.
Call CV's directly
In case of XSUB's or in any case when the CV slot of the subname glob is known not to be changed since compile-time (type-hints: sub :locked
:const
or :immutable
),
it would make sense to create a faster entersub perl op, which calls the CV directly and does not do the glob run-time lookup.
gv => *Class::subname
entersub
=>
entersubcv => &Class::subname
or maybe
cv => &Class::subname
entersubcv
entersubxs for the XSUB counterpart.
XSUB's should be seperated from normal CV's for Safe to work correctly. perl subs are generally safe, xsubs generally unsafe.
The dynamic package problem in "Optimization of static methods, e.g. Class->new" can also be solved with lexical subs. (NYI)
package foo;
my sub new { bless ... }
package main;
my $foo = new foo;
Since &foo::new is lexically defined, the compiler may statically optimize all sub and method calls to &foo::new to an improved entersub (the CV being a pad) without namespace lookup and dynamic method resolution, without having to const %foo:: and @foo::ISA.
Lexical definition guarantees compile-time definition, not overridable dynamically at run-time.