From: frufoo
Subject: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <wlQK7.68341$dk.4921018@bin1.nnrp.aus1.giganews.com>
Hi,
A few days ago, Kenny T. posted about CLIM, and made the comment that
Digitool's MCL has no MOP. (With the amusing side comment, "Are they mad?"
:-D )

Now, I'm a total CL n00b, but Digitool's page does say they have full CLOS
with introspection, etc etc...
I'm confused - isn't CLOS an implementation of a metaobject protocol?  Can
you have one without the other?

Anyway, I thought it was curious, and since nobody else picked up on it in
the original CLIM thread, I thought I'd toss it out for clarification...

Thanks..

From: Kenny Tilton
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <3BFBDB5C.8D596DB9@nyc.rr.com>
frufoo wrote:
> 
> Hi,
> A few days ago, Kenny T. posted about CLIM, and made the comment that
> Digitool's MCL has no MOP. (With the amusing side comment, "Are they mad?"
> :-D )
> 
> Now, I'm a total CL n00b, but Digitool's page does say they have full CLOS
> with introspection, etc etc...

Yes, that much works dandy.

> I'm confused - isn't CLOS an implementation of a metaobject protocol?
>  Can you have one without the other?

Yes. MCL provides (1) proper CLOS behavior by (2) doing whatever they
damn please behind the scenes. :)

ACL (and others) however implement CLOS with stuff very very close to
what you see specified in AMOP the book, or online:
http://www.alu.org/mop/index.html

A good example is the MOP method slot-value-using-class (svuc). No such
animal in MCL. I was told they were after faster slot access. But in ACL
I can (a) define my own metaclass (b) arrange for my own slot-definition
structures to be allocated for certain slots and (c) specialize svuc to
do fun stuff at slot-access time.

btw, while CLOS is in the standard, MOP is not, so MCL is not out of
line.

kenny
clinisys


> 
> Anyway, I thought it was curious, and since nobody else picked up on it in
> the original CLIM thread, I thought I'd toss it out for clarification...
> 
> Thanks..
From: Carl Shapiro
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <ouyk7wkjbne.fsf@panix3.panix.com>
Kenny Tilton <·······@nyc.rr.com> writes:

> A good example is the MOP method slot-value-using-class (svuc). No such
> animal in MCL.

Well, that's just not true at all.

ftp://ftp.digitool.com/pub/mcl/contrib/slot-value-using-class.lisp
From: Kenny Tilton
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <3BFBE6AF.7ABAA4E3@nyc.rr.com>
that's a contrib. that's just stuff Digitool throws on the CD, not part
of MCL.

kenny
clinisys

Carl Shapiro wrote:
> 
> Kenny Tilton <·······@nyc.rr.com> writes:
> 
> > A good example is the MOP method slot-value-using-class (svuc). No such
> > animal in MCL.
> 
> Well, that's just not true at all.
> 
> ftp://ftp.digitool.com/pub/mcl/contrib/slot-value-using-class.lisp
From: Carl Shapiro
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <ouyg077kji5.fsf@panix3.panix.com>
Kenny Tilton <·······@nyc.rr.com> writes:

> that's a contrib. that's just stuff Digitool throws on the CD, not part
> of MCL.

Maybe so, but I don't think your "No such animal in MCL" remark is
very accurate when considering that such solutions do exists.
From: Kenny Tilton
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <3BFC12D0.428A6D98@nyc.rr.com>
Well the point I wanted to make is that a CL implementation need not
expose (or even utilize) the MOP to produce compliant CLOS, and that MCL
has lousy MOP support. No direct-slot-definition-class? Who can program
without that?

btw, I would /love/ to learn MCL 4.3 had MOP support (my version is 4.2)
but something tells me it does not. And MCL is excellent anyway. 

Carl Shapiro wrote:
> Maybe so, but I don't think your "No such animal in MCL" remark is
> very accurate when considering that such solutions do exists.

Are you saying slot-value-using-class /is/ an animal? :) Also, have you
tested that stuff under MCL 4.3? It is advertised as MCL 2.0, and
something tells me it is not necessarily portable (hope this comes out
OK in the NG):

