Hi Robert,

On 28 Jan., 11:14, Robert Bradshaw <[email protected]>
wrote:
> Yep. It's sad when sometimes it's quicker to not cache non-trivial
> computations.

Indeed it was. Here is an example from the ticket:

Setting:

sage: class A:
....:     @cached_method
....:     def bar(self,x):
....:         return x
....:     @cached_in_parent_method
....:     def barP(self,x):
....:         return x
....:     def parent(self):
....:         return self
....:
sage: a = A()


WITHOUT #8611, we get:

# The non-critical path:
sage: time for x in range(10^6): b=a.bar(x)
CPU times: user 24.97 s, sys: 0.16 s, total: 25.13 s
Wall time: 25.13 s
sage: time for x in range(10^6): b=a.barP(x)
CPU times: user 41.03 s, sys: 0.13 s, total: 41.16 s
Wall time: 41.17 s

# The critical path (now the values are cached):
sage: time for x in range(10^6): b=a.bar(x)
CPU times: user 16.45 s, sys: 0.02 s, total: 16.47 s
Wall time: 16.47 s
sage: time for x in range(10^6): b=a.barP(x)
CPU times: user 20.47 s, sys: 0.02 s, total: 20.50 s
Wall time: 20.50 s

#########################

WITH #8611, we get:

# The non-critical path:
sage: time for x in range(10^6): b=a.bar(x)
CPU times: user 11.40 s, sys: 0.08 s, total: 11.48 s
Wall time: 11.48 s
sage: time for x in range(10^6): b=a.barP(x)
CPU times: user 27.89 s, sys: 0.12 s, total: 28.01 s
Wall time: 28.02 s

# The critical path (now the values are cached):
sage: time for x in range(10^6): b=a.bar(x)
CPU times: user 3.09 s, sys: 0.01 s, total: 3.10 s
Wall time: 3.10 s
sage: time for x in range(10^6): b=a.barP(x)
CPU times: user 5.25 s, sys: 0.02 s, total: 5.26 s
Wall time: 5.26 s

Hence, due to the optimisation of accessing the cache, it is now
quicker to compute the values *and* store them in the cache (the non-
critical path) than it used to be to only get the cached values that
have been computed before (the critical path).

> +1 to all of the above, I think a cached result should be put on the
> object itself itself.

It still is. In addition, a cached method now *also* belongs to the
object itself.

> >  - objects that need fast caching could have x._cache (or maybe even
> >   x._cache.foo) as a Cython attribute
>
> That's a really nice idea.

... if the other problems with cached_method+Cython can be solved.

> I think caches are, by nature, potentially ephemeral, so it's OK to
> get back fully-intact objects without their caches. Of coures, someone
> may be counting on the fact that they've computed this before
> pickling--if so they should speak up now.

There is something called sage.misc.cachefunc.ClearCacheOnPickle. It
is not documented, and I wasn't able to make it work in an example.
But I think the idea is nice: You have one decorator for methods whose
cache should be pickled, and another one where this should not be the
case.

But now that I think about it: It should be easy on top of #8611.

Namely, I.groebner_basis.cache is pickled because I.__dict__ is
pickled and contains I._cache__groebner_basis. Hence, if one modifies
the decorator a little, it should be possible to have the cache in
x.bar.cache, but NOT in x.__dict__['_cache__bar']. In that way, the
cache would not be pickled.

Syntax suggestion:

class FOO:
    @cached_method
    def bar(self,...):
        ...
    @cached_method(clear=True)
    def foo_bar(self,...):
        ...

Then, when pickling an instance X of FOO, the cache of X.bar would be
pickled, but not the cache of X.foo_bar.

Best regards,
Simon

-- 
To post to this group, send an email to [email protected]
To unsubscribe from this group, send an email to 
[email protected]
For more options, visit this group at http://groups.google.com/group/sage-devel
URL: http://www.sagemath.org

Reply via email to