0.9.7.17:
authorChristophe Rhodes <csr21@cam.ac.uk>
Wed, 7 Dec 2005 14:20:04 +0000 (14:20 +0000)
committerChristophe Rhodes <csr21@cam.ac.uk>
Wed, 7 Dec 2005 14:20:04 +0000 (14:20 +0000)
Document what we've learnt about discriminating functions
in the internals manual
... now we depend on dot (graphviz) to draw pretty state
transition graphs.
... lots o' text.  I hope it's clear.

(it would be good to upload this to the web somewhere, in
order to be the top hit for e.g. "PCL CONSTANT-VALUE" on search
engines, so that the next person to hit the swamp has a chance
to get out before it's too late.  Hack the Makefile enough so
that we can build an html version of an internals manual.)

doc/internals/Makefile
doc/internals/discriminating-functions.dot [new file with mode: 0644]
doc/internals/discriminating-functions.texinfo [new file with mode: 0644]
version.lisp-expr

index a620b84..df076da 100644 (file)
@@ -1,6 +1,6 @@
-.PHONY: top info clean all
+.PHONY: top clean html all
 
-all: info pdf
+all: sbcl-internals.pdf sbcl-internals.info
 
 top:
        sh make-top.sh
@@ -8,10 +8,32 @@ top:
 info: top
        makeinfo sbcl-internals.texinfo
 
-pdf: top
+%.eps: %.dot
+       dot -Tps -Gsize="5,5" -Gratio=compress -Gconcentrate=true $< > $@
+
+%.png: %.dot
+       dot -Tpng -Gsize="5,5" -Gratio=compress -Gconcentrate=true $< > $@
+
+%.txt: %.dot
+       # FIXME.
+       dot -Tcanon $< > $@
+
+%.pdf: %.eps
+       epstopdf $< > $@
+
+sbcl-internals.pdf: top $(patsubst %.dot,%.pdf,$(wildcard *.dot)) *.texinfo
        texi2pdf sbcl-internals.texinfo
 
-clean: 
-       rm -f *.include *.info *.pdf *~ *.cp *.fn *.ky *.log *.pg *.toc \
-               *.tp *.vr *.aux
+sbcl-internals.info: top $(patsubst %.dot,%.txt,$(wildcard *.dot)) *.texinfo
+
+html: html-stamp
 
