Function unrolling (was Re: Speed: bytecode vz C API calls)
Jacek Generowicz
jacek.generowicz at cern.ch
Tue Dec 9 17:32:00 EST 2003
aahz at pythoncraft.com (Aahz) writes:
> In article <tyfd6ayylhy.fsf at pcepsft001.cern.ch>,
> Jacek Generowicz <jacek.generowicz at cern.ch> wrote:
> >aahz at pythoncraft.com (Aahz) writes:
> >>
> >> I think you're going to need to write a solution that doesn't call
> >> functions (function unrolling, so to speak). Instead of returning a
> >> memoizer function, just use a dict of dicts.
^^^^^^^^
(Maybe this is where we are misunderstanding eachother. On first
reading, I thought that "memoizer" was just a typo. I don't return a
memoizer function, I return a memoized function.)
> >Sounds interesting. But how do I get the dictionary to use the
> >function which it is memoizing, to calculate values which haven't been
> >cached yet?
>
> Same way you decide which function to call currently.
The way I decide which function to call currently is to type its name
directly into the source at the call site. Deciding which function to
call is not the problem: calling any function at all, once I've
reduced myself to having _just_ a dictionary, is the problem.
Maybe I didn't make the use of "memoize" clear enough.
You use it thus:
def foo():
...
foo = memoize(foo)
And from now on foo is a lot faster than it was before[*].
> Essentially, you do something like this:
>
> def foo():
> pass
>
> def bar():
> pass
>
> lookup = {
> 'foo': {'func': foo, 'cache': {}},
> 'bar': {'func': bar, 'cache': {}},
> }
>
> for operation,args in WorkQueue:
> curr = lookup[operation]
> try:
> return curr['cache'][args]
> except KeyError:
> return curr['cache'].setdefault(args, curr['func'](*args))
Nononononooooo ! :-)
In any given loop, I know _which_ function I want to call, so the
dictionary of dictionaries is pointless. Let's recap the original
memoizer:
def memoize(callable):
cache = {}
def proxy(*args):
try: return cache[args]
except KeyError: return cache.setdefault(args, callable(*args))
return proxy
I thought that you were suggesting to replace "proxy" with just the
dictionary "cache", in order to save the function call overhead, as
proxy is (almost) just a wrapper around the dictionary lookup. That's
great, but what do I do in the (very rare) cases where I'm presented
with an argument/key I haven't seen yet? Somehow, I need to recognize
that the key is not present, call the original function, and add the
key-value pair to the cache. I can't see how to do that without
re-introducing the function call. I could to it by inlining the try
block, but then I wouldn't be able to use the memoized function inside
map ... and turning a map into a for-loop would lose me more than I'd
gained.
[*] With a whole bunch of exceptions which are beside the point here.
More information about the Python-list
mailing list