#20686: Refactor getattr_from_other_class() for lookup of methods in categories
-------------------------------------+-------------------------------------
       Reporter:  nthiery            |        Owner:
           Type:  enhancement        |       Status:  needs_review
       Priority:  major              |    Milestone:  sage-7.3
      Component:  categories         |   Resolution:
       Keywords:                     |    Merged in:
        Authors:  Jeroen Demeyer     |    Reviewers:
Report Upstream:  Fixed upstream,    |  Work issues:
  but not in a stable release.       |
         Branch:                     |       Commit:
  
u/jdemeyer/optimize_method_lookup_from_the_categories_for_instances_of_cython_classes|
  7140ee389ac63921f356d5abc4cdc94a6bf48580
   Dependencies:  #20825             |     Stopgaps:
-------------------------------------+-------------------------------------
Description changed by jdemeyer:

Old description:

> For parents and elements implemented in Cython, category lookup is
> emulated in `__getattr__` using `getattr_from_other_class`. In this
> ticket, this is refactored a bit:
>
> 1. We should not pick up special attributes from `type`, there is no
> point in returning the type name of the category here (similarly for
> `__dict__`, `__mro__`, ...):
> {{{
> sage: ZZ(1).__name__
> 'JoinCategory.element_class'
> sage: ZZ.__name__
> 'JoinCategory.parent_class'
> }}}
> This change causes a few failures which need to be fixed.
>
> 2. The implementation of `getattr_from_other_class` is not very robust.
> For example, static methods are not supported. We fix this by using low-
> level Python functions to get the attribute and we manually call the
> descriptor `__get__` if needed.
>
> 3. The lookup using `getattr_from_other_class` is about 9 times slower
> than a normal method lookup::
> {{{
> sage: def foo(self): return
> sage: Sets().element_class.foo = foo
> sage: def g(x):
> ....:     for i in range(1000):
> ....:          x.foo()
>
> sage: x = Semigroups().example().an_element()
> sage: y = 1
>
> sage: %timeit g(x)
> 10000 loops, best of 3: 115 µs per loop
>
> sage: %timeit g(y)
> 1000 loops, best of 3: 1.18 ms per loop
> }}}
> We improve on this by roughly a factor 2 (but even then, it's still a lot
> slower than usual method lookup).
>
> NOTE: This needs a trivial Cython patch (accepted by upstream):
> https://github.com/cython/cython/pull/522

New description:

 For parents and elements implemented in Cython, category lookup is
 emulated in `__getattr__` using `getattr_from_other_class`. In this
 ticket, this is refactored a bit:

 1. We should not pick up special attributes from `type`, there is no point
 in returning the type name of the category here (similarly for `__dict__`,
 `__mro__`, ...):
 {{{
 sage: ZZ(1).__name__
 'JoinCategory.element_class'
 sage: ZZ.__name__
 'JoinCategory.parent_class'
 }}}
 This change causes a few failures which need to be fixed.

 2. The implementation of `getattr_from_other_class` is not very robust.
 For example, static methods are not supported. We fix this by using low-
 level Python functions to get the attribute and we manually call the
 descriptor `__get__` if needed.

 3. We shouldn't do anything special with double-underscore private
 attributes: in plain Python, this is implemented by the parser and not by
 `getattr()`. So `__getattr__` would already receive the mangled private
 name.

 4. Some of the changes broke `_sage_src_lines_`, so we also change that:
 currently, `_sage_src_lines_()` is used both to get the source lines of a
 class (e.g. dynamic classes) and an instance (e.g. cached functions). We
 change this to always mean the source lines of an instance, which makes
 things clearer.

 5. The lookup using `getattr_from_other_class` is about 9 times slower
 than a normal method lookup::
 {{{
 sage: def foo(self): return
 sage: Sets().element_class.foo = foo
 sage: def g(x):
 ....:     for i in range(1000):
 ....:          x.foo()

 sage: x = Semigroups().example().an_element()
 sage: y = 1

 sage: %timeit g(x)
 10000 loops, best of 3: 115 µs per loop

 sage: %timeit g(y)
 1000 loops, best of 3: 1.18 ms per loop
 }}}
 We improve on this by roughly a factor 2 (but even then, it's still a lot
 slower than usual method lookup).

 NOTE: This needs a trivial Cython patch (accepted by upstream):
 https://github.com/cython/cython/pull/522

--

--
Ticket URL: <https://trac.sagemath.org/ticket/20686#comment:37>
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 unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sage-trac.
For more options, visit https://groups.google.com/d/optout.

Reply via email to