Hello everyone! I'm new to this maillist. Actually i'm new to perl core
development flow too.
I've used perl for 10 years and last 3 months i was learning all the perl
source codes because wasn't
satisfied with perl's OOP perfomance (i mean speed).
For example i wanted to create a very lightweight XS object which would be
used in a project very intensively (create/destroy)
but i realised that only a constructor which does 'bless' runs just
1.500.000/s (on a fast server).
No matter how fast my XS methods are (3m/s, 10m/s or 20m/s), because just
constructing an object takes so long.
So i've decided to make some optimizations.
As with any other long-term stories we need something to start with.
I've done some work speeding up class method calls, caching stashes and
method and so on.
Here are the results:
Class method calls speeded up by 50-100% (MyClass->func)
Class/object super method calls speeded up by 65-100%
(MyClass->SUPER::func, $obj->SUPER::func)
Class/object redirect method calls speeded up by
250-400%(MyClass->OtherClass::func, $obj->OtherClass::func)
Object constructors (Class method + bless) speeded up by 100-150%
Misc fixes and optimizations (see git comment)
Also any XS code which explicitly or implicitly uses gv_stashpvn,
gv_fetchmeth_pvn & co will run faster.
Benchmark was performed on Core i7 3.2Ghz, MacOSX 10.6
This test calls empty methods 1000 times per iter (so cl_meth actually runs
9.954.000/s).
Benchmark script attached.
Patched perl (blead based)
Benchmark: running cl_can_real, cl_can_unreal, cl_meth, cl_methred,
cl_super, new, o_meth, o_next2, o_next4, o_super for at least 1 CPU
seconds...
cl_can_real: 1 wallclock secs ( 1.09 usr + 0.00 sys = 1.09 CPU) @
8220.18/s (n=8960)
cl_can_unreal: 1 wallclock secs ( 1.09 usr + 0.00 sys = 1.09 CPU) @
9863.30/s (n=10751)
cl_meth: 1 wallclock secs ( 1.08 usr + 0.00 sys = 1.08 CPU) @
9954.63/s (n=10751)
cl_methred: 1 wallclock secs ( 1.07 usr + 0.00 sys = 1.07 CPU) @
9134.58/s (n=9774)
cl_super: 1 wallclock secs ( 1.06 usr + 0.00 sys = 1.06 CPU) @
7801.89/s (n=8270)
new: 1 wallclock secs ( 1.09 usr + 0.00 sys = 1.09 CPU) @
4110.09/s (n=4480)
o_meth: 1 wallclock secs ( 1.07 usr + 0.00 sys = 1.07 CPU) @
10576.64/s (n=11317)
o_next2: 1 wallclock secs ( 1.07 usr + 0.00 sys = 1.07 CPU) @
660.75/s (n=707)
o_next4: 1 wallclock secs ( 1.06 usr + 0.00 sys = 1.06 CPU) @
225.47/s (n=239)
o_super: 1 wallclock secs ( 1.08 usr + 0.00 sys = 1.08 CPU) @
10478.70/s (n=11317)
Vanilla perl (blead)
Benchmark: running cl_can_real, cl_can_unreal, cl_meth, cl_methred,
cl_super, new, o_meth, o_next2, o_next4, o_super for at least 1 CPU
seconds...
cl_can_real: 1 wallclock secs ( 1.06 usr + 0.00 sys = 1.06 CPU) @
1267.92/s (n=1344)
cl_can_unreal: 1 wallclock secs ( 1.09 usr + 0.01 sys = 1.10 CPU) @
1285.45/s (n=1414)
cl_meth: 1 wallclock secs ( 1.05 usr + 0.01 sys = 1.06 CPU) @
5070.75/s (n=5375)
cl_methred: 1 wallclock secs ( 1.09 usr + 0.00 sys = 1.09 CPU) @
1896.33/s (n=2067)
cl_super: 1 wallclock secs ( 1.06 usr + 0.00 sys = 1.06 CPU) @
4225.47/s (n=4479)
new: 2 wallclock secs ( 1.10 usr + 0.01 sys = 1.11 CPU) @
1613.51/s (n=1791)
o_meth: 1 wallclock secs ( 1.06 usr + 0.00 sys = 1.06 CPU) @
10143.40/s (n=10752)
o_next2: 1 wallclock secs ( 1.07 usr + 0.00 sys = 1.07 CPU) @
570.09/s (n=610)
o_next4: 1 wallclock secs ( 1.07 usr + 0.00 sys = 1.07 CPU) @
195.33/s (n=209)
o_super: 1 wallclock secs ( 1.07 usr + 0.00 sys = 1.07 CPU) @
6698.13/s (n=7167)
I've tested patch on couple of projects in our company. Perfomance boost
was about 15-20%
Nearest plans:
1) speedup next::method, next::can, maybe::next::method by about 500%
2) speedup perlhash lookups
3) see if we can speedup pp_entersub
Git: https://github.com/syberrus/perl5/tree/syber/oop
Perl passes all the core tests (a couple of tests were removed as perl no
longer has such behaviour).
Tested on Mac, FreeBSD and Linux, threaded and non-threaded.
Several CPAN modules don't. All of them use undocumented behaviour of
method cache.
Most popular of them:
Catalyst (Catalyst::ClassData). - doesn't work
Line 19
$meta->namespace->{$attribute} = \$_[1];
Wrong usage of typeglobs. This code sets an RV to package stash.
This code works because current perl already has a typeglob $attribute
because of method caching right in stash's HV
so that glob_assign_ref called and sets SvRV(RV) to a glob's slot.
With my patch perl no longer stores method cache in stash's HV and
Catalyst::ClassData sets an RV to a stash which
is turned into a const sub next call.
Bugfix:
no strict 'refs';
*{"${pkg}::$attribute"} = \$_[1];
Variable::Magic - works but doesn't pass one test
test 35-stash.t casts GET and SET magic to a package's stash and tests
a lot of perl internal behaviour
related to how perl caches methods.
Should either fix the test to meet new behaviour or remove this test at
all as casting magic to package's stash
leads to a strange behaviour even in current perl.
All modules that use old perl4-style package separators (') don't work.
(for example MyClass'Plugin'Logger).
I can return this support back but i don't see any reason for using that
ancient syntax.
I understand that there are no POD changes, docs and so on.
Firstly i would like to know your opinion about this.
Thanks everyone for great work!
--
Oleg Pronin,
CTO, Co-Founder,
Crazy Panda LTD
CP Decision LTD