1

Problem

Let's say I have some programming language with only referentially transparent functions. It's well-known that any of these functions can then be memoized. However, it's not always worth it in terms of time or space to memoize functions. What is an algorithm for deciding whether or not it will save space or time to automatically memoize a function?

Example

# likely to benefit from memoization
def fact(n):
    return 1 if n == 0 else n * fact(n - 1)

# unlikely to benefit from memoization
def inc(x):
    return x + 1

My use case

I have a Lisp interpreter as a side-project, which restricts itself to a purely functional subset of Lisp. I want to memoize functions, but I don't want to blindly decide what should and should not be memoized.

Additional info

The solution could either operate using static code analysis, or it could operate during the lifetime of the program, or it could operate over several runs of a program.

The solution doesn't have to be perfect; it could just be a heuristic which is right a good amount of the time.

michaelsnowden
  • 6,031
  • 2
  • 38
  • 83
  • Hi Michael. I don't think this question is really answerable in the stack-overflow format -- there's too many trade-offs, heuristics, and dependencies on whole-program concerns (for example if you only call `fact` once, memoizing it is not cost-effective). Questions on s-o should have clear right/wrong answers. – Paul Hankin Jun 22 '16 at 05:41
  • @PaulHankin I'm afraid you're right. Maybe better for cs.stackexchange? – michaelsnowden Jun 22 '16 at 05:42
  • 1
    I'm not a regular user of cs.s-e, but I suspect you'll have the same problem there. Probably most helpful to you would be to find all related CS papers/work that discuss this problem (which I guess is a research area), but I'm not sure there's any online resource that makes this easy. Sorry! – Paul Hankin Jun 22 '16 at 05:46
  • Interesting question! One easy necessary-but-not-sufficient rule: The function must call itself recursively (possibly via a chain of other functions) to benefit from memoisation. You'll also need strong static analysis to determine the set of parameter combinations it will be called with (or, more likely, a sufficiently small superset thereof). One thing that isn't a concern is termination: Although a "function" in a referentially transparent language may fail to be a total mathematical function due to nontermination on some inputs, memo(f) will terminate on all inputs for which f terminates. – j_random_hacker Jun 22 '16 at 13:00
  • This could actually be something where profile-guided optimisation can be usefully applied: instrument the function to record what sets of parameters it gets called with, run the program a few times, examine the stats and heuristically pick a set of parameter combinations to memoise in the next compilation/interpretation run. – j_random_hacker Jun 22 '16 at 13:06
  • For simplicity, I'd consider only functions whose parameters are tuples of integers, and limit memoisation to a single hypercube (product of intervals) in this parameter space... Memory used for the memo table is proportional to the hypercube volume, and there's probably a reasonably efficient way to find the "best hypercube" for a fixed volume -- that is, the hypercube of volume at most k bytes that contains the largest number of points, where each point is a parameter tuple that the function was called with. – j_random_hacker Jun 22 '16 at 13:09
  • @j_random_hacker Yeah I think profile-guided optimization has gotta be the way to go because it's kind of impossible to tell with static code analysis how often a function will be called (halting problem and all that). – michaelsnowden Jun 23 '16 at 04:10

0 Answers0