home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / comp / lang / lisp / 2924 < prev    next >
Encoding:
Text File  |  1992-11-20  |  11.9 KB  |  233 lines

  1. Xref: sparky comp.lang.lisp:2924 comp.ai:4354
  2. Newsgroups: comp.lang.lisp,comp.ai
  3. Path: sparky!uunet!cs.utexas.edu!zaphod.mps.ohio-state.edu!saimiri.primate.wisc.edu!aplcen.apl.jhu.edu!aplcenmp!hall
  4. From: hall@aplcenmp.apl.jhu.edu (Marty Hall)
  5. Subject: Automatic Memoization Facility Available
  6. Message-ID: <By0tIx.Dst@aplcenmp.apl.jhu.edu>
  7. Organization: AAI Corp AI Lab, JHU P/T CS Faculty
  8. Date: Fri, 20 Nov 1992 15:25:44 GMT
  9. Lines: 222
  10.  
  11.  
  12. This message is to announce the availability of a facility for
  13. automatic memoization in Common LISP. Automatic memoization is a
  14. technique by which an existing function can be transformed into one
  15. that "remembers" previous arguments and their associated results,
  16. yielding large performance gains for certain types of applications.
  17. This code, in beta version, was inspired by the section on automatic
  18. memoization in Peter Norvig's _Paradigms of AI Programming_ (which in
  19. turn was inspired by Abelson and Sussman's _Structure and
  20. Interpretation of Computer Programs_). However, the idea of this
  21. facility is to extend those ideas into what is needed for a practical
  22. facility for use in a large program. As such, it adds many facilities
  23. for bookkeeping, timing, evaluating the timing advantages memoization
  24. provides, saving hash tables to disk for automatic reuse in a later
  25. session, etc. This utility has been used over the last year on the
  26. DARPA Signature Management System, resulting in performance
  27. improvements of 10-20 fold in several key modules.
  28.  
  29. These utilities, along with an overview of memoization and its
  30. applications, are described below. The code itself is available via
  31. anonymous FTP from archive.cs.umbc.edu, in /pub/Memoization. If you
  32. are interested but do not have FTP access, I can send the code via email.
  33.  
  34. I am quite interested in suggestions, comments, corrections, and
  35. experiences of anyone who tries out the package.
  36.  
  37.                     Marty Hall
  38.                     Artificial Intelligence Lab
  39.                     AAI Corporation
  40.                     PO Box 126
  41.                     Hunt Valley, MD 21030
  42.                     hall@aplcen.apl.jhu.edu
  43.                     (410) 683-6455
  44.  
  45. =================== Lengthy Description Follows =============================
  46.  
  47. WHAT IS AUTOMATIC MEMOIZATION?
  48. ==============================
  49.  
  50. The idea of memoization is that it allows a function to "remember"
  51. previous invocations, returning the previously calculated values
  52. (rather than recalculating) if it is called with exactly the same
  53. arguments as in a previous execution. *Automatic* memoization means
  54. that an existing function can be transformed into a memoized one
  55. without requiring any changes in the code for the function itself.
  56. This can result in tremendous speedups if calculations are repeated at
  57. various points in a program's execution, yet while remaining somewhat
  58. transparent to the users of the code.
  59.  
  60. APPLICATIONS
  61. ============
  62.  
  63. (A) There are four basic applications of memoization. The first, which is
  64. illustrated below, is when a single routine calls some subroutine (or
  65. itself recursively) more than is needed, resulting in extra calculations.
  66. By memoizing, these results are returned immediately for subsequent calls,
  67. with the effect of dynamic programming. In fact, this first case can be
  68. thought of as a tool for automatic dynamic programming, but without the
  69. need to build the subpieces in the correct order. This can frequently
  70. reduce the time of exponential algorithms to polynomial or even linear
  71. time. Given enough thought, this can be solved without an automatic
  72. memoization facility by either building up the subpieces in the proper
  73. order or maintaining a special purpose local data structure to retain the
  74. results (ie "manual" memoization). The advantage of doing it automatically
  75. is that less debugging and testing is required if the simple algorithm has
  76. been already tested, the versions can be changed back and forth
  77. interactively at run time, it is more transparent, and most importantly it
  78. is simple and easy to use.
  79.  
  80. To illustrate this first case, consider a naive implementation of a
  81. function to calculate the Nth Fibonacci number:
  82.  
  83. (defun Fib (N)
  84.   (if (<= N 1)
  85.       1
  86.       (+ (Fib (- N 1)) (Fib (- N 2)))) )
  87.  
  88. Once (Fib (- N 1)) is calculated, there should be no need to repeat the
  89. calculation of (Fib (- N 2)), since it has already been performed as part of
  90. the calculation of (Fib (- N 1)). These wasted calculations result in 
  91. exponential time to calculate (Fib N), growing as the golden ratio to
  92. the Nth power. On almost any machine, N of 40 or 50 is the maximum tractable
  93. calculation. Calling (Memoize 'Fib) then calling Fib results in linear time,
  94. so that N in the hundreds still only requires fractions of a second on most
  95. machines. Later calculations require only constant time if they calculate
  96. (Fib M) for an M less than or equal to a previously calculated value. 
  97.  
  98. Of course, one doesn't need memoization to get an efficient calculation of
  99. Fibonacci numbers. Simple tail-recursive or iterative implementations will
  100. give the same performance, and there is even a closed form. But there are
  101. many real-life problems where the more efficient algorithm is not so obvious,
  102. and it is far simpler to memoize the obvious algorithm than to determine
  103. a better algorithm. For instance, in the Memoization-Examples file included
  104. in the distribution, a slightly less obvious illustration is given of an
  105. algorithm to calculated divided differences. As further illustrations, Peter
  106. Norvig showed that a memoized version of a simple recursive descent parser
  107. yields the same performance as chart parsing or Earley's algorithm for parsing
  108. context free languages. Paul McNamee has shown that memoizing the simple
  109. brute-force approaches to the 0/1 knapsack problem or the matrix chain
  110. problem gives the same performance as the complex dynamic programming
  111. implementations. Other similar examples abound.
  112.  
  113. (B) The second case is for invocations of a function that are repeated over
  114. time, but from scattered places in the program, or even when revoked
  115. repeatedly by a user in an interactive program. This generally yields a
  116. speedup by a constant factor, but that factor may be large. Without an
  117. automatic memoization facility, the only alternative is maintaining a
  118. special purpose global data structure, requiring testing and debugging,
  119. and much extra effort for something that at best is equally efficient as
  120. memoization.
  121.  
  122. (C) The third case is when a function is so expensive that you want to
  123. perform the calculations off-line and save the results for a later
  124. session. The automatic memoization facility provides a simple and
  125. transparent method to save the results and have them associated with the
  126. function automatically in a later session. See Save-Memo-Table in the
  127. following section for an explanation of how to do that.
  128.  
  129. (D) The final case is when using memoization as a tool in conventional
  130. performance profiling and optimization. Many implementations provide
  131. some sort of a metering system, and this should be used for major test
  132. cases.  However, there is often tremendous overhead involved, with
  133. 20-30x slower performance when a routine is fully metered. For quick
  134. test cases, it is often useful to know if speeding up a particular
  135. routine will have much effect on the top-level timing. By using
  136. Memoized-Time or With-Memoization, a user can memoize the routines in
  137. question then run the same test case multiple times. If the identical
  138. test case runs only, say 5% faster even during the second memoized
  139. run, then this suggests that no amount of memoization in the routines
  140. in question will make more than a 5% difference in the performance of
  141. the test case, and that this is likely not the place to begin the
  142. optimization efforts.
  143.  
  144. MAIN USER ROUTINES
  145. ==================
  146.  
  147.    Define-Memo-Function: a macro that can be used just like "defun", but
  148.      which has the result of defining a memoized function. Also updates
  149.      the doc string and the results of calling "Arglist" (if available in
  150.      current LISP implementation) on that function name. Any of the keywords
  151.      acceptable to Memoize can optionally be passed on, resulting in 
  152.      specialized versions of memoization that seed their initial hash 
  153.      tables from the disk, use particular hash table tests, etc.
  154.  
  155.    With-Memoization: a macro that takes a list of function names and any
  156.      number of LISP forms and executes them in a context where the
  157.      functions are temporarily memoized.
  158.        (With-Memoization (Foo Bar Baz)
  159.          (Form-1)
  160.          (Form-2))    results in executing the two forms while functions
  161.      named Foo, Bar, and Baz are memoized. Useful for getting a quick feel
  162.      for the potential speed benefits of memoization.
  163.  
  164.    Without-Memoization: a macro that executes LISP forms in a context
  165.      where all memoization is temporarily turned off.
  166.      (Without-Memoization
  167.        (Form-1)
  168.        (Form-2))  executes the two forms with no functions memoized.
  169.  
  170.    Memoize: Takes a function name and changes its function definition to
  171.      be a memoized function. 
  172.        (defun Foo (Args) <Body of Foo>)  followed by 
  173.        (Memoize 'Foo) has the same effect as doing 
  174.        (Define-Memo-Function Foo (Args) <Body of Foo>), but calling
  175.      "Memoize" directly is sometimes more convenient when testing things
  176.      out, as it requires no changes in the preexisting code.
  177.  
  178.    Save-Memo-Table: Saves to disk a definition of the hash table
  179.      associated with a given memoized function name. By defining a
  180.      memoized function via 
  181.         (Define-Memo-Function Foo (<Args>)
  182.           <Body>)
  183.       running the time-consuming cases off-line, calling
  184.         (Save-Memo-Table '<Function-Name>)
  185.       then using
  186.         (Define-Memo-Function Foo (<Args>)
  187.           (:Hash-Table-Source :Disk)
  188.           <Body>)
  189.      or by calling Memoize with the :Hash-Table-Source set to :Disk,
  190.      you can have a function "remember" the values it calculated, not only
  191.      in the current run but also in the previous off-line run.
  192.  
  193.    Clear-Memo-Table: Takes a function name and clears out the memo table
  194.      associated with the function. Useful when some internal change makes
  195.      the previously stored values incorrect.
  196.  
  197.    Unmemoize: Takes a function name and returns it to the unmemoized form.
  198.      Useful for timing and for debugging, especially tracing recursive
  199.      routines. In combination with "Memoize", this lets you switch back
  200.      and forth between memoized and normal versions without changing or
  201.      reloading the code. Similarly, Unmemoize-Functions takes a list
  202.      instead of a single one, and Unmemoize-All-Functions unmemoizes
  203.      everything.
  204.  
  205.    Rememoize: Takes the name of a function that is currently unmemoized,
  206.      but which had previously been memoized. Memoizes it again, but uses
  207.      the previous hash table instead of creating a new one. Similarly,
  208.      Rememoize-Functions applies to a list.
  209.  
  210.    Memoized-Function-Call-Count: Given the name of a memoized function,
  211.      tells how many times that function has been called, and which of
  212.      those were simple table lookups that had been stored from a previous
  213.      invocation, vs how many were newly calculated using the original
  214.      function. For a normal memoized function, lets the user see if
  215.      memoization is paying off after a long period of time. For a function
  216.      whose memo table was stored on disk, lets the user see if the stored
  217.      values covered all or most of the cases.
  218.  
  219.    Memoized-Time: Takes a list of functions and a single form and evaluates
  220.      and times the form 3 times, once without memoization, once with
  221.      memoization and an empty cache, and once with memoization but the
  222.      full cache from the previous run. 
  223.  
  224.    *Memoized-Function-Names*: a list of the currently memoized functions.
  225.  
  226. "Memoize", "Memo", and "Clear-Memo-Table" were based on code in Peter
  227. Norvig's outstanding book _Paradigms of AI Programming: Case Studies in
  228. Common LISP_, Morgan Kaufmann, 1992, which in turn was inspired by code in
  229. ex. 3.27 of Abelson, Sussman, and Sussman's _Structure and Interpretation
  230. of Computer Programs_, MIT Press, 1985. Comments and suggestions on the
  231. code were given by Jim Mayfield (University of Maryland Baltimore County),
  232. J. Paul McNamee (AAI Corporation), Peter Norvig (Sun Microsystems), and
  233.