[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Strange Things...(long, technical, possibly pointless :-)

I'm currently looking at redesigning a part of my Tarraingim Abstract
Program Visualisation system (built, of course, in Self) and I'm
interested in some feedback related to a couple of particular parts of
the system.

One part of the system is a large object ("allMessages") which is part
of Tarraingim's Encapsulators mechanism which is used to
mostly-transparently trace messages recieved by objects. Currently,
there is one monolithic allMessages object, built as the system is
booted; in this redesign I hope to make things more dynamic.

AllMessages consists of one slot per message in the system (actually
one slot per canonical string that could syntatically be a
message-name). The slots contain a message like:

    allMesages = (|
	foo: a Bar: b = (|rv|
	    tracer notify: 'foo:Bar:' With: a With: b
	    rv: resend.foo: a Bar: b.
	    tracer notify: 'foo:Bar:' Returning: rv.

Any object which is to be traced is modified so that allMessages is a
high-priority parent. when an object recieves a message, it is first
handled by allMessages, which notifies the rest of the system that a
particular object has received a message; resends the message (so the
original behaviour takes effect); notifies the completion of the
message and passes on the return value. There are two known problems
with this (which are not the main subject of this message) firstly, an
error or non-local return in the resent message will cause the system
to miss the completion notification; second, the because all the
slots in allMessages are public, all the slots in the traced object
become public: if that object is using non-overriding private slots
then the traced object will no longer work.

The main problem about which I would like advice is the allMessages
object itself. Currently the main allMessages object inherits from
about 70 category objects each of which contain 50-100 message slots.

	+-------------+	    +----------------+
	| allMessages |---->| foo:bar: = ()  |
	+-------------+	    | foo:baz: = ()  |
		|	    |                | category object#1
	| caz: = () 	|
	| cat = ()	|category object #2

The changes I am considering are twofold: to include a list of all the
selector names in allMessages to facilitate dynamic updating; and to
allow "someMessages" objects - the same structure as allMessages, but
only catching some message names.

Keeping a list of all message names should not be a problem: a large
array or hash table should do the trick. I can dynamically update
allMessages when a selector I wish to add is not in the hash table by
the same method I use to create it (building the method as a string
and evaluating it).

However, in the someMessages case I'm not sure what to do. An obvious
design is to merely build the small objects on the fly: creating
strings describing them and evaluating. An alternative idea is as
follows. First, I could change allMessages so each message was in it's
own one slot object, these inherited from the allMessage categories.
Then, a somemessage object would be build up from these small

	+-------------+	    +-------------------+    +----------------+
	| allMessages |---->| category object#1 |--> | foo:bar: = ()  |
	+-------------+	    +-------------------+    +----------------+
		|			|		       ^
		|			|   +----------------+ |
		v			+-->|  foo:baz: = () | |
	+--------------------+		    +----------------+ |
	| category object #2 |			     ^	       |
	+--------------------+			     |         |
          |	|				+---------------+
	  |  	+----->				| SOME-MESSAGES	|
	  v					+---------------+

In this way I would only need to compile the various message handlers
once: they could be shared. (Another option would be to use _AddSlots
to just copy them into the someMessages object). However, this would
require another level of inheritance for the allMessages object.
(See, David, Randy, Bay, Urs, et al what I've done to your beautiful

What I would especially like to know is the "best" way of arranging
this in terms of speed and space (hints on how to dynamically compile
7000 objects from consVectors in less than three-quaters of a hour
would also sbe appreciated). Last time I tried something this low
level I spend a week timing out various options(*): this time I thought
I'd ask *First*.

Any suggestions, comments, flames, will be warmly appreciated.

James Noble

(*) PS: I don't suppose there is a way to tell the compiler not to
optimise a particular object (rather than globally). For example, I
have encapsulated dictionaries which do a lot of _Defines and _Mirror
removeAllSlots. (The timings were to figure out when it was quicker to
add slots to an empty prototype then replace the whole object with
define vs removing all slots and replacing then a slot at a time)
Cloning an encapsulated object requires building an unencapsulated
clone: dictionaries clone themselves on expansion. With the system
taking up to 700ms per clone, the encapsulated dictionary benchmarks
were looking _very_ bad.