The Inbox: Kernel-fn.1151.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-fn.1151.mcz

Levente Uzonyi
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 <[hidden email]> wrote:
>
>       > On 10.02.2018, at 20:36, Tony Garnock-Jones <[hidden email]> 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 ]

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
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-fn.1151.mcz

Eliot Miranda-2


On Wed, Mar 21, 2018 at 2:48 PM, Levente Uzonyi <[hidden email]> 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 <[hidden email]> wrote:

      > On 10.02.2018, at 20:36, Tony Garnock-Jones <[hidden email]> 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 ]

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


cbc
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-fn.1151.mcz

cbc


On Wed, Mar 21, 2018 at 4:09 PM, Eliot Miranda <[hidden email]> wrote:


On Wed, Mar 21, 2018 at 2:48 PM, Levente Uzonyi <[hidden email]> 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 <[hidden email]> wrote:

      > On 10.02.2018, at 20:36, Tony Garnock-Jones <[hidden email]> 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 ]

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.

I started out with this mind-set, but Tobias' arguments crystallized the discussion for me:
1. Adhere to standards (booleans only)
2. Or, maybe #xor: behave like and: and or:.
I also found another discussion - unlike and: and or:, for xor: you will ALWAYS have to evaluate the block - and that doubles (or significantly more) the amount of time needed to run the method.  It is still small, of course.

With this last slight variation, our rule would be "as long as the receiver is a boolean and the arguments' value is a boolean, then we apply the rule".
I could live with that.

-cbc


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






Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-fn.1151.mcz

Levente Uzonyi
In reply to this post by Eliot Miranda-2
On Wed, 21 Mar 2018, Eliot Miranda wrote:

>
>
> On Wed, Mar 21, 2018 at 2:48 PM, Levente Uzonyi <[hidden email]> 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 <[hidden email]> wrote:
>
>                   > On 10.02.2018, at 20:36, Tony Garnock-Jones <[hidden email]> 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.

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
>
>

Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-fn.1151.mcz

Eliot Miranda-2


On Wed, Mar 21, 2018 at 5:15 PM, Levente Uzonyi <[hidden email]> wrote:
On Wed, 21 Mar 2018, Eliot Miranda wrote:



On Wed, Mar 21, 2018 at 2:48 PM, Levente Uzonyi <[hidden email]> 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 <[hidden email]> wrote:

                  > On 10.02.2018, at 20:36, Tony Garnock-Jones <[hidden email]> 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


cbc
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-fn.1151.mcz

cbc
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 <[hidden email]> wrote:


On Wed, Mar 21, 2018 at 5:15 PM, Levente Uzonyi <[hidden email]> wrote:
On Wed, 21 Mar 2018, Eliot Miranda wrote:



On Wed, Mar 21, 2018 at 2:48 PM, Levente Uzonyi <[hidden email]> 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 <[hidden email]> wrote:

                  > On 10.02.2018, at 20:36, Tony Garnock-Jones <[hidden email]> 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






12