; slot-value-using-class.lisp
;
; Slow and simple implementation of SLOT-VALUE-USING-CLASS and friends
; for MCL 2.0.
; This slows down all calls to SLOT-VALUE & friends and disables all
; optimization for DEFCLASS generated accessors.

(in-package :ccl)

(export '(slot-value-using-class slot-boundp-using-class 
          slot-exists-p-using-class))

(eval-when (:compile-toplevel :execute)
  (require "LISPEQU")                   ; for population-data
  )

(defvar *slot-value-using-class-inited* nil)

(unless *slot-value-using-class-inited*
  (setf (symbol-function 'std-slot-value) #'slot-value
        (symbol-function 'std-set-slot-value) #'set-slot-value
        (symbol-function 'std-slot-boundp) #'slot-boundp
        (symbol-function 'std-slot-exists-p) #'slot-exists-p
        (symbol-function 'std-slot-makunbound) #'slot-makunbound)
  ; This turns off optimization for DEFCLASS generated accessors
  (setq *standard-reader-method-class* nil
        *standard-writer-method-class* nil)
  (dolist (gf (population-data %all-gfs%))
    ; unoptimize existing accessors
    (compute-dcode gf))
  (setq *slot-value-using-class-inited* t))

...etc...

kenny
clinisys
From: Tim Bradshaw
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <fbc0f5d1.0111221029.3cbf5a1b@posting.google.com>
Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·················@nyc.rr.com>...
> 
> Are you saying slot-value-using-class /is/ an animal? :) Also, have you
> tested that stuff under MCL 4.3? It is advertised as MCL 2.0, and
> something tells me it is not necessarily portable (hope this comes out
> OK in the NG):

Somewhat at a tangent to this, does anyone have a feel for how well
implementations can do in the presence of SVUC and its ilk?  It's
always seemed to me as if it should be catastrophic, but I guess it
can't be since some implementations seem to do OK and have it.

I suppose the trick is to have any call to SLOT-VALUE (or the
equivalent inlined code) check (quickly) some `this is OK' flag in the
object, and if it is OK to just fetch the slot.  Of course, I suppose
you already have to deal with half of this since the slot could move,
or vanish, or something, but somehow SVUC fills me with cold fear -
unjustified fear, I guess.

--tim
From: Kenny Tilton
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <3BFD4DB5.A8273690@nyc.rr.com>
I can't make out exactly what hazard you see in SVUC. It's just a GF.
Also, in your this-is-ok? flag, what is this and what is not OK?

kenny
clinisys

Tim Bradshaw wrote:
> 
> Somewhat at a tangent to this, does anyone have a feel for how well
> implementations can do in the presence of SVUC and its ilk?  It's
> always seemed to me as if it should be catastrophic, but I guess it
> can't be since some implementations seem to do OK and have it.
> 
> I suppose the trick is to have any call to SLOT-VALUE (or the
> equivalent inlined code) check (quickly) some `this is OK' flag in the
> object, and if it is OK to just fetch the slot.  Of course, I suppose
> you already have to deal with half of this since the slot could move,
> or vanish, or something, but somehow SVUC fills me with cold fear -
> unjustified fear, I guess.
> 
> --tim
From: Tim Bradshaw
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <fbc0f5d1.0111230530.288058c7@posting.google.com>
Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·················@nyc.rr.com>...
> I can't make out exactly what hazard you see in SVUC. It's just a GF.
> Also, in your this-is-ok? flag, what is this and what is not OK?

Yes, it's just a GF.  And it's right in the core of the code that
reads slots, meaning that every access to a slot in an object has to
call it, potentially executing completely abitrary code.  You might
want slot access to perform pretty well if you use CLOS a lot, for
instance you might want to be able to optimise it to something pretty
close to an array access.  SVUC means that this is not trivial, to say
the least.

My `is this ok' flag says something like:  `There are no applicable
methods other than the system-defined one for SVUC here, and this
class has not been redefined (or had any of its parents redefined)
since last time we worried about this, so it's OK to assume that we
know the slot is in location n, same as it was last time, and just do
AREF to get it'.  If the flag is not true we need to (1) check if
there is an SVUC method, if there is, all is basically lost and we
have to do the whole slow thing; (2) if there is no SVUC method, then
something got redefined: recompute the slot locations, splice them
into the code, set the flag to OK, and carry on.

Of course, this is just my guess, and I'm not an implementor.  I guess
Duane or someone can describe what serious implementations actually do
to try and work reasonably fast.

A good example of the kind of thing you can do is what Genera does on
Ivory (maybe on 3600 too).  If you have a class like:

  (defclass foo ()
    ((x :accessor foo-x)))

then, experimentally, calls to FOO-X are *very* fast in good cases:
faster than a function call (yes, I mean this), and almost as fast as
an access to a slot in something defined with DEFSTRUCT. This is true
even if you have other primary methods on FOO-X, so long as they don't
apply: code like this:

  (defmethod grind-foo ((f foo))
    ...
    (foo-x foo) ...)

gets compiled *really* well.  However, if you say this:

  (defmethod foo-x :after (f)
    ...)

Then everything just falls to bits: performance goes down by factors
of a lot.

After a certain number of calls to DISASSEMBLE I found out that what
happens is that calls to FOO-X get compiled as a check of an
`is-this-ok' bit somewhere, and then code to access the slot (which
itself is very quick, and does slot-location-caching tricks).  If the
OK-bit is not true then it falls back and punts and does the whole
generic function call, except in fact it's even worse than generic
function call for various reasons.  It's possible to get less bad
performance by declaring FOO-X notinline, which avoids the hairy
optimisation, so it always does generic dispatch, but never has to do
the worst-case thing (and generic dispatch is optimised pretty well
too, of course).

This isn't the same case, of course, but it illustrates what I'm
getting at - you want to be able to do fast checks for the good case
so you can get good performance in these cases.  SVUC makes these
checks harder to do, and in some cases means you can never do the fast
thing.  For instance a mediumly-naive implementation might have a
global `have any methods ever been defined on SVUC' bit, and if this
ever gets set true do the whole generic dispatch for every slot access
thus killing performance globally, but only if you use SVUC.  I doubt
any of the commercial implementations are like that - Genera I think
just didn't have SVUC which is another reasonable approach.  I wish
someone would document for posterity the tricks Genera did in CLOS,
because whatever they did made it go very fast (considering the
machines were not fast, of course).

--tim
From: Pierre R. Mai
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <87oflt790i.fsf@orion.bln.pmsf.de>
··········@tfeb.org (Tim Bradshaw) writes:

> Of course, this is just my guess, and I'm not an implementor.  I guess
> Duane or someone can describe what serious implementations actually do
> to try and work reasonably fast.

The stuff you describe is more or less what CMU CL/PCL does.  Inside a
method for example, PCL arranges for the slots storage vector to be
fetched only once per instance.  SLOT-VALUE then gets the slot
location from the class wrapper of the instance (two indirections and
an svref).  If all assumptions still hold, this will be a valid fixnum
index into the slots storage vector, so we can do an unchecked svref.

If something has changed that might invalidate the assumptions, then
the slot location will be nil.  In that case, and in the case of
retrieving the "unbound" value in the other case, we enter the more
general code path, by calling the slot accessor GF for the slot
and class.

So the very fast path of SLOT-VALUE of a constantly named slot (or
slot-accessor uses that get converted to slot-value) inside a method,
takes:

- One unchecked svref of an array with a compile-time constant offset
  to get the slot location index
- One fixnum typecheck
- One unchecked svref into the slots vector with that index
- A check that the retrieved value isn't eq to the unbound slot
  value.

The time to get the slots storage vector, which is amortized between
all calls to slot-value inside the method function, is:

- One PCL-INSTANCE-P and one KERNEL:%INSTANCEP typecheck (all very
  fast for matching objects), possibly optimized to a single typecheck
  by the compiler.
- One KERNEL:%INSTANCE-REF call with constant index, that is a very
  fast low-level "SVREF" for kernel instance objects.

The normal path that goes via the accessor still contains a number of
optimizations that don't have to call S-V-U-C if it isn't necessary.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Pierre R. Mai
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <87k7wh77nw.fsf@orion.bln.pmsf.de>
"Pierre R. Mai" <····@acm.org> writes:

> So the very fast path of SLOT-VALUE of a constantly named slot (or
> slot-accessor uses that get converted to slot-value) inside a method,
> takes:
> 
> - One unchecked svref of an array with a compile-time constant offset
>   to get the slot location index
> - One fixnum typecheck
> - One unchecked svref into the slots vector with that index
> - A check that the retrieved value isn't eq to the unbound slot
>   value.

Furthermore, this cost couldn't be elided if we eliminated S-V-U-C:
You'd also have to eliminate CHANGE-CLASS, the ability to redefine
classes, etc.

This IMHO pretty much vindicates S-V-U-C... ;)

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Kenny Tilton
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <3BFEF86E.98B16A3C@nyc.rr.com>
OK, the concern is performance, I thought we were talking about some
Heisenberg Uncertainty thing in SVUC.

Anyway, thx for the impetus, I have been meaning to look into how badly
my specializations of svuc and (setf svuc) have been whacking my app,
ran some tests today.

The test was:

(defclass xxx ()
  ((yyy :initarg :yyy :accessor yyy)))

(defparameter getct 0)

#+sometimes
(defmethod yyy :around (self)
  (declare (ignore self))
  (incf getct)
  (call-next-method))

(defun test10 () (test 5000))

(defun test (n)
  (let (pop (getct 0))
     (dotimes (j n)
       (push (make-instance 'xxx :yyy (random 10000)) pop))

     (time
      (dotimes (x 10)
        (setf pop (sort pop #'> :key #'yyy))
        (dotimes (j n)
          (setf (yyy (elt pop j)) (random 10000)))))

     getct))

That getct bit is there because I had no idea how many times yyy got hit
during a sort. Turned out to be around 1.2 million sorting 5000 things
10 times.

So unless my arithmetic is off each test was of 1.2 million reads of yyy
and 50k writes.

Without the around on yyy to count the reads the above test runs in
about 1.6 seconds CPU time.

Then I simply changed yyy to have my metaclass and the same test took
about 5.7 seconds.

350% is a lot, but is 4.1 seconds over a million plus accesses a lot?
Hold that thought, we're not done.

But first, a curiosity. I used the ACL Definitions inspector to whack my
specializations one by one. Between svuc and (setf svuc) I have about
eight. The first cool thing is that whacking a specialization which
/applies/ improves speed. Whacking them all got the test doen to 2.0
seconds. Sounds obvious, but I could see how once I had gone my own way
I would be stuck with something suboptimal. The second thing (perhaps it
is the same) is that whacking an SVUC that did /not/ apply did not
change anything. So I think ACL must do some good stuff behind the
scenes when it removes a method.

But the punch line is that putting in that around method on yyy just to
count the accesses of course slowed me right back down to the 5 second
range. So if I want to do something interesting with slot access (and I
have to think that is one of the more common reasons for doing a
metaclass), well, I can run but not hide from a performance hit, and
given that SVUC seems like a better place to muck with slot access.

kenny
clinisys
From: Francis Leboutte
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <js5svt0u29jd81h369b91idhh2l09b46cs@4ax.com>
··········@tfeb.org (Tim Bradshaw) wrote:

>Kenny Tilton <·······@nyc.rr.com> wrote in message news:<·················@nyc.rr.com>...
>> 
>> Are you saying slot-value-using-class /is/ an animal? :) Also, have you
>> tested that stuff under MCL 4.3? It is advertised as MCL 2.0, and
>> something tells me it is not necessarily portable (hope this comes out
>> OK in the NG):
>
>Somewhat at a tangent to this, does anyone have a feel for how well
>implementations can do in the presence of SVUC and its ilk?  It's
>always seemed to me as if it should be catastrophic, but I guess it
>can't be since some implementations seem to do OK and have it.

In the context of Allegrostore or any persistent system, it can not be
catastrophic. BTW I have been very happy to have this 'animal' at hand
because it has allowed me to implement easily writing interception of
persistent slots.

--
www.algo.be
Logo programming : www.algo.be/logo.html
From: Tim Bradshaw
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <fbc0f5d1.0111230743.24e3ff76@posting.google.com>
Francis Leboutte <··········@algo.be> wrote in message news:<··································@4ax.com>...
> 
> In the context of Allegrostore or any persistent system, it can not be
> catastrophic. BTW I have been very happy to have this 'animal' at hand
> because it has allowed me to implement easily writing interception of
> persistent slots.

This (having SVUC) only matters if you want your persitence to work
even for direct slot access.  It would be quite possible to have a
system that did persistence in the absence of SVUC.  Further, if you
are doing persistence then the SVUC performance issues are not likely
in the critical path since you are writing stuff into databases on
persistent storage.

Note, I'm not really against SVUC, I'm interested more in knowing from
people who have knowledge of implementing CLOS how systems with it can
be made to perform reasonably well.

However, in general I *am* against the idea that we should just
provide primitives of unbounded power without worrying about
performance implications or implementation-complexity implications:
something that CL has got mostly right so far (no call/cc, no
dynamic-wind). SVUC may or may not be one of these, but certainly
things like MOPs are areas where you need to worry, a lot, about these
kinds of issues.

--tim
From: Francis Leboutte
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <11vsvt0t17g091hbktbr2mu6cfd00pcvhf@4ax.com>
··········@tfeb.org (Tim Bradshaw) wrote:

>Francis Leboutte <··········@algo.be> wrote in message news:<··································@4ax.com>...
>> 
>> In the context of Allegrostore or any persistent system, it can not be
>> catastrophic. BTW I have been very happy to have this 'animal' at hand
>> because it has allowed me to implement easily writing interception of
>> persistent slots.
>
>This (having SVUC) only matters if you want your persitence to work
>even for direct slot access.  It would be quite possible to have a
>system that did persistence in the absence of SVUC. Further, if you
>are doing persistence then the SVUC performance issues are not likely
>in the critical path since you are writing stuff into databases on
>persistent storage.

It's exactly what I meant (maybe I should have written, 'it will never
be catastrophic in the context of persistence' ...)

>Note, I'm not really against SVUC, I'm interested more in knowing from
>people who have knowledge of implementing CLOS how systems with it can
>be made to perform reasonably well.
>
>However, in general I *am* against the idea that we should just
>provide primitives of unbounded power without worrying about
>performance implications or implementation-complexity implications:
>something that CL has got mostly right so far (no call/cc, no
>dynamic-wind). SVUC may or may not be one of these, but certainly
>things like MOPs are areas where you need to worry, a lot, about these
>kinds of issues.

I like the idea of primitives of unbounded power. Unbounded and
amazing power is actually a feeling I often have when I program in CL
(and especially CLOS). I don't care about implementation-complexity
implications, I'm happy some clever guys handle that for me :-)

>--tim

--
www.algo.be
Logo programming : www.algo.be/logo.html
From: Tim Bradshaw
Subject: Re: Offshoot question re MCL from the CLIM thread
Date: 
Message-ID: <fbc0f5d1.0111260414.b1d66d5@posting.google.com>
Francis Leboutte <··········@algo.be> wrote in message news:<··································@4ax.com>...

> I like the idea of primitives of unbounded power. Unbounded and
> amazing power is actually a feeling I often have when I program in CL
> (and especially CLOS). I don't care about implementation-complexity
> implications, I'm happy some clever guys handle that for me :-)

Oh no, I think that's really bad.  You, or whoever designs the
language, needs to think hard about the tradeoffs and add the
primitives that you need taking careful account of the tradeoffs.

Imagine if I told you that MAKE-LIST and NTH were enough to do
everything arrays do, so we don't need all this array crap in the
language, so maybe I'll give you a dialect without arrays or fail to
teach you arrays in your course.  Sure, you can still do everything
but you might end up with a bad taste since array access has just gone
from constant-time to linear.  Pretty soon `Lisp is slow'.

Or I might argue that full continuations can do everything you need,
and continuations + some facility to do timed interrupts are all you
need to do multithreading.  Now imagine you're an implementor trying
to get something like that to sit on top of posix threads...

It's all about tradeoffs between power, performance, and
implementability without heroic effort.  CL seems to have done pretty
well at this so far (and despite my cavilling, SVUC seems empirically
to be OK)

--tim