[squeak-dev] Object>>is:

On Thu, 10 Feb 2011, David T. Lewis wrote:
> On Fri, Feb 11, 2011 at 02:51:26AM +0100, Levente Uzonyi wrote:
>> On Fri, 11 Feb 2011, Igor Stasenko wrote:
>>>>> On 11 February 2011 02:28, David T. Lewis <lewis at mail.msen.com> wrote:
>>>> We had a good deal of discussion earlier about adding Object>>is:
>>>> with an inconclusive outcome:
>>>> ?http://lists.squeakfoundation.org/pipermail/squeak-dev/2010-April/148501.html>>>>>>>> Igor's original proposal is:
>>>> ?http://lists.squeakfoundation.org/pipermail/squeak-dev/2009-June/136793.html>>>>>>>> I have been tinkering around with Juan's SimpleMorphic in hopes of
>>>> getting it running in Squeak alongside MVC and Morphic, and it would
>>>> be convenient to have the default implementation of #is: in the image
>>>> so I don't have to put it in a package override.
>>>>>>>>>> Just yesterday i had discussion about this with people in the lab. I
>>> can't say that i heard something new regarding this,
>>> and not saying that i'd like to resurrect the discussion.
>>> So, in short: there was no objection concerning getting rid of isXXX
>>> in favor of using #is: method.
>>>> What about performance?
>> I do not think that Juan would have chosen to use the #is: idiom for
> his Cuis Morphic implementation if he thought that performance would
> be a problem. It seems to work well.
You're right, it's pretty fast, but methods that return only a boolean
and have no arguments are special methods, which is true for almost every
#is* methods. I did a quick benchmark to see the effects of the change:
Object
compile: 'is: aSymbol
^false'
classified: 'testing'.
Morph
compile: 'is: aSymbol
^aSymbol == #Morph or: [ super is: aSymbol ]'
classified: 'testing'.
{
[
Smalltalk garbageCollect.
(1 to: 5) collect: [ :run |
[ SystemNavigation default allObjectsDo: [ :each | ] ] timeToRun ] ].
[
Smalltalk garbageCollect.
(1 to: 5) collect: [ :run |
[ SystemNavigation default allObjectsDo: [ :each | each isMorph ] ] timeToRun ] ].
[
Smalltalk garbageCollect.
(1 to: 5) collect: [ :run |
[ SystemNavigation default allObjectsDo: [ :each | each is: #Morph ] ] timeToRun ] ] }
collect: [ :each | each value ].
With SqueakVM (4.0.3 windows VM), I got the following results:
#(#(43 44 45 44 44) #(53 53 54 53 54) #(64 64 64 64 64)).
So using #is: #Morph instead of #isMorph is 2.13x slower in this case.
With CogVM (r2361 on windows), I got these (after a few runs that ensure
that Cog compiles the code to machine code):
#(#(16 17 16 16 16) #(20 14 27 19 20) #(27 20 21 20 20))
So using #is: #Morph instead of #isMorph is 1.42x slower in this case.
I calculated the speedup with the following method (replace the array to
the other case's to get the other result):
#(#(16 17 16 16 16) #(20 14 27 19 20) #(27 20 21 20 20))in: [ :results |
(results third average - results first average / (results second average - results first average)) asFloat ].
The difference for Cog is not significant, maybe just noise, but it's
clear that using #is: is slower with SqueakVM.
Let's see a bit larger scale benchmark: SmalltalkImage >>
#cleanUpUndoCommands. Note that this method is a "heavy user" of #is*.
(1 to: 5) collect: [ :run |
Smalltalk garbageCollect.
[ Smalltalk cleanUpUndoCommands ] timeToRun ].
With SqueakVM:
#isMorph : #(68 68 69 68 68)
#is: #Morph : #(83 83 90 82 83)
The difference is still there, but obviously smaller.
With CogVM (I couldn't force it to compile the method to machine code):
#isMorph : #(101 101 103 103 103)
#is: #Morph : #(103 102 103 103 104)
No real difference.
So code that uses #is* methods in tight loops may be slower with SqueakVM,
but probably won't with CogVM. The change probably won't have measurable
effect on other code.
>>>>>> The only thing which still looks controversial is too simple default
>>> implementation which answers false.
>>>>>> To my current opinion, Juan's variant is preferable. :) Yes. it should
>>> answer false and don't contain any extra logic , like trying to follow
>>> class hierarchy etc.
>> My intention is to include the method copied directly from Cuis, which is:
>> is: aSymbol
> "A means for cleanly replacing all isXXX like methods.
> Please use judiciously!
> Suggested by Igor Stasenko at
>http://lists.squeakfoundation.org/pipermail/squeak-dev/2009-June/136793.html">> ^false
Okay.
>>>> Because it serves to replace isXXXX pattern, and not adding something
>>> new (i.e. more 'userful'). So, answering false for Object class fits
>>> well for this purpose.
>>>>>> And of course solution to that problem is simple:
>>> - avoid writing code in style, which require isXXX tests and then
>>> branching. Nicely written code should use message dispatch instead.
>>> Too bad, we are not living in perfect world :)
>>>> Is it better to flood superclasses with no-op extension methods?
>>>> Absolutely no. But the proposal is from June 2009 and was supported
> by Andreas, whose judgement I trust a lot. This is the first time in
> over 10 years of squeaking that I have ever suggested adding a method
> to Object, so it's really not too much of a flood. And I promise
> never to do it again ;-)
The flooding is unrelated to #is:. It would be the side effect of Igor's
suggestion: "- avoid writing code in style, which require isXXX tests and
then branching.".
Let's see the above example: SmalltalkImage >> #cleanUpUndoCommands. It
sends #isMorph to all objects and then does something if the result is
true.
How could it be modified to satisfy Igor's suggestion?
Implement Object >> #cleanUpUndoCommands which does nothing (this is the
bloat) and implement Morph >> #cleanUpUndoCommands which does the real
work. Then send this method from SmalltalkImage's method to all objects.
No branching on the result of a #is* method, but an extra method for
Object. Why is this worse than using #isMorph? Because this method is
hardly useful from any other code, but #isMorph is.
Levente
>> Dave
>>>