Christophe Weblog Wiki Code Publications Music
some benchmarketing.
authorChristophe Rhodes <csr21@cantab.net>
Sat, 8 Feb 2014 21:02:08 +0000 (21:02 +0000)
committerChristophe Rhodes <csr21@cantab.net>
Sat, 8 Feb 2014 21:02:08 +0000 (21:02 +0000)
els-specializers.org

index b4e3e79bdc188ddb08c098202be4db8ce6ae96cd..d7e129df5a32090cae7ee9c98e8ac209ad957948 100644 (file)
 #| XXX insert motivating example from Newton/Rhodes here |#
 #+end_src
 ** signum
-   NB this example can definitely be done using filtered dispatch.
+   Our second example of the implementation and use of generalized
+   specializers is a reimplementation of one of the examples in
+   \cite{Costanza.etal:2008}: specifically, the factorial function.
+   Here, we will perform dispatch based on the =signum= of the
+   argument, and again, at most one method with a =signum= specializer
+   will be appliable to any given argument, which makes the structure
+   of the specializer implementation very similar to the =cons=
+   specializers in the previous section.
 
-   Point out obvious similarity between this and car-of-cons.  Note
-   the subtlety, though, in generalizer-of-using-class / signum wrt
-   rational vs floating point arguments
+   We have chosen to compare signum values using \texttt{=}, which
+   means that a method with specializer =(signum 1)= will be
+   applicable to positive floating-point arguments (see the first
+   method on =specializer-accepts-generalizer-p= and the method on
+   =specializer=accepts-p= below).  This leads to one subtle
+   difference in behaviour compared to that of the =cons=
+   specializers: in the case of =signum= specializers, the /next/
+   method after any =signum= specializer can be different, depending
+   on the class of the argument.  This aspect of the dispatch is
+   handled by the second method on =specializer-accepts-generalizer-p=
+   below.
 #+begin_src lisp
 (defclass signum-specializer (specializer)
   ((%signum :reader %signum :initarg :signum)))
   (and (realp o) (= (%signum s) (signum o))))
 
 #| again elide more boring methods |#
+#+end_src
+
+Given these definitions, and some more straightforward ones elided for
+reasons of space, we can implement the factorial function as follows:
 
+#+begin_src lisp
 (defgeneric fact (n)
   (:generic-function-class signum-generic-function))
 (defmethod fact ((n (signum 0))) 1)
 (defmethod fact ((n (signum 1))) (* n (fact (1- n))))
-(defmethod fact ((n (signum -1)))
-  (error "factorial of negative number: ~D" n))
+#+end_src
+
+We do not need to include a method on (signum -1), as the standard
+=no-applicable-method= protocol will automatically apply to negative
+real or non-real arguments.
+
+Benchmarketing: we chose to benchmark 20! because that is the largest
+factorial whose answer fits in SBCL's 63-bit fixnums, so as to attempt
+to measure the maximum effect of dispatch (unobscured by allocation /
+gc issues)
+
+| fact (signum-gf) | %fact (fun) | %%fact (gf / 1meth) | fact (signum-gf / 1arg hash-special-case) |
+|------------------+-------------+---------------------+-------------------------------------------|
+|            0.284 |       0.004 |               0.016 |                                     0.032 |
+|            0.076 |       0.008 |               0.012 |                                     0.024 |
+|            0.072 |       0.004 |               0.012 |                                     0.116 |
+|            0.264 |       0.004 |               0.008 |                                     0.120 |
+|            0.292 |       0.008 |               0.012 |                                     0.120 |
+|            0.264 |       0.004 |               0.016 |                                     0.084 |
+|            0.276 |       0.008 |               0.012 |                                     0.092 |
+|            0.264 |       0.008 |               0.012 |                                     0.036 |
+|            0.276 |       0.008 |               0.004 |                                     0.104 |
+|            0.272 |       0.004 |               0.012 |                                     0.020 |
+
+#+begin_src lisp
+(progn (gc :full t) (time (dotimes (i 10000) (%fact 20))))
 #+end_src
 ** HTTP Accept header
    implement RFC2616 content negotiation