[squeak-dev] The Inbox: Kernel-fn.1151.mcz

Chris Cunningham cunningham.cb at gmail.com
Thu Mar 22 04:00:53 UTC 2018


So, speed:

current (^aBoolean or ^aBoolean not) - standard
~~ 71% faster
ul's xor: (aBoolean ifTrue: [^] ifFalse: [^]) 7% slower
em's xor: ( ^aBoolean ifTrue: [] ifFalse: []) 22% slower
ul's block xor: (aBoolean value ifTrue: [^] ifFalse: [^]) 42% slower
em's block xor: (^aBoolean value ifTrue: [] ifFalse: []) 65% slower
fn's xor: (^ alternativeBlock value or ^alternativeBlock value not) 84%
slower

I find it weird that the extra ifTrue:ifFalse: call after the value
actually makes the method faster.  Weird.
But it does make it more consistent/right - it only works when the receiver
and value of the argument are both booleans - with the right results.

Note that even though the percentages are fairly large, the actual time to
run 10,000,000 of these checks took right about 3 second for the slowest of
these - once I've removed the 6 seconds of the loop overhead.  Still fast.

Will be moving to trunk shortly, using this style:  ^aBoolean value ifTrue:
[] ifFalse: []
It just feels more natural.  Feel free to adjust if desired.

-cbc

On Wed, Mar 21, 2018 at 7:20 PM, Eliot Miranda <eliot.miranda at gmail.com>
wrote:

