From: Kleanthes Koniaris
Subject: CLOS and type declarations
Date: 
Message-ID: <36633@dime.cs.umass.edu>
Dear Lispers:

I recently read a book on CLOS, and I was very excited to try it.
The first thing that I wanted to do was write a generic math package,
with operations g+, g-, g/ and g*.

For example,

(defmethod g+ ((i number) (j number)) (+ i j))

(defmethod g+ ((a number) (b vector))
  (map 'vector #'(lambda (x) (g+ a x)) b))

(defmethod g+ ((b vector) (a number)) (g+ a b)) ; save typing....

(defmethod g+ ((a vector) (b vector))
  (unless (= (length a) (length b))
    (error "How can you add vectors of different lengths?"))
  (map 'vector #'(lambda (x1 x2) (g+ x1 x2)) a b))

And so on.

Now, I did the following, using Allegro CL and PCL:

(proclaim '(optimize (speed 3)))

(defun foo-clos (a b)
  (declare (type fixnum a b))
  (g+ a b))

(defun foo (a b)
  (declare (type fixnum a b))
  (+ a b))

Then, I decided to time the functions, along the lines of

(defun foo () (dotimes (i 10000) (foo-clos i i)))
(compile 'foo)
(time (foo))

Anyway, the CLOS version turned out to be around 30 times SLOWER.
Yes, you read right -- 30 times SLOWER.

It seemed to me that the following should take place when the compiler
is looking at foo-clos:

 (defun foo-clos (a b)
   (declare (type fixnum a b))
   (g+ a b))

 Hmmm, given that a and b are both declared to be fixnums, and fixnums
 are numbers, it is clear that I need the method

 (defmethod g+ ((i number) (j number)) (+ i j)).

 I could then inline the code and replace "g+" with regular "+"....

In fact, it would then be even better if it realized that + was taking
two fixnums....  (Never mind, if that was to take place I could give
up C forever.)

Why doesn't something make this most obvious optimization?  After
reflecting on this subject for some time, I decided that the compiler
might be scared that after compilation I'll write something like

(defmethod g+ :after ((a number) (b number)) ....).

If it just inlined the primary method, then it would not be able to
call the after method, and this is clearly unacceptable.  However,
let's say that I'm done writing code, and I *NEVER* will add more
without a recompile.  Then, shouldn't there some way to tell CLOS that
it is time to optimize seriously?

I used PCL in Allegro CL, but I don't have Franz's new Lisp with their
integrated CLOS.  I have heard that Lucid wrote their own CLOS.  I
presume that Symbolics has their own CLOS.  Do these other versions of
CLOS do what I want?  There is no doubt that CLOS is very beautiful,
but unless it goes faster, I fear that it will never be used.
Sometimes I get scared that I will one day have to give up Common Lisp
and turn to C++.  :^(

	--kleanthes (···@gang.umass.edu)

(require :standard-disclaimer)
From: Barry Margolin
Subject: Re: CLOS and type declarations
Date: 
Message-ID: <1991Sep23.171637.16572@Think.COM>
[I've redirected this discussion to comp.lang.clos, as most of the CLOS
wizards don't have access to comp.lang.lisp.]

In article <·····@dime.cs.umass.edu> ···@gang.umass.edu (Kleanthes Koniaris) writes:
>Dear Lispers:
>
>I recently read a book on CLOS, and I was very excited to try it.
>The first thing that I wanted to do was write a generic math package,
>with operations g+, g-, g/ and g*.
>
>For example,
>
>(defmethod g+ ((i number) (j number)) (+ i j))
>
>(defmethod g+ ((a number) (b vector))
>  (map 'vector #'(lambda (x) (g+ a x)) b))
>
>(defmethod g+ ((b vector) (a number)) (g+ a b)) ; save typing....
>
>(defmethod g+ ((a vector) (b vector))
>  (unless (= (length a) (length b))
>    (error "How can you add vectors of different lengths?"))
>  (map 'vector #'(lambda (x1 x2) (g+ x1 x2)) a b))
>
>And so on.
>
>Now, I did the following, using Allegro CL and PCL:
>
>(proclaim '(optimize (speed 3)))
>
>(defun foo-clos (a b)
>  (declare (type fixnum a b))
>  (g+ a b))
>
>(defun foo (a b)
>  (declare (type fixnum a b))
>  (+ a b))
>
>Then, I decided to time the functions, along the lines of
>
>(defun foo () (dotimes (i 10000) (foo-clos i i)))
>(compile 'foo)
>(time (foo))
>
>Anyway, the CLOS version turned out to be around 30 times SLOWER.
>Yes, you read right -- 30 times SLOWER.

PCL is *not* highly optimized.  I just tried it in Lucid and the CLOS
version was only 10 times slower.  Replacing addition with multiplication
resulted in only a 3x slowdown, because multiplication isn't implemented in
hardware on the Sun-4/370 I was using (replacing (* a b) with (the fixnum
(* a b)) sped the non-CLOS function up some more, so the CLOS version was
5x slower, but the compiler can't make this optimization on its own).

>It seemed to me that the following should take place when the compiler
>is looking at foo-clos:
>
> (defun foo-clos (a b)
>   (declare (type fixnum a b))
>   (g+ a b))
>
> Hmmm, given that a and b are both declared to be fixnums, and fixnums
> are numbers, it is clear that I need the method
>
> (defmethod g+ ((i number) (j number)) (+ i j)).
>
> I could then inline the code and replace "g+" with regular "+"....

First of all, remember that you're doing this exercise in PCL, which is not
integrated with Allegro's compiler.  The "P" in "PCL" stands for
"Portable", and there are no portable ways to cause type-specific inline
coding.

Also, you never proclaimed G+ to be INLINE.  Without that declaration, no
Common Lisp compiler will inline any calls to G+.  I just tried it in Lucid
and it doesn't help, but even as compilers get smarter about CLOS you'll
still need to do this.

>In fact, it would then be even better if it realized that + was taking
>two fixnums....  (Never mind, if that was to take place I could give
>up C forever.)

Well, if it did inline substitution of the method body, that optimization
would come naturally.

>Why doesn't something make this most obvious optimization?  After
>reflecting on this subject for some time, I decided that the compiler
>might be scared that after compilation I'll write something like
>
>(defmethod g+ :after ((a number) (b number)) ....).

or you might do

(defmethod g+ ((a fixnum) (b fixnum)) ...)

or

(remove-method #'g+ (find-method #'g+ nil '(number number)))

or any of a zillion other things that could affect what gets run when you
call G+ on two fixnums.

>If it just inlined the primary method, then it would not be able to
>call the after method, and this is clearly unacceptable.  However,
>let's say that I'm done writing code, and I *NEVER* will add more
>without a recompile.  Then, shouldn't there some way to tell CLOS that
>it is time to optimize seriously?

(proclaim '(inline g+)) is the only standard way to indicate that you don't
plan on redefining a function (or at least that you don't care whether
previously-compiled callers get the new version).

Some CL implementations provide a "block compilation" facility, that
compiles a bunch of files and uses static linking to speed up the function
calling.  This kind of facility might be extended to optimize CLOS better.

>I used PCL in Allegro CL, but I don't have Franz's new Lisp with their
>integrated CLOS.  I have heard that Lucid wrote their own CLOS.  I
>presume that Symbolics has their own CLOS.  Do these other versions of
>CLOS do what I want?  There is no doubt that CLOS is very beautiful,
>but unless it goes faster, I fear that it will never be used.
>Sometimes I get scared that I will one day have to give up Common Lisp
>and turn to C++.  :^(

I think you're going a bit far when you say that this will prevent it from
being used.  Most uses of OO programming are for more structured objects,
not for simple data types such as integers and fixnums.  For these types,
the overhead of CLOS is not nearly as significant.

Also, I think your condemnation of CLOS is premature.  We're just seeing
the first releases of everyone's CLOS implementations, so I think it's a
bit unreasonable to expect it to be tied well into the arcane area of
compiler optimizers.  CLOS was designed with the kinds of optimizations you
described in mind, but you have to give the implementors time.
-- 
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar