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@gmail.com> wrote:


On Wed, Mar 21, 2018 at 5:15 PM, Levente Uzonyi <leves@caesar.elte.hu> wrote:
On Wed, 21 Mar 2018, Eliot Miranda wrote:



On Wed, Mar 21, 2018 at 2:48 PM, Levente Uzonyi <leves@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@gmx.de> wrote:

                  > On 10.02.2018, at 20:36, Tony Garnock-Jones <tonyg@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