+html-stamp: top $(patsubst %.dot,%.png,$(wildcard *.dot)) *.texinfo
+       makeinfo --html sbcl-internals.texinfo 
+       # FIXME
+       cp -f *.png sbcl-internals
+       touch html-stamp
+clean: 
+       rm -rf *.include *.info *.pdf *~ *.cp *.fn *.ky *.log *.pg *.toc \
+               *.tp *.vr *.aux *.eps *.png *.dvi *.ps *.txt *.fns \
+               html-stamp sbcl-internals/
diff --git a/doc/internals/discriminating-functions.dot b/doc/internals/discriminating-functions.dot
new file mode 100644 (file)
index 0000000..d843cfa
--- /dev/null
@@ -0,0 +1,53 @@
+strict digraph dfun {
+
+mclimit=2.0
+
+edge [color=gray40]
+
+oneclass [label="one-class"]
+twoclass [label="two-class"]
+oneindex [label="one-index"]
+nn [label="n-n"]
+default [label="default-method-only"]
+nomethods [label="no-methods"]
+constant [label="constant-value"]
+
+subgraph class {
+
+edge [color=black]
+
+oneclass -> twoclass -> oneindex -> nn
+oneclass -> nn
+twoclass -> nn
+
+}
+
+// oneclass -> caching
+// oneclass -> checking
+// oneclass -> dispatch
+// twoclass -> caching
+// twoclass -> checking
+// twoclass -> dispatch
+// oneindex -> caching
+// oneindex -> checking
+// oneindex -> dispatch
+// nn -> caching
+// nn -> checking
+// nn -> dispatch
+
+subgraph class -> caching
+subgraph class -> checking
+subgraph class -> dispatch
+
+initial -> oneclass
+
+initial -> default
+initial -> nomethods
+initial -> constant
+
+initial -> caching
+initial -> checking -> caching
+initial -> dispatch
+checking -> dispatch
+
+}
\ No newline at end of file
diff --git a/doc/internals/discriminating-functions.texinfo b/doc/internals/discriminating-functions.texinfo
new file mode 100644 (file)
index 0000000..d4bc004
--- /dev/null
@@ -0,0 +1,212 @@
+@node Discriminating Functions
+@comment  node-name,  next,  previous,  up
+@chapter Discriminating Functions
+
+@menu
+* The Initial Discriminating Function::
+* Method-Based Discriminating Functions::
+* Accessor Discriminating Functions::
+* Cacheing and Dispatch Functions::
+* The Cacheing Mechanism::
+@end menu
+
+The Common Lisp Object System specifies a great deal of run-time
+customizeability, such as class redefinition, generic function and
+method redefinition, addition and removal of methods and redefinitions
+of method combinations.  The additional flexibility defined by the
+Metaobject Protocol, specifying the generic functions called to achieve
+the effects of CLOS operations (and allowing many of them to be
+overridden by the user) makes any form of optimization seem intractable.
+And yet such optimization is necessary to achieve reasonable
+performance: the MOP specifies that a slot access looks up the class of
+the object, and the slot definition from that class and the slot name,
+and then invokes a generic function specialized on those three
+arguments.  This is clearly going to act against the user's intuition
+that a slot access given an instance should be relatively fast.
+
+The optimizations performed cannot be done wholly at compile-time,
+however, thanks to all of these possibilities for run-time redefinition
+and extensibility.  This section describes the optimizations performed
+in SBCL's CLOS implementation in computing and calling the effective
+method for generic functions.
+
+@node The Initial Discriminating Function
+@comment  node-name,  next,  previous,  up
+@section The Initial Discriminating Function
+
+@findex compute-discriminating-function
+@findex sb-mop:compute-discriminating-function
+
+The system method on @code{SB-MOP:COMPUTE-DISCRIMINATING-FUNCTION},
+under most circumstances, returns a function which closes over a
+structure of type @code{SB-PCL::INITIAL}, and which calls
+@code{SB-PCL::INITIAL-DFUN}.  This discriminating function is
+responsible for implementing the computation of the applicable methods,
+the effective method, and thence the result of the call to the generic
+function.  In addition, it implements optimization of these steps, based
+on the arguments it has been called with since the discriminating
+function was installed and the methods of the generic function.
+
+@float Figure,fig:dfun-transitions
+@image{discriminating-functions}
+@end float
+
+For each substantive change of the generic function (such as addition or
+removal of a method, or other reinitialization) the discriminating
+function is reset to its initial state.
+
+The initial discriminating function can transition into a discriminating
+function optimized for the methods on the generic function
+(@code{SB-PCL::NO-METHODS}, @code{SB-PCL::DEFAULT-METHOD-ONLY},
+@code{SB-PCL::CONSTANT-VALUE}), for slot access
+(@code{SB-PCL::ONE-CLASS}, @code{SB-PCL::TWO-CLASS},
+@code{SB-PCL::ONE-INDEX}, @code{SB-PCL::N-N}@footnote{Would be better
+named as @code{M-N}.}), or for dispatch based on its arguments
+(@code{SB-PCL::CACHING}, @code{SB-PCL::DISPATCH}).  Those in the second
+category can transition into the third, or into a
+@code{SB-PCL::CHECKING} state where the choice between
+@code{SB-PCL::CACHING} and @code{SB-PCL::DISPATCH} has not yet been
+made.
+
+The possible transitions are shown in @ref{fig:dfun-transitions}.
+
+@node Method-Based Discriminating Functions
+@comment  node-name,  next,  previous,  up
+@section Method-Based Discriminating Functions
+
+@findex no-applicable-method
+
+The method-based discriminating functions are used if all the methods of
+the generic function at the time of the first call are suitable:
+therefore, these discriminating function strategies do not transition
+into any of the other states unless the generic function is
+reinitialized.  Of these discriminating functions, the simplest is the
+@code{SB-PCL::NO-METHODS}, which is appropriate when the generic
+function has no methods.  In this case, the discriminating function
+simply performs an argument count check@footnote{Actually, this bit
+isn't currently done.  Oops.} and then calls
+@code{NO-APPLICABLE-METHOD} with the appropriate arguments.
+
+If all of the specializers in all methods of the generic function are
+the root of the class hierarchy, @code{t}, then no discrimination need
+be performed: all of the methods are applicable on every
+call@footnote{Hm, there might be another problem with argument count
+here.}.  In this case, the @code{SB-PCL::DEFAULT-METHOD-ONLY}
+discriminating function can call the effective method directly, as it
+will be the same for every generic function call.@footnote{I wonder if
+we're invalidating this right if we define a method on
+compute-applicable-methods...}
+
+If all methods of the generic function are known by the system to be
+side-effect-free and return constants, and the generic function has
+standard-method-combination and no eql-specialized methods, then the
+@code{SB-PCL::CONSTANT-VALUE} discriminating function can simply cache
+the return values for given argument types.  Though this may initially
+appear to have limited applicability, type predicates are usually of
+this form, as in @ref{ex:pred}@footnote{There is vestigial code in SBCL
+for a currently unused specialization of @code{SB-PCL::CONSTANT-VALUE}
+for boolean values only.}.
+
+@float Example,ex:pred
+@example
+(defgeneric foop (x))
+(defmethod foop ((foo foo)) t)
+(defmethod foop (object) nil)
+@end example
+@end float
+
+More details of the cacheing mechanism are given in @ref{The Cacheing
+Mechanism} below.
+
+@node Accessor Discriminating Functions
+@comment  node-name,  next,  previous,  up
+@section Accessor Discriminating Functions
+
+Accessor Discriminating Functions are used when the effective method of
+all calls is an access to a slot, either reading, writing or checking
+boundness@footnote{Although there is ordinarily no way for a user to
+define a boundp method, some automatically generated generic functions
+have them}; for this path to apply, there must be no non-standard
+methods on @code{SB-MOP:SLOT-VALUE-USING-CLASS} and its siblings.  The
+first state is @code{SB-PCL::ONE-CLASS}, entered when one class of
+instance has been accessed; the discriminating function here closes over
+the wrapper of the class and the slot index, and accesses the slot of
+the instance directly.  
+
+If a direct instance of another class is passed to the generic function
+for slot access, then another accessor discriminating function is
+created: if the index of the slot in the slots vector of each instance
+is the same, then a @code{SB-PCL::TWO-CLASS} function is created,
+closing over the two class wrappers and the index and performing the
+simple dispatch.  If the slot indexes are not the same, then we go to
+the @code{SB-PCL::N-N} state.
+
+For slot accesses for more than two classes with the same index, we move
+to the @code{SB-PCL::ONE-INDEX} state which maintains a cache of
+wrappers for which the slot index is the same.  If at any point the slot
+index for an instance is not the same, the state moves to
+@code{SB-PCL::N-N}, which maintains a cache of wrappers and their
+associated indexes; if at any point an effective method which is not a
+simple slot access is encountered, then the discriminating function
+moves into the @code{SB-PCL::CHECKING}, @code{SB-PCL::CACHING} or
+@code{SB-PCL::DISPATCH} states.
+
+@node Cacheing and Dispatch Functions
+@comment  node-name,  next,  previous,  up
+@section Cacheing and Dispatch Functions
+
+@code{SB-PCL::CACHING} functions simply cache effective methods as a
+function of argument wrappers, while @code{SB-PCL::DISPATCH} functions
+have code that computes the actual dispatch.  @code{SB-PCL::CHECKING}
+functions have a cache, but on cache misses will recompute whether or
+not to generate a @code{SB-PCL::CACHING} or @code{SB-PCL::DISPATCH}
+function.
+
+(FIXME: I'm actually not certain about the above paragraph.  Read the
+code again and see if it makes any more sense.)
+
+@node The Cacheing Mechanism
+@comment  node-name,  next,  previous,  up
+@section The Cacheing Mechanism
+
+In general, the cacheing mechanism works as follows: each class has an
+associated wrapper, with some number of uniformly-distributed random
+hash values associated with it; each cache has an associated index into
+this pseudovector of random hash values.  To look a value up from a
+cache from a single class, the hash corresponding to the cache's index
+is looked up and reduced to the size of the cache (by bitmasking, for
+cache sizes of a power of two); then the entry at that index is looked
+up and compared for indentity with the wrapper in question.  If it
+matches, this is a hit; otherwise the cache is walked sequentially from
+this index, skipping the 0th entry.  If the original index is reached,
+the cache does not contain the value sought@footnote{Actually, there's
+some kind of scope for overflow.}.
+
+To add an entry to a cache, much the same computation is executed.
+However, if there is a collision in hash values, before the cache is
+grown, an attempt is made to fill the cache using a different index into
+the wrappers' hash values.
+
+Wrappers are invalidated for caches by setting all of their hash values
+to zero.  (Additionally, they are invalidated by setting their
+@code{depthoid} to -1, to communicate to structure type testers, and
+their @code{invalid} to non-@code{nil}, communicating to
+@code{obsolete-instance-trap}.
+
+The hash value for multiple dispatch is computed by summing all of the
+individual hash values from each wrapper (excluding arguments for which
+all methods have @code{t} specializers, for which no dispatch
+computation needs to be done), jumping to the cache miss case if any
+wrapper has a zero hash index.
+
+(FIXME: As of sbcl-0.9.x.y, the generality of multiple hash values per
+wrapper was removed, as it appeared to do nothing in particular for
+performance in real-world situations.)
+
+References (O for working BibTeX):
+
+The CLOS standards proposal
+
+Kiczales and Rodruigez
+
+AMOP
index 52ca12e..0c13a86 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"0.9.7.16"
+"0.9.7.17"