#14214: Cythoned homsets
----------------------------------------------+-----------------------------
Reporter: SimonKing | Owner: tbd
Type: enhancement | Status: needs_work
Priority: major | Milestone: sage-5.10
Component: performance | Resolution:
Keywords: Hom, cython, cached method | Work issues: How to store
domain and codomain?
Report Upstream: N/A | Reviewers:
Authors: Simon King | Merged in:
Dependencies: #14159, #12951, #13184 | Stopgaps:
----------------------------------------------+-----------------------------
Comment (by nbruin):
I guess our discussion got side-tracked a little bit:
- For `_domain` and `_codomain` there is no reason to use lazy_attribute.
In absence of a dict (where our code doesn't matter) we need a descriptor
that looks up the value elsewhere.
- what we should really have a `cdef public` because then one and the
same location is fast from C ''and'' python writes the python access code
for us. But for that we have inheritance problems at the moment.
Then there's the second bit, cythonization of lazy_attribute:
- a proper cythonized version of lazy_attribute is faster in all cases,
mostly by a factor 2 as far as I could see. Even with required
`__cached_methods` storage, I get attribute retrieval (once cached) of
`84.6 ns` rather than `547 ns`
That actually means you COULD put a cythonized lazy_attribute on `_domain`
and `_codomain`. The overhead compared to a custom nondatadescriptor is
minimal, and within a factor 2 of a `dict` lookup. Here's the code I mean:
{{{
cdef class LazyAttribute:
cdef public f
cdef public str __name__
def __init__(self,f):
self.f=f
self.__name__=f.__name__
def __get__(self,a,cls):
cdef CM
cdef result
if a is None:
return lazy_attribute(self.f)
try:
# __cached_methods is supposed to be a public Cython
attribute.
# Apparently, these are *not* subject to name mangling.
CM = getattr(a, '__cached_methods')
if CM is None:
CM = {}
setattr(a, '__cached_methods', CM)
except AttributeError,msg:
CM = None
if CM is not None:
try:
return CM[self.__name__]
except KeyError:
pass
result = self.f(a)
if result is NotImplemented:
# Workaround: we make sure that cls is the class
# where the lazy attribute self is actually defined.
# This avoids running into an infinite loop
# See About descriptor specifications
for supercls in cls.__mro__:
if self.f.__name__ in supercls.__dict__ and self is
supercls.__dict__[self.__name__]:
cls = supercls
return getattr(super(cls, a),self.__name__)
try:
setattr(a, self.__name__, result)
except AttributeError:
if CM is not None:
CM[self.f.__name__] = result
return result
raise
return result
}}}
There's a bit of clean-up to do on this concerning returning a documented
object when requested on the bare class. It works now, but only by reusing
the old implementation. Is returning the descriptor object itself on the
bare class customary/desirable behaviour?
--
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/14214#comment:52>
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 http://groups.google.com/group/sage-trac?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.