| Store | Cart

Speeding up perl's OOP. Step 1.

From: Олег Пронин <syb...@crazypanda.ru>
Thu, 31 Jul 2014 21:52:34 +0400
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

Recent Messages in this Thread
Олег Пронин Jul 31, 2014 05:52 pm
Reini Urban Jul 31, 2014 09:22 pm
Олег Пронин Jul 31, 2014 10:31 pm
demerphq Aug 01, 2014 10:11 am
Ricardo Signes Aug 01, 2014 11:46 am
Reini Urban Aug 04, 2014 03:57 pm
Dave Mitchell Aug 04, 2014 03:21 pm
Nicholas Clark Aug 04, 2014 03:33 pm
Олег Пронин Aug 04, 2014 03:59 pm
demerphq Aug 04, 2014 04:05 pm
Dave Mitchell Aug 05, 2014 09:40 am
Олег Пронин Aug 05, 2014 05:03 pm
demerphq Aug 05, 2014 06:00 pm
demerphq Aug 05, 2014 06:06 pm
David Golden Aug 06, 2014 05:12 pm
Олег Пронин Aug 07, 2014 04:54 pm
Олег Пронин Aug 11, 2014 01:24 pm
Tim Bunce Aug 11, 2014 04:33 pm
Dave Mitchell Aug 12, 2014 04:17 pm
Steffen Mueller Aug 12, 2014 05:23 pm
Олег Пронин Aug 13, 2014 10:59 am
Dave Mitchell Aug 14, 2014 09:25 am
Steffen Mueller Aug 04, 2014 05:01 pm
Steffen Mueller Aug 04, 2014 05:27 pm
demerphq Aug 05, 2014 08:28 pm
demerphq Aug 06, 2014 09:55 am
bulk88 Aug 06, 2014 10:20 am
demerphq Aug 06, 2014 10:25 am
bulk88 Aug 06, 2014 11:24 am
demerphq Aug 06, 2014 11:36 am
David Nicol Aug 12, 2014 04:01 pm
Messages in this thread