{Begin SubSec The Interlisp-10 Swapper} {Title The Interlisp-10 Swapper} {Text {note The Interlisp-10 swapper was designed by E. L. Wegbreit (PARC) and J. W. Goodwin (BBN), and implemented by J. W. Goodwin.} {index *BEGIN* overlays} Interlisp-10 provides a very large auxilary address space exclusively for swappable arrays (primarily compiled function definitions). In addition to the 256K of {it resident} address space, this "shadow space" can currently accomodate an additonal 256K words, can easily be expanded to 3.5 million words, and with some further modifications, could be expanded to 128 million words. Thus, the overlay system provides essentially unlimited space for compiled code.{foot Since compiled code arrays point to atoms for function names, and strings for error messages, not to mention the fact that programs usually have data base, which are typically lists rather than arrays, there is still a very real and finite limit to the total size of programs that Interlisp-10 can accomodate. However, since much of the system and user compiled code can be made swappable, there is that much more resident space available for these other data-types. }{comment endfootnote} Shadow space and the swapper are intended to be more or less transparent to the user. However, this section is included in the manual to give programmers a reasonable feeling for what overlays are like, without getting unnecessarily technical, as well as to document some new functions and system controls which may be of interest for authors of exceptionally large systems. {Begin SubSec Overlays} {Title Overlays} {Text The shadow space is a very large auxiliary address space used exclusively for an Interlisp data-type called a swappable array{index swappable array}. The regular address space is called the "resident" space to distinguish it from shadow space. Any kind of resident array - compiled code, pointer data, binary data, or a hash array - can be copied into shadow space ("made swappable"), from which it is referred to by a one-word resident entity called a {term handle}{index handle}. The resident space occupied by the original array can then be garbage collected normally (assuming there are no remaining pointers to it, and it has not been made shared by a {fn MAKESYS}). Similarly, a swappable array can be made resident again at any time, but of course this requires (re)allocating the necessary resident space. {it The main purpose and intent of the swapping system is to permit utilization of swappable arrays directly and interchangeably with resident arrays, thereby saving resident space which is then available for other data-types, such as lists, atoms, strings, etc. } This is accomplished as follows: A section of the resident address space is permanently reserved for a {it swapping buffer}.{foot Initially 64,512 word pages, but can be changed via the function {fn SETSBSIZE} described below. }{comment endfootnote} When a particular swappable array is requested, it is brought (swapped) in by mapping or {it overlaying} the pages of shadow space in which it lies onto a section of the swapping buffer{index swapping buffer}. This process is the swapping or overlaying from which the system takes its name. The array is now (directly) accessible. However, further requests for swapping could cause the array to be overlaid with something else, so in effect it is liable to go away at any time. Thus all system code that relates to arrays must recognize handles as a special kind of array, fetch them into the buffer (if not already there), when necessary check that they have not disappeared, fetch them back in if they have, and even be prepared for the second fetch to bring the swappable array in at a different place than did the first. The major emphasis in the design of the overlay system has been placed on running compiled code, because this accounts for the overwhelming majority of arrays in typical systems, and for as much as 60% of the overall data and code. The system supports the running of compiled code directly from the swapping buffer, and the function calling mechanism knows when a swappable definition is being called, finds it in the buffer if it is already there, and brings it in otherwise. Thus, from the user's point of view, there is no need to distinguish between swappable and resident compiled definitions, and in fact {fn CCODEP}{index CCODEP FN} will be true for either. }{End SubSec Overlays} {Begin SubSec Efficiency} {Title Efficiency} {Text Once of the most important design goals for the overlay system was that swappable code should not execute any extra instructions compared to resident code, once it had been swapped in. Thus, the instructions of a swappable piece of code are identical (except for two instructions at the entry point) to those of the resident code from which it was copied,{foot The relocatable instructions are indexed by a base register, to make them run equally well at any location in the buffer. The net slowdown due to this extra level of indirection is too small to measure accurately in the overall running of a program. On analytical grounds, one would expect it to be around 2%. }{comment endfootnote} and similarly when a swappable function calls another function (of any kind) it uses the exact same calling sequence as any other code. Thus, all costs associated with running of swappable code are paid at the point of entry (both calling and returning).{foot If the function in question does nothing, e.g. a compiled {lisp (LAMBDA NIL NIL)}, it costs approximately twice as much to enter its definition if it is swappable as compared to resident. However, very small functions are normally not made swappable (see {fn MKSWAPP}, {PageRef Fn MKSWAPP}), because they don't save much space, and are (typically) entered frequently. Larger programs don't exhibit a measurable slow down since they amortize the entry cost over longer runs. }{comment endfootnote} The cost of the swapping itself, i.e. the fetch of a new piece of swapped code into the buffer, is even harder to measure meaningfully, since two successive fetches of the same function are not the same, due to the fact that the instance created by the first fetch is almost certain to be resident when the second is done, if no swapping is done in between. Similarly, two successive PMAP's (the Tenex operation to fetch one page) are not the same from one moment to another, even if the virtual state of both forks is exactly the same - a difficult constraint to meet in itself.{foot The cost of fetching is probably not in the mapping operation itself but in the first reference to the page, which has a high probability of faulting. This raises the problem of measuring page fault activity, another morass of uncertainty. }{comment endfootnote} Thus, all that can be reported is that empirical measurements and observations have shown no consistent slowdown in performance of systems containing swappable functions viz a viz resident functions. }{End SubSec Efficiency} {Begin SubSec Specifications} {Title Specifications} {Text Associated with the overlay system is a datatype called a {lisp SWPARRAY}{index SWPARRAYP (datatype)}, (type name {lisp SWPARRAYP}), which occupies one word of resident space, plus however much of shadow space needed for the body of the array. {fn ARGLIST}, {fn FNTYP}, {fn NARGS}, {fn GETD}, {fn PUTD}, {fn ARGTYPE}, {fn ARRAYSIZE}, {fn CHANGENAME}, {fn CALLS}, {fn BREAK}, {fn ADVISE}, and {fn EDITA} all work equally well with swappable as resident programs. {fn CCODEP}{index CCODEP FN} is true for all compiled functions/definitions. {FnDef {FnName SWPARRAYP} {FnArgs X} {Text Analogous to {fn ARRAYP}{index ARRAYP FN}. Returns {arg X} if {arg X} is a swappable array and, {lisp NIL} otherwise. }} {FnDef {FnName SCODEP} {FnArgs X} {Text Analogous to {fn CCODEP}. Returns {lisp T} if {arg X} is or has a swapped compiled definition. }} {FnDef {FnName MKSWAP} {FnArgs X} {Text If {arg X} is a resident array, returns a swappable array which is a copy of {arg X}. If {arg X} is a literal atom and {lisp (CCODEP {arg X})} is true, its definition is copied into a swappable array, and it is (undoably) redefined with the latter. {fn MKSWAP} returns {arg X}. }} {FnDef {FnName MKUNSWAP} {FnArgs X} {Text The inverse of {fn MKSWAP}. {arg X} is either a swappable array, or an atom with swapped definition on its {lisp CODE}{index CODE Prop} property. }} {FnDef {FnName MKSWAPP} {FnArgs FNAME CDEF} {Text All compiled definitions begin life as resident arrays, whether they are created by {fn LOAD}, or by compiling to core. Before they are stored away into their atom's function cell, {fn MKSWAPP} is applied to the atom and the array. If the value of {fn MKSWAPP} is {lisp T}, the definition is made swappable; otherwise, it is left resident. By redefining {fn MKSWAPP} or advising it, the user can completely control the swappability of all future definitions as they are created. The initial definition of {fn MKSWAPP} will make a function swappable if (1) {var NOSWAPFLG}{index NOSWAPFLG Var} is {lisp NIL}, and (2) the name of the function is not on {var NOSWAPFNS}{index NOSWAPFNS Var}, and (3) the size of its definition is greater than {var MKSWAPSIZE}{index MKSWAPSIZE Var} words, initially 128. }} {FnDef {FnName SETSBSIZE} {FnArgs N} {Text Sets the size of the swapping buffer to {arg N}, a number of {it pages}. Returns the previous value. {lisp (SETSBSIZE)} returns the current size without changing it. Note: Currently, the system lacks error recovery routines for situations such as a call to a swappable function which is too big for the swapping buffer, or when the size is zero. Therefore, {fn SETSBSIZE} should be used with care. }} }{End SubSec Specifications} {index *END* overlays} }{End SubSec The Interlisp-10 Swapper}