>
>
> On Wed, Mar 21, 2018 at 5:15 PM, Levente Uzonyi <leves at caesar.elte.hu>
> wrote:
>
>> On Wed, 21 Mar 2018, Eliot Miranda wrote:
>>
>>
>>>
>>> On Wed, Mar 21, 2018 at 2:48 PM, Levente Uzonyi <leves at caesar.elte.hu>
>>> wrote:
>>>       On Tue, 20 Mar 2018, Chris Cunningham wrote:
>>>
>>>             Hi.  Reopening this thread - probably tomorrow will
>>> implement 'better' solution (with documentation).
>>>
>>>             On Sat, Feb 10, 2018 at 12:03 PM, Tobias Pape <
>>> Das.Linux at gmx.de> wrote:
>>>
>>>                   > On 10.02.2018, at 20:36, Tony Garnock-Jones <
>>> tonyg at leastfixedpoint.com> wrote:
>>>                   >
>>>                   > On 02/10/2018 07:02 PM, Levente Uzonyi wrote:
>>>                   >> So,
>>>                   >>    a perform: {#or:. #xor:. #and:} atRandom with: b
>>>                   >> would just work for that imaginary interpreter if b
>>> were a Boolean.
>>>                   >
>>>                   > Yes, that "interpreter" works just fine today, if b
>>> is a Boolean. It's
>>>                   > the case where b is a Boolean-producing expression -
>>> such as in a "lazy"
>>>                   > interpreter - that doesn't work without Fabio's
>>> proposed change.
>>>                   >
>>>                   > I went and looked at the ANSI standard (draft), btw
>>> [1].
>>>                   >
>>>                   > There, #xor: is specified as taking only a boolean.
>>>                   >
>>>                   > So this would be an extension, potentially affecting
>>> portability, for
>>>                   > what that's worth these days.
>>>                   >
>>>                   > I think the performance objection has been
>>> well-refuted, and I see the
>>>                   > consistency benefit as being real, but probably
>>> pretty limited, and I
>>>                   > kind of don't like the potential portability
>>> implications.
>>>
>>>                   I presume the main "feeling" here is parallelity:
>>>
>>>                   #& takes an evaluated boolean   #and: takes an
>>> unevaluated block
>>>                   #| takes an evaluated boolean   #or: takes an
>>> unevaluated block
>>>
>>>                   #xor: takes an evaluated boolean but looks like #and:
>>> and #or:,
>>>                   So it seems to belong to the right side, and then we
>>> think again about
>>>                   the left side, come up with symbols, only to find that
>>> #~= and #~~ are already there.
>>>
>>>                   So, just lets go all the way and document #xor:
>>> better, not making take it a block,
>>>                   maybe pointing out that #~= ist typically better
>>> fitted…
>>>
>>>                   Best regards
>>>                           -Tobias
>>>
>>>
>>>             So, I have written some tests for speed and 'ensuring the
>>> arguments are booleans', with another proposed solution.
>>>
>>>             First, speed:
>>>
>>>             #xor: base
>>>             #~= is 124% slower (or 2-1/4 as much time as existing xor:
>>> method)
>>>             #~~ is 75% faster
>>>             #cbcXor: is 32% slower
>>>
>>>             Note: for real speed, use ~~ , not ~= !
>>>
>>>             Why cbcXor: ?  It is the only one that makes sure the
>>> arguments are boolean - fails otherwise.
>>>
>>>
>>> Here is a simpler and faster alternative:
>>>
>>> True >> #xor: aBoolean
>>>
>>>         aBoolean ifTrue: [ ^false ] ifFalse: [ ^true ]
>>>
>>> False >> #xor: aBoolean
>>>
>>>         aBoolean ifTrue: [ ^true ] ifFalse: [ ^false ]
>>>
>>>
>>> and is this noticeably slower?
>>>
>>>  True >> #xor: aBoolean
>>>
>>>         ^aBoolean ifTrue: [ false ] ifFalse: [ true ]
>>>
>>> False >> #xor: aBoolean
>>>
>>>         ^aBoolean ifTrue: [ true ] ifFalse: [ false ]
>>>
>>
>> Yes, it is. Here are the bytecodes for your suggestion:
>>
>> 33 <10> pushTemp: 0
>> 34 <99> jumpFalse: 37
>> 35 <71> pushConstant: true
>> 36 <90> jumpTo: 38
>> 37 <72> pushConstant: false
>> 38 <7C> returnTop
>>
>> And for my variant:
>>
>> 33 <10> pushTemp: 0
>> 34 <98> jumpFalse: 36
>> 35 <79> return: true
>> 36 <7A> return: false
>>
>> I measured the latter to be 14% faster.
>
>
> For xor: it's hardly worth it.  Nicer if the compiler and decompiler did
> the transformation...
>
>
>>
>>
>> Levente
>>
>>
>>
>>> but I would much prefer to see either
>>>
>>> Boolean>>xor: aBooleanOrBlock
>>>
>>>     ^ aBooleanOrBlock value not
>>>
>>> or
>>>
>>> True >> #xor: aBooleanOrBlock
>>>
>>>         ^aBooleanOrBlock value ifTrue: [ false ] ifFalse: [ true ]
>>>
>>> False >> #xor: aBooleanOrBlock
>>>
>>>         ^aBooleanOrBlock value ifTrue: [ true ] ifFalse: [ false ]
>>>
>>> The lack of symmetry with and: and or: is, IMO, bad.
>>>
>>>
>>>       Levente
>>>
>>>
>>>             Tests to run:
>>>
>>>             First, install
>>>
>>>             True>>cbcXor: boolean
>>>             ^boolean isFalse
>>>             True>>isTrue
>>>             ^true
>>>             True>>isFalse
>>>             ^false
>>>             False>>cbcXor: boolean
>>>             ^boolean isTrue
>>>             False>>isFalse
>>>             ^true
>>>             False>>isTrue
>>>             ^false
>>>
>>>             "Setup"
>>>             pairs := #( true false true true false false false true ).
>>>             invalidPairs := { true. 1. true. #[ 1 3 0 9 ]. true. 'abc'.
>>> false. 1. false. #[ 1 3 0 9 ]. false. 'abc'. 'abc'. true. #[ 1 3 0 9 ].
>>> false. }.
>>>             methods := {
>>>             [:f :s| ].
>>>             [:f :s| f xor: s].
>>>             [:f :s| f cbcXor: s].
>>>             [:f :s| f ~= s].
>>>             [:f :s| f ~~ s].
>>>             }.
>>>             "Validity Test"
>>>             validCheck := methods collect: [:m|
>>>             {
>>>             m sourceString.
>>>             #( true false false true ) = (pairs pairsCollect: [:a :b| [m
>>> value: a value: b] on: Error do: [#error]])
>>>             ifTrue: ['Valid'] ifFalse: ['ERRORS'].
>>>             pairs pairsCollect: [:a :b| [m value: a value: b] on: Error
>>> do: [#error]].
>>>             }].
>>>             "all methods are valid"
>>>             "Testing that non-booleans actually result in errors, and
>>> not false positive/negatives"
>>>             invalidCheck := methods collect: [:m|
>>>             {
>>>             m sourceString.
>>>             ((invalidPairs pairsCollect: [:a :b| [m value: a value: b]
>>> on: Error do: [#error]]) select: [:r| r = #error]) size = 8
>>>             ifTrue: ['Valid'] ifFalse: ['ERRORS'].
>>>             invalidPairs pairsCollect: [:a :b| [m value: a value: b] on:
>>> Error do: [#error]].
>>>             }].
>>>             "Only #cbcXor: correctly fails all of these.  Some
>>> interesting results..."
>>>             "Timing test.  Need to run 10,000,000 to get reasonable
>>> distinctions on my machine."
>>>             timing := methods collect: [:m|
>>>             { m sourceString.  [[10000000 timesRepeat: [pairs pairsDo:
>>> m]] timeToRun] on: Error do: [#invalid]. }
>>>             ].
>>>             "And showing percentage slower"
>>>             base := timing first second.
>>>             bench := timing second second - base.
>>>             timing allButFirst collect: [:res| { res first. this := res
>>> second - base. ((this - bench) * 100 / bench) rounded. }].
>>>
>>>             Thanks,
>>>             cbc
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> --
>>> _,,,^..^,,,_
>>> best, Eliot
>>>
>>>
>>
>>
>>
>
>
> --
> _,,,^..^,,,_
> best, Eliot
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20180321/be361631/attachment.html>


More information about the Squeak-dev mailing list