[squeak-dev] Using #= for integer comparison instead of #==

Eliot Miranda eliot.miranda at gmail.com
Fri Nov 19 18:23:53 UTC 2010


On Fri, Nov 19, 2010 at 10:15 AM, Mariano Martinez Peck <
marianopeck at gmail.com> wrote:

>
>
> On Wed, Nov 17, 2010 at 1:13 AM, Eliot Miranda <eliot.miranda at gmail.com>wrote:
>
>>
>>
>> On Tue, Nov 16, 2010 at 3:32 PM, Levente Uzonyi <leves at elte.hu> wrote:
>>
>>> On Tue, 16 Nov 2010, Juan Vuletich wrote:
>>>
>>>  Hi Levente,
>>>>
>>>> Levente Uzonyi wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> I'm mostly ready with this cleanup, only a few methods left in the
>>>>> image that use #== for integer comparison. Some of them must use #==, others
>>>>> may be changed, but I wasn't sure if the code will work if it's changed or
>>>>> not. If you'd like to check these methods, then evaluate the following in a
>>>>> workspace:
>>>>>
>>>>> ... code here...
>>>>>
>>>>> Cheers,
>>>>> Levente
>>>>>
>>>>
>>>> Thanks for the snippet! It is now a method in Cuis. I also checked a bit
>>>> on trunk. In all senders of #nextObject you can apply the pattern in
>>>> #allObjectsDo:. Instead of assuming == 0 for last #nextObject, it assumes
>>>> that Object new will go at the end. If that ever changed, a few places would
>>>> need fixing...
>>>>
>>>
>>> I'm not sure if it's ok to use that pattern in ImageSegment. Even if it's
>>> ok, the code is so messy, that it seems to be hard to apply the pattern
>>> without breaking the code.
>>>
>>>
>>>> As an experiment, in #allObjectsDo: I tried to remove the Object new
>>>> stuff and replace with '[ 0 = object and: [ object isMemberOf: SmallInteger
>>>> ]] whileFalse: ', but my test became extremely slow. I suspect #= is sending
>>>> the message even if it has its own bytecode... So, the only method that
>>>> remains with the '0 ==' pattern in Cuis is #critical:ifLocked: , as I'm not
>>>> sure if we can clean it.
>>>>
>>>
>>> #= is a special selector, so I'm sure it won't send a message if it
>>> doesn't have to.
>>
>>
>> Um, on Cog it sends a message if the method containing the #= has been
>> jitted.
>>
>
> Hi Eliot. I have just read the whole thread, but I wanted to ask you
> something about this line. Did I understand correct??  If #= has already
> been jitted, then Gog doesn't use the short-circuit sends
> (#bytecodePrimEqual) but it does a normal send (a primitive in this case)?
> So...suppose I change SmallInteger >> #=   and I put a halt, then if will be
> halted (and broke everything, of course) once that method was jitted ?
>

Yes, that's right; since in Cog jitted code #= is always sent if you put a
breakpoint in SmallInteger>#= execution will hit the break-point, unlike in
the interpreter.


> And I guess the same happens with all special selectors.
>
> Thanks
>
> Mariano
>
>
>
>> But the issue is /not/ whether there is a send or not.  The issue is what
>> the behaviour of the primitive code is.  In the interpreter #= will
>> short-circuit (avoid the send) if both the receiver and the argument are
>> either a SmallInteger or a Float.  In the JIT the primitive will not fail if
>> the receiver and argument are both SmallIntegers or Floats or if the
>> receiver is a Float and the argument is an Integer, but will fail if the
>> receiver is a SmallInteger and the argument is a Float.
>>
>> In general the VM is free to fail for non-matching numeric types (this is
>> historical, derived from the blue book) so I don't feel Cog is at fault
>> here.  Certainly it is not answering incorrect results; it is simply taking
>> more sends to produce the result in some circumstances. I think in cases
>> like this (where you're enumerating over all objects) using #== is a wiser
>> choice.
>>
>> HTH
>>
>>
>> It's possible that #= is a bit slower than #== because it has to do some
>>> extra checks, but the difference is probably neglible. Here's a benchmark:
>>>
>>> | offset equality identity |
>>> Smalltalk garbageCollect.
>>> offset := [ 1 to: 1000000 do: [ :i | ] ] timeToRun.
>>> equality := [ :x | [ 1 to: 1000000 do: [ :i | 0 = x ] ] timeToRun ].
>>> identity := [ :x | [ 1 to: 1000000 do: [ :i | 0 == x ] ] timeToRun ].
>>> { 0. 1. SmallInteger maxVal. SmallInteger maxVal + 1. nil. Object new.
>>> 0.0. Array new. 1/2 } collect: [ :each |
>>>        each -> ({
>>>                equality value: each.
>>>                identity value: each.
>>>                equality value: each.
>>>                identity value: each.
>>>                equality value: each.
>>>                identity value: each } - offset) ].
>>>
>>> And my results on CogVM:
>>>
>>> {
>>>        0->#(3 2 3 1 3 2).
>>>        1->#(2 4 3 0 3 4).
>>>        1073741823->#(3 3 2 1 3 5).
>>>        1073741824->#(221 4 223 1 223 4).
>>>        nil->#(14 4 14 0 15 4).
>>>        an Object->#(14 5 13 1 14 4).
>>>        0.0->#(622 5 623 2 624 4).
>>>        #()->#(16 5 14 1 16 4).
>>>        (1/2)->#(260 4 259 1 258 5)
>>> }
>>>
>>
>> I'd do many more iterations.  I wouldn't put any faith any millisecond
>> numbers that are less than 3 digits (100 ms).
>>
>>
>>>
>>> So the cause of the slowdown in your #allObjectsDo: implementation is
>>> that #= is only fast if both the receiver and the argument are
>>> SmallIntegers, otherwise there will be message sends.
>>>
>>> Another way to implement #allObjectsDo: without the marker object is to
>>> replace the loop condition with: object class == SmallInteger. It's actually
>>> the same as your implementation without the #= check. #isMemberOf: is
>>> optimized to non-real sends. This is as fast as the current implementation
>>> on my pc.
>>>
>>>
>>> Levente
>>>
>>>
>>>> Cheers,
>>>> Juan Vuletich
>>>>
>>>>
>>>>
>>>
>>
>>
>>
>>
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20101119/b7216c20/attachment.htm


More information about the Squeak-dev mailing list