#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                  |     Merged in:              
   Dependencies:                                  |      Stopgaps:              
--------------------------------------------------+-------------------------

Comment (by SimonKing):

 We suggested the following approaches towards getting fast metaclasses.

  * You suggested: "Let a metaclass be a Python class that gets fast
 methods from a Cython class"
  * I suggested: "Implement the metaclass directly in Cython" (you showed
 how it works)

 Here, I study the question whether we should expect a significant speed
 difference in the two approaches.

 Let the following be put in a pyx file that we attach to a Sage session:
 {{{
 cdef class T1:
     def __call__(self, x):
         return x

 cdef class T2:
     def __hash__(self):
         return 123

 cdef class T3:
     def __add__(self, x):
         return self

 cdef class T4:
     def test(self, x):
         return x
 }}}

 Hence, we have Cython classes, the first three of them providing typical
 "magical" methods, the fourth a custom method. In the Sage session, we
 derive a Python class from the four classes, and, to make it more
 "realistic", we give a second base.
 {{{
 sage: C1 = type("C1",(T1,Parent),{})
 sage: C2 = type("C2",(T2,Parent),{})
 sage: C3 = type("C3",(T3,Parent),{})
 sage: C4 = type("C4",(T4,Parent),{})
 }}}

 C1,...,C4 correspond to metaclasses implemented as you suggest, T1,...,T4
 as I suggest.

 It could be that in future we want to have meta-metaclasses, which would
 allow to mix the metaclasses. Hence, I am also considering new classes
 derived from C1,...,C4 resp. from T1,...,T4:
 {{{
 sage: Cdirect = type("Cdirect",(T1,T2,T3,T4,Parent),{})
 sage: Cindirect = type("Cindirect",(C1,C2,C3,C4),{})
 }}}
 Let us create instances of each class:
 {{{
 sage: c1 = C1()
 sage: c2 = C2()
 sage: c3 = C3()
 sage: c4 = C4()
 sage: t1 = T1()
 sage: t2 = T2()
 sage: t3 = T3()
 sage: t4 = T4()
 sage: ci = Cindirect()
 sage: cd = Cdirect()
 }}}

 Now for the timings. First, the call method:
 {{{
 sage: timeit("a = c1(3)", number=300000)
 300000 loops, best of 3: 280 ns per loop
 sage: timeit("a = t1(3)", number=300000)
 300000 loops, best of 3: 277 ns per loop
 sage: timeit("a = ci(3)", number=300000)
 300000 loops, best of 3: 279 ns per loop
 sage: timeit("a = cd(3)", number=300000)
 300000 loops, best of 3: 278 ns per loop
 }}}

 Next, the hash:
 {{{
 sage: timeit("a = hash(c2)", number=300000)
 300000 loops, best of 3: 87.6 ns per loop
 sage: timeit("a = hash(t2)", number=300000)
 300000 loops, best of 3: 85.9 ns per loop
 sage: timeit("a = hash(ci)", number=300000)
 300000 loops, best of 3: 83.6 ns per loop
 sage: timeit("a = hash(cd)", number=300000)
 300000 loops, best of 3: 99.7 ns per loop
 }}}

 Arithmetics:
 {{{
 sage: timeit("a = c3+c3", number=300000)
 300000 loops, best of 3: 149 ns per loop
 sage: timeit("a = t3+t3", number=300000)
 300000 loops, best of 3: 83 ns per loop
 sage: timeit("a = ci+ci", number=300000)
 300000 loops, best of 3: 77.8 ns per loop
 sage: timeit("a = cd+cd", number=300000)
 300000 loops, best of 3: 77.4 ns per loop
 }}}

 And a non-magical method:
 {{{
 sage: timeit("a = c4.test(4)", number=300000)
 300000 loops, best of 3: 327 ns per loop
 sage: timeit("a = t4.test(4)", number=300000)
 300000 loops, best of 3: 285 ns per loop
 sage: timeit("a = ci.test(4)", number=300000)
 300000 loops, best of 3: 444 ns per loop
 sage: timeit("a = cd.test(4)", number=300000)
 300000 loops, best of 3: 364 ns per loop
 }}}

 Summary:

 For the magical methods, there is no significant speed difference between
 our suggestions (with exception of addition, but that result looks
 strange, and I don't really believe it). This would even hold if (in
 future) we would consider to create metaclasses "dynamically" (i.e., in
 the same way as I have created `Cindirect` and `Cdirect` above).

 It only pays off to ''directly'' implement stuff in Cython, if non-magical
 methods are concerned. But I doubt that we want non-magical methods for
 metaclasses.

 So, the decision between our suggestions must rely on different reasons:
 Which one is simpler and easier to maintain? The plus of my suggestion is
 that it is direct, i.e., there are less layers of indirection. The plus of
 your suggestion is that it works without creating an extension type for
 builtin types.

 After a break, I will attach my two patches, and then the discussion can
 start...

-- 
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/12808#comment:40>
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