Path: utzoo!attcan!uunet!cs.utexas.edu!csd4.csd.uwm.edu!uakari.primate.wisc.edu!indri!caesar!blake!uw-beaver!uw-june!jmaloney From: jmaloney@cs.washington.edu (John Maloney) Newsgroups: comp.lang.smalltalk Subject: Performance of "doesNotUnderstand:" Message-ID: <9070@june.cs.washington.edu> Date: 24 Aug 89 17:08:08 GMT Organization: U of Washington, Computer Science, Seattle Lines: 82 I became interested in a recent claim that using the "doesNotUnderstand: mechanism of Smalltalk to construct "forwarder" objects is a big performance penalty. Since no numbers were provided, I did a little experiment. I created a class Forwarder that responded to the message "add:plus:" by computing the sum of the arguments. I then re-defined the "doesNotUnderstand:" message for Forwarder to re-direct any messages not understood to itself. In this case, the forwarder forwards messages to itself, rather than to a different object, but that should not effect the performance we are interested in. In a more realistic example there might be some additional tests of the message selector and argument count before forwarding the message. These were omitted for simplicity. I then evaluated the following expression: | f count t1 t2 t3 | f _ (Forwarder new). count _ 10000. t1 _ Time millisecondsToRun: [count timesRepeat: []]. t2 _ Time millisecondsToRun: [count timesRepeat: [f bar: 2 plus: 2]]. t3 _ Time millisecondsToRun: [count timesRepeat: [f add: 2 plus: 2]]. Array with: (t2 - t1) with: (t3 - t1). I executed the expression ten times with the following raw results: (5905 318) (5924 327) (5894 317) (5873 327) (5935 338) (5914 327) (5904 338) (5893 328) (5925 308) (5935 339) Note that the first number of each pair is the time in milliseconds to forward a message 10000 times while the second number is the time to process the message directly. These times were measured on a ParcPlace 2.3 interpreter running on a Mac SE with a 16 MHz 68020 accelerator board; the times should be very similar to the performance on a Mac II. The average time to forward the message was 5910 milliseconds. The average time to send the message directly was 327. Thus, forwarding takes roughly 18 times longer than sending a message directly. But, remember that each loop was repeated 10000 times, so direct message sends take 0.3 *micro*seconds while forwarding takes 5.9 microseconds. For comparison, a single floating divide with no coercion takes about 1.1 microseconds on my machine. So, although you wouldn't want to use message forwarding for everything, I don't believe it should be avoided. I have seen some very creative uses of "doesNotUnderstand:", such as Stephen Pope's PropertList object. You can store a property/value pair in a PropertyList (say, "color: #blue") and then later retrieve the property by sending it the message "color". In some applications the flexibility of such a dynamic data structure may be far more important than a slight decrease in performance. On the other hand, if you are building a database and must process millions of records at the absolute maximum speed, you would probably not want to use message forwarding in your basic data structures! By the way, "nil"ing the superclass pointer in Forwarder is not a good idea, since "perform:withArguments:" and many other useful messages are inherited from Object. What is more, "nil"ing the superclass pointer breaks the ParcPlace interpreter. -- John Maloney (jmaloney@june.cs.washington.edu) The code for class Forwarder follows. ------------------ Object subclass: #Forwarder instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'hack'! !Forwarder methodsFor: 'all'! add: a plus: b ^a + b! doesNotUnderstand: aMessage ^self perform: #add:plus: withArguments: aMessage arguments! ! ------------------