#12808: Optimize ClassCallMetaClass using Cython
--------------------------------------------------+-------------------------
       Reporter:  hivert                          |         Owner:  jason       
           Type:  enhancement                     |        Status:  needs_review
       Priority:  major                           |     Milestone:  sage-5.0    
      Component:  misc                            |    Resolution:              
       Keywords:  classcall UniqueRepresentation  |   Work issues:              
Report Upstream:  N/A                             |     Reviewers:              
        Authors:  Florent Hivert, Simon King      |     Merged in:              
   Dependencies:                                  |      Stopgaps:              
--------------------------------------------------+-------------------------
Changes (by {'newvalue': u'Florent Hivert, Simon King', 'oldvalue': u'Florent 
Hivert'}):

  * author:  Florent Hivert => Florent Hivert, Simon King


Old description:

> When a class {{{C}}} is an instance of {{{ClasscallMetaclass}}}, then for
> any constructor call, the system currently looks for {{{__classcall__}}}
> and {{{__classcall_private__}}} in {{{C}}}. This adds quite an overhead
> and this could be cached, assuming no one modifies {{{C}}} (which seems
> reasonable). Improving this speeds up, in particular, all call to the
> constructor of a subclass of {{{UniqueRepresentation}}}, eg. many
> parents, all categories...
>
> __Apply__
>
> [attachment:trac_12808-classcall_speedup-fh.patch]

New description:

 When a class {{{C}}} is an instance of {{{ClasscallMetaclass}}}, then for
 any constructor call, the system currently looks for {{{__classcall__}}}
 and {{{__classcall_private__}}} in {{{C}}}. This adds quite an overhead
 and this could be cached, assuming no one modifies {{{C}}} (which seems
 reasonable). Improving this speeds up, in particular, all call to the
 constructor of a subclass of {{{UniqueRepresentation}}}, eg. many parents,
 all categories...

 __Apply__

  * [attachment:trac_12808-classcall_speedup-fh.patch]
  * [attachment:trac_12808_nested_class_cython.patch]

 and we have to decide whether the third patch helps to make the code
 clearer:

  * [attachment:trac_12808-classcall_cdef.patch]

--

Comment:

 I have attached my two patches. Your patch and my patch actually turn out
 to be independent.

 Purpose of my first patch: Make `NestedClassMetaclass` an extension type
 of type, and avoid some calling overhead during its creation.

 Purpose of my second patch: Make `ClasscallMetclass` an extension type as
 well, directly derived from `NestedClassMetaclass`.

 '''__Timings__'''

 With '''sage-5.1.notebook unpatched''', I get for Florent's examples
 {{{
 sage: class Rien(object):
 ....:     pass
 ....:
 sage: from sage.misc.classcall_metaclass import ClasscallMetaclass
 sage: class NOCALL(object):
 ....:     __metaclass__ = ClasscallMetaclass
 ....:     pass
 ....:
 sage: %timeit [Rien() for i in range(10000)]
 125 loops, best of 3: 1.74 ms per loop
 sage: %timeit [NOCALL() for i in range(10000)]
 25 loops, best of 3: 16.7 ms per loop
 sage: class CALL(object):
 ....:     __metaclass__ = ClasscallMetaclass
 ....:     @staticmethod
 ....:     def __classcall_private__(cls, arg):
 ....:         arg = arg + arg
 ....:         return arg
 ....:
 sage: %timeit [CALL(i) for i in range(10000)]
 25 loops, best of 3: 9.05 ms per loop
 }}}
 Here is something with `__classcall__` instead of `__classcall_private__`,
 and in a way that has less overhead than `arg+arg`:
 {{{
 sage: class NewCall(object):
 ....:     __metaclass__ = ClasscallMetaclass
 ....:     @staticmethod
 ....:     def __classcall__(cls, C):
 ....:         return C
 ....:
 sage: C = ZZ.__class__
 sage: timeit("a = NewCall(C)", number=10000)
 10000 loops, best of 3: 878 ns per loop
 }}}
 And finally a "nested class" example:
 {{{
 sage: from sage.misc.nested_class import NestedClassMetaclass
 sage: def test_nest():
 ....:     class A:
 ....:         __metaclass__ = NestedClassMetaclass
 ....:         class B:
 ....:             pass
 ....:
 sage: %timeit test_nest()
 625 loops, best of 3: 33.1 µs per loop
 }}}

 Now, the same examples with my first patch:
 {{{
 sage: %timeit [Rien() for i in range(10000)]
 125 loops, best of 3: 1.76 ms per loop
 sage: %timeit [NOCALL() for i in range(10000)]
 25 loops, best of 3: 18.3 ms per loop
 sage: %timeit [CALL(i) for i in range(10000)]
 25 loops, best of 3: 10.7 ms per loop
 sage: %timeit test_nest()
 625 loops, best of 3: 23.6 µs per loop
 }}}

 Now, with your patch only:
 {{{
 sage: %timeit [Rien() for i in range(10000)]
 125 loops, best of 3: 1.77 ms per loop
 sage: %timeit [NOCALL() for i in range(10000)]
 125 loops, best of 3: 2.05 ms per loop
 sage: %timeit [CALL(i) for i in range(10000)]
 125 loops, best of 3: 4.34 ms per loop
 sage: timeit("a = NewCall(C)", number=10000)
 10000 loops, best of 3: 889 ns per loop
 sage: %timeit test_nest()
 625 loops, best of 3: 32.8 µs per loop
 }}}

 Thus, our patches treat orthogonal aspects. Now, the first two patches
 together:
 {{{
 sage: %timeit [Rien() for i in range(10000)]
 125 loops, best of 3: 1.78 ms per loop
 sage: %timeit [NOCALL() for i in range(10000)]
 125 loops, best of 3: 2 ms per loop
 sage: %timeit [CALL(i) for i in range(10000)]
 125 loops, best of 3: 4.34 ms per loop
 sage: timeit("a = NewCall(C)", number=10000)
 10000 loops, best of 3: 895 ns per loop
 sage: %timeit test_nest()
 625 loops, best of 3: 23.6 µs per loop
 }}}

 And with all three patches:
 {{{
 sage: %timeit [Rien() for i in range(10000)]
 125 loops, best of 3: 1.75 ms per loop
 sage: %timeit [NOCALL() for i in range(10000)]
 125 loops, best of 3: 2.08 ms per loop
 sage: %timeit [CALL(i) for i in range(10000)]
 125 loops, best of 3: 4.44 ms per loop
 sage: timeit("a = NewCall(C)", number=10000)
 10000 loops, best of 3: 897 ns per loop
 sage: %timeit test_nest()
 625 loops, best of 3: 23.6 µs per loop
 }}}

 __CONCLUSION__

 My first patch does improve the time spent for the creation of a nested
 class. Your patch improves a lot of things for classcall metaclass. My
 second patch makes (I think) the inheritance a bit clearer, but it does
 not provide a speed-up. The reason is explained in my previous post.

 Apply trac_12808-classcall_speedup-fh.patch
 trac_12808_nested_class_cython.patch trac_12808-classcall_cdef.patch

-- 
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/12808#comment:41>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, 
and MATLAB

-- 
You received this message because you are subscribed to the Google Groups 
"sage-trac" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sage-trac?hl=en.

Reply via email to