-
-
Notifications
You must be signed in to change notification settings - Fork 559
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite cached_method in Cython #11115
Comments
comment:1
The reason is as follows (compare #8611): The cached method f has a getter method, that returns a Moreover, the cached method would try to cache its return values in a dictionary assigned to O - again, it fails. So, caching can not work. My suggestion is as follows. We want, in particular, that caching works for the base classes Element and Parent. I propose to provide them with a public cdef method Concerning the cache for storing the return values of f: It can not be stored in a dict as an attribute of O. But by #8611, there is a dict O.f.cache that is used for storing anyway. So, in a nutshell, it simply works (and I already have a patch that only remains to be documented). I tried to extend that approach to |
comment:2
Replying to @simon-king-jena:
... and documentation is hard, in particular for building the reference manual. See #9976. The idea is:
I guess I will be able to submit patches to both tickets tomorrow. |
Author: Simon King |
This comment has been minimized.
This comment has been minimized.
comment:3
It took a bit longer than I originally expected, but I think it was worth it. New Features
Examples Python The following code defines a category, and a Parent class that has a method with a hand-written cache and a corresponding cached method inherited from the category:
We do some sanity tests, and then show that the cached method is faster than the hand-written cache, unless we provide arguments by name:
Here are timings for the hand-knitted cache:
and here are the corresponding timings for the cached method inherited from the category:
Cython You can not use arbitrary decorators in Cython. But it is now possible to wrap a Cython function by the cached_method and assign it as a method - one needs to explicitly provide its name, though. In addition, we provide a hand-written cache programmed in Cython.
It is a Parent class, and thus it can inherit parent methods from a category. Without the patch, the cache of an inherited cached_method would break, but now it is fine:
The trick is that I introduced an attribute While it is nice that cached_method works at all in Cython, the performance is not as good as I wish. Here are the times for the hand-knitted cache written in Cython:
Here for the cached_method inherited from the category:
And here using the decorator in Cython code:
Conclusion The patch provides a considerable speed-up in a case where cached_method used before, so that cached_method is not only convenient but efficient. Also, it enables to use cached_method in a broader context; here, it is not particularly efficient, but it is convenient. We want that decorated (in particular, cached) methods appear nicely in introspection and in the reference manual. Therefore, I like to have: Depends on #9976 |
This comment has been minimized.
This comment has been minimized.
comment:5
Replying to @simon-king-jena:
What exactly is the problem with double inheritance? It would be nice if all |
comment:6
Replying to @novoselt:
The problem is that you can not have double inheritance from extension classes if Python does not know whether the underlying data structures are compatible. So, if you have
(which is currently the case) then it is fine, such as here:
But you get an error if you want to put more structure in it
|
comment:7
Here is another data point:
There are methods that are frequently called and just return a double-underscore attribute. It would be faster to achieve the same with a cached method! I guess this is because of the name mangling that takes place for double-underscore attributes. However, that only holds when calling a method. Inside of a method, the double-underscore seems to be quicker:
I think it would be worth trying to use this trick: Little methods returning a double-underscore attribute appear all over the place. See #9138 for one example. |
comment:8
There is one problem: The startup time increases. I suspect this is because I extended the |
comment:9
I did some tests. I reverted the That is strange. Is it the case that loading a Cython class takes longer than loading a Python class. |
comment:10
One more idea: Currently, a cached method will modify its own Certainly it does not happen very frequently that the documentation of a method (cached or not) is requested: It is when the documentation is built, and it is when the user wants to learn something. So, computing and storing the documentation during initialisation of a cached method may be a waste of time and also a waste of memory. Instead, I suggest that the documentation is only computed in the method |
comment:11
I think the problem with the startup time is solved. Indeed, it turned out that the time was wasted by creating doc strings for cached methods. Next, I'll do some more tests. If it can be confirmed that a Python method returning a double-underscore attribute is slower than a cached_method that does the same, then I will try to speed Sage up in general, by using cached methods extensively. I wonder what will come out... Still: Depends on #9976 |
comment:12
In order to be on the safe side, I made some timings for the patches from #9138 (on top of #9944), and I'd like to repeat these timings here, with this patch applied additionally.
So, there is no slow-down, compared with the timings from an unpatched Sage on the same machine, as reported on #9138. Here is an arithmetical example:
That is about the same as with the patches from #9944 and #9138, but slower than with an unpatched Sage. I found that one can use the improved cached_methods to considerably speed up the arithmetics. Namely, in the multiplication, the method
That is much faster than without patches, which is 501 µs!!. Currently, I try to find some places where using a cached_method might be beneficial. As I have demonstrated in previous posts, there are chances to speed methods up that do nothing more but " But independent of that possible second patch, I think the first patch can be reviewed. |
comment:13
I improved the performance of accessing the cache even further. It is quite common to have methods that do not take arguments but whose return value should be cached. Typical example is the set of generators of a multivariate polynomial ring. Since sage-4.7.alpha5, it is a cached method. Obviously, if a method takes no arguments, then caching the single value is much easier than storing the results in a dictionary for different arguments. I implemented this special case in a class Here is the performance. Bot
That is much faster than with an unpatched sage-4.7.alpha5:
To give you an idea of how fast 170 ns or 250 ns are:
Note that the cache is pickled -- see examples in the doc strings. There is one further extension. There is a class That is to say: Let X be an instance of ClearCacheOnPickle and assume that its pickling is implemented using the The idea existed, but it did not work for me. So, I re-implemented it essentially from scratch, using the original ideas. Also, I provided doc tests for On top of sage-4.7.alpha5, I have the patches from #10296, #9944, #9138 and #9976, and then all doc tests pass with the patch from here. But there should only be one dependency, namely of #9976, which provides introspection to interactive Cython code. Depends on #9976 |
comment:14
I just updated the patch, because I noticed the following: If you have an old pickle of an object with a cached method then of course the pickle will contain the cached value. If you unpickle then it may be that an old It would thus be nice if What I did to test it:
So, the cache is already filled after unpickling. |
Work Issues: Wait for #9138 |
comment:179
Could you please explain this line in
It seems that the implementation assumes that |
comment:180
Replying to @jdemeyer:
That's why:
We use weak value dictionaries for weak_cached_function.
But certainly not in weak_cached_function. |
comment:181
Replying to @simon-king-jena:
I think that's a bad design with all the
This is like the classes for dense/sparse vectors. |
comment:182
As it seems, there will be a new ticket on cached methods soon, because of the sad fact that removing a cython source file from the Sage library does not only result in problems with introspection of cached methods, but the cached methods can't even be called. So, I suppose the design flaw can be addressed there, too. |
comment:183
Replying to @simon-king-jena:
Unless #17814 really requires major refactoring anyway, I wouldn't do it on the same ticket. Also, I certainly don't want to force you to "fix" this. It works as it is. |
comment:185
Hm. Fixing sageinspect won't be easy. I currently have absolutely no clue how to read off the number of arguments (or even the names of the arguments) of a function that is defined in a Cython file. Those functions don't seem to have any attributes holding useful information. Has someone else an idea? If not, then I don't see what we can do. We do want a special cached method implementation for methods without arguments, and thus we need to determine the number of arguments of the to-be-wrapped method. If that doesn't work without reading the sources, what could we possibly do? |
comment:186
What's the purpose of this opaque block of code?
|
Broadening the original description of the ticket:
The aim is to provide a Cython version of the cached_method decorator. There are three benefits:
Speed (see timings in the comments)
Using cached methods in Cython code.
Let me elaborate on the last point:
In order to make full use of the parent and element methods of a category, it should be possible to define a cached method in the category, so that any object of that category inherits it with caching.
Currently, it fails as follows:
So, the method is inherited, but not cached.
Apply:
Note that, if you want to remove the patches after testing them, you need to do
Otherwise,
sage -br
would not work.Depends on #9138
Depends on #11900
CC: @nthiery
Component: misc
Keywords: category cython cache
Author: Simon King
Reviewer: Nicolas M. Thiéry, Andrey Novoseltsev, Volker Braun
Merged: sage-5.0.beta0
Issue created by migration from https://trac.sagemath.org/ticket/11115
The text was updated successfully, but these errors were encountered: