opal optimize to do limits

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

opal optimize to do limits

Nicolai Hess-3-2
What is the purpose of replacing expressions, used as limits in a
to:do: call with by a new temporary variable?

For example:

OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
the code is

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count - 1 do: [ :each | sum := sum + each].
    ^sum

the decompiled bytecode :

exampleToDoArgumentLimitIsExpression
    | tmp1 tmp2 tmp3 |
    tmp1 := 10.
    tmp2 := 0.
    tmp3 := tmp1 - 1.
    1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
    ^ tmp2

So, the expression (count - 1) in the to:do: loop is replaced by a new
temporay (tmp3).

Ok, but why ?
Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

stepharo
SSA form?

Le 19/11/15 22:14, Nicolai Hess a écrit :

> What is the purpose of replacing expressions, used as limits in a
> to:do: call with by a new temporary variable?
>
> For example:
>
> OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
> the code is
>
> exampleToDoArgumentLimitIsExpression
>     | count sum |
>     count := 10.
>     sum := 0.
>     1 to: count - 1 do: [ :each | sum := sum + each].
>     ^sum
>
> the decompiled bytecode :
>
> exampleToDoArgumentLimitIsExpression
>     | tmp1 tmp2 tmp3 |
>     tmp1 := 10.
>     tmp2 := 0.
>     tmp3 := tmp1 - 1.
>     1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
>     ^ tmp2
>
> So, the expression (count - 1) in the to:do: loop is replaced by a new
> temporay (tmp3).
>
> Ok, but why ?


Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Nicolai Hess-3-2


2015-11-19 22:29 GMT+01:00 stepharo <[hidden email]>:
SSA form?


and for method arguments?

foo: count
    | sum |
    sum := 0.
    1 to: count do: [ :ku | sum := sum + ku ].
    ^ sum

transforms to:

 "foo: arg1
    | tmp1 tmp2 |
    tmp1 := 0.
    tmp2 := arg1.
    1 to: tmp2 do: [ :tmp3 | tmp1 := tmp1 + tmp3 ].
    ^ tmp1"


 
Le 19/11/15 22:14, Nicolai Hess a écrit :

What is the purpose of replacing expressions, used as limits in a
to:do: call with by a new temporary variable?

For example:

OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
the code is

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count - 1 do: [ :each | sum := sum + each].
    ^sum

the decompiled bytecode :

exampleToDoArgumentLimitIsExpression
    | tmp1 tmp2 tmp3 |
    tmp1 := 10.
    tmp2 := 0.
    tmp3 := tmp1 - 1.
    1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
    ^ tmp2

So, the expression (count - 1) in the to:do: loop is replaced by a new
temporay (tmp3).

Ok, but why ?



Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Eliot Miranda-2
In reply to this post by Nicolai Hess-3-2
Because were to:do: not inlined the expression passed as the to: actual argument would be evaluated exactly once (bound to the formal argument for to:) and so assigning to a temporary preserves the semantics in the most direct way.  If the expression has side effects then those side effects must occur only once.

Note that were the to: expression merely another variable then the compiler could only substitute the original variable if it's value could not change, e.g. in

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each].
    ^sum

It is ok to use count directly as the limit variable, but in this it is not:

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each. count := #somethingElseEntirely].
    ^sum

_,,,^..^,,,_ (phone)

> On Nov 19, 2015, at 1:14 PM, Nicolai Hess <[hidden email]> wrote:
>
> What is the purpose of replacing expressions, used as limits in a
> to:do: call with by a new temporary variable?
>
> For example:
>
> OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
> the code is
>
> exampleToDoArgumentLimitIsExpression
>     | count sum |
>     count := 10.
>     sum := 0.
>     1 to: count - 1 do: [ :each | sum := sum + each].
>     ^sum
>
> the decompiled bytecode :
>
> exampleToDoArgumentLimitIsExpression
>     | tmp1 tmp2 tmp3 |
>     tmp1 := 10.
>     tmp2 := 0.
>     tmp3 := tmp1 - 1.
>     1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
>     ^ tmp2
>
> So, the expression (count - 1) in the to:do: loop is replaced by a new
> temporay (tmp3).
>
> Ok, but why ?

Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Clément Béra
You can use whileTrue: if it's a problem for you not to evaluate the limit at each iteration of the loop.

If you look at OrderedCollection for example, there are implemented differently in Pharo/VW and Squeak. Pharo and VW use to:do: whereas Squeak uses whileTrue: to iterate over the collection. Hence, if you remove elements from the OrderedCollection while iterating over it, the behavior is different.


2015-11-20 1:34 GMT+01:00 Eliot Miranda <[hidden email]>:
Because were to:do: not inlined the expression passed as the to: actual argument would be evaluated exactly once (bound to the formal argument for to:) and so assigning to a temporary preserves the semantics in the most direct way.  If the expression has side effects then those side effects must occur only once.

Note that were the to: expression merely another variable then the compiler could only substitute the original variable if it's value could not change, e.g. in

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each].
    ^sum

It is ok to use count directly as the limit variable, but in this it is not:

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each. count := #somethingElseEntirely].
    ^sum

_,,,^..^,,,_ (phone)

> On Nov 19, 2015, at 1:14 PM, Nicolai Hess <[hidden email]> wrote:
>
> What is the purpose of replacing expressions, used as limits in a
> to:do: call with by a new temporary variable?
>
> For example:
>
> OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
> the code is
>
> exampleToDoArgumentLimitIsExpression
>     | count sum |
>     count := 10.
>     sum := 0.
>     1 to: count - 1 do: [ :each | sum := sum + each].
>     ^sum
>
> the decompiled bytecode :
>
> exampleToDoArgumentLimitIsExpression
>     | tmp1 tmp2 tmp3 |
>     tmp1 := 10.
>     tmp2 := 0.
>     tmp3 := tmp1 - 1.
>     1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
>     ^ tmp2
>
> So, the expression (count - 1) in the to:do: loop is replaced by a new
> temporay (tmp3).
>
> Ok, but why ?


Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Nicolai Hess-3-2
Thanks eliot, Clement,

but if the argument for to:do: is not an expression but one of the method argument, this
transformation is not needed. Or still?




2015-11-20 8:31 GMT+01:00 Clément Bera <[hidden email]>:
You can use whileTrue: if it's a problem for you not to evaluate the limit at each iteration of the loop.

I don't need this. I just compared some compiled methods bytecodes after the latest fix for the wrong frame size calculation.
For this, I compared opal compilers result with that of the old compiler. And I noticed that some methods compiled with
opal had some "hidden" temps and couldn't explain way:)

 

If you look at OrderedCollection for example, there are implemented differently in Pharo/VW and Squeak. Pharo and VW use to:do: whereas Squeak uses whileTrue: to iterate over the collection. Hence, if you remove elements from the OrderedCollection while iterating over it, the behavior is different.


2015-11-20 1:34 GMT+01:00 Eliot Miranda <[hidden email]>:
Because were to:do: not inlined the expression passed as the to: actual argument would be evaluated exactly once (bound to the formal argument for to:) and so assigning to a temporary preserves the semantics in the most direct way.  If the expression has side effects then those side effects must occur only once.

Note that were the to: expression merely another variable then the compiler could only substitute the original variable if it's value could not change, e.g. in

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each].
    ^sum

It is ok to use count directly as the limit variable, but in this it is not:

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each. count := #somethingElseEntirely].
    ^sum

_,,,^..^,,,_ (phone)

> On Nov 19, 2015, at 1:14 PM, Nicolai Hess <[hidden email]> wrote:
>
> What is the purpose of replacing expressions, used as limits in a
> to:do: call with by a new temporary variable?
>
> For example:
>
> OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
> the code is
>
> exampleToDoArgumentLimitIsExpression
>     | count sum |
>     count := 10.
>     sum := 0.
>     1 to: count - 1 do: [ :each | sum := sum + each].
>     ^sum
>
> the decompiled bytecode :
>
> exampleToDoArgumentLimitIsExpression
>     | tmp1 tmp2 tmp3 |
>     tmp1 := 10.
>     tmp2 := 0.
>     tmp3 := tmp1 - 1.
>     1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
>     ^ tmp2
>
> So, the expression (count - 1) in the to:do: loop is replaced by a new
> temporay (tmp3).
>
> Ok, but why ?



Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Eliot Miranda-2
Hi Nicolai,

On Nov 20, 2015, at 12:33 AM, Nicolai Hess <[hidden email]> wrote:

Thanks eliot, Clement,

but if the argument for to:do: is not an expression but one of the method argument, this
transformation is not needed. Or still?

That's the corollary to what I said.  Since method (*) arguments are read-only it is safe to use them directly and avoid the transformation.  One could implement this quickly in the Squeak compiler too :-).  How many cases have you identified where this matters?  Is it very common?


(* and with the right preference, block)
_,,,^..^,,,_ (phone)





2015-11-20 8:31 GMT+01:00 Clément Bera <[hidden email]>:
You can use whileTrue: if it's a problem for you not to evaluate the limit at each iteration of the loop.

I don't need this. I just compared some compiled methods bytecodes after the latest fix for the wrong frame size calculation.
For this, I compared opal compilers result with that of the old compiler. And I noticed that some methods compiled with
opal had some "hidden" temps and couldn't explain way:)

 

If you look at OrderedCollection for example, there are implemented differently in Pharo/VW and Squeak. Pharo and VW use to:do: whereas Squeak uses whileTrue: to iterate over the collection. Hence, if you remove elements from the OrderedCollection while iterating over it, the behavior is different.


2015-11-20 1:34 GMT+01:00 Eliot Miranda <[hidden email]>:
Because were to:do: not inlined the expression passed as the to: actual argument would be evaluated exactly once (bound to the formal argument for to:) and so assigning to a temporary preserves the semantics in the most direct way.  If the expression has side effects then those side effects must occur only once.

Note that were the to: expression merely another variable then the compiler could only substitute the original variable if it's value could not change, e.g. in

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each].
    ^sum

It is ok to use count directly as the limit variable, but in this it is not:

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each. count := #somethingElseEntirely].
    ^sum

_,,,^..^,,,_ (phone)

> On Nov 19, 2015, at 1:14 PM, Nicolai Hess <[hidden email]> wrote:
>
> What is the purpose of replacing expressions, used as limits in a
> to:do: call with by a new temporary variable?
>
> For example:
>
> OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
> the code is
>
> exampleToDoArgumentLimitIsExpression
>     | count sum |
>     count := 10.
>     sum := 0.
>     1 to: count - 1 do: [ :each | sum := sum + each].
>     ^sum
>
> the decompiled bytecode :
>
> exampleToDoArgumentLimitIsExpression
>     | tmp1 tmp2 tmp3 |
>     tmp1 := 10.
>     tmp2 := 0.
>     tmp3 := tmp1 - 1.
>     1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
>     ^ tmp2
>
> So, the expression (count - 1) in the to:do: loop is replaced by a new
> temporay (tmp3).
>
> Ok, but why ?



Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Nicolai Hess-3-2


2015-11-20 9:58 GMT+01:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Nov 20, 2015, at 12:33 AM, Nicolai Hess <[hidden email]> wrote:

Thanks eliot, Clement,

but if the argument for to:do: is not an expression but one of the method argument, this
transformation is not needed. Or still?

That's the corollary to what I said.  Since method (*) arguments are read-only it is safe to use them directly and avoid the transformation.  One could implement this quickly in the Squeak compiler too :-).  How many cases have you identified where this matters?  Is it very common?

I don't know, but I don't think so.
Recompiling all ~90000 compiled methods, once with opal and once with the other compiler and comparing
the numbers of tempVars gives a difference of about 400 methods.
Not all 400 differences came from the extra tempvars for the to:do: transform, and sometimes opal generates code with less temp vars.

Anyway, adding extra tempvars for accessing the method arguments seems to be a bug.
 


(* and with the right preference, block)
_,,,^..^,,,_ (phone)





2015-11-20 8:31 GMT+01:00 Clément Bera <[hidden email]>:
You can use whileTrue: if it's a problem for you not to evaluate the limit at each iteration of the loop.

I don't need this. I just compared some compiled methods bytecodes after the latest fix for the wrong frame size calculation.
For this, I compared opal compilers result with that of the old compiler. And I noticed that some methods compiled with
opal had some "hidden" temps and couldn't explain way:)

 

If you look at OrderedCollection for example, there are implemented differently in Pharo/VW and Squeak. Pharo and VW use to:do: whereas Squeak uses whileTrue: to iterate over the collection. Hence, if you remove elements from the OrderedCollection while iterating over it, the behavior is different.


2015-11-20 1:34 GMT+01:00 Eliot Miranda <[hidden email]>:
Because were to:do: not inlined the expression passed as the to: actual argument would be evaluated exactly once (bound to the formal argument for to:) and so assigning to a temporary preserves the semantics in the most direct way.  If the expression has side effects then those side effects must occur only once.

Note that were the to: expression merely another variable then the compiler could only substitute the original variable if it's value could not change, e.g. in

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each].
    ^sum

It is ok to use count directly as the limit variable, but in this it is not:

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each. count := #somethingElseEntirely].
    ^sum

_,,,^..^,,,_ (phone)

> On Nov 19, 2015, at 1:14 PM, Nicolai Hess <[hidden email]> wrote:
>
> What is the purpose of replacing expressions, used as limits in a
> to:do: call with by a new temporary variable?
>
> For example:
>
> OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
> the code is
>
> exampleToDoArgumentLimitIsExpression
>     | count sum |
>     count := 10.
>     sum := 0.
>     1 to: count - 1 do: [ :each | sum := sum + each].
>     ^sum
>
> the decompiled bytecode :
>
> exampleToDoArgumentLimitIsExpression
>     | tmp1 tmp2 tmp3 |
>     tmp1 := 10.
>     tmp2 := 0.
>     tmp3 := tmp1 - 1.
>     1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
>     ^ tmp2
>
> So, the expression (count - 1) in the to:do: loop is replaced by a new
> temporay (tmp3).
>
> Ok, but why ?




Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Eliot Miranda-2
Hi Nicolai,

On Sat, Nov 21, 2015 at 10:40 AM, Nicolai Hess <[hidden email]> wrote:


2015-11-20 9:58 GMT+01:00 Eliot Miranda <[hidden email]>:
Hi Nicolai,

On Nov 20, 2015, at 12:33 AM, Nicolai Hess <[hidden email]> wrote:

Thanks eliot, Clement,

but if the argument for to:do: is not an expression but one of the method argument, this
transformation is not needed. Or still?

That's the corollary to what I said.  Since method (*) arguments are read-only it is safe to use them directly and avoid the transformation.  One could implement this quickly in the Squeak compiler too :-).  How many cases have you identified where this matters?  Is it very common?

I don't know, but I don't think so.
Recompiling all ~90000 compiled methods, once with opal and once with the other compiler and comparing
the numbers of tempVars gives a difference of about 400 methods.
Not all 400 differences came from the extra tempvars for the to:do: transform, and sometimes opal generates code with less temp vars.

Well it's a nice surprise to find out that the Squeak compiler does use a variable directly if it's not assigned to in the block.  Thanks to your observation I've simplified the test a little, not scanning for assignments if the variable is a method argument or a block argument and the assignment to block arg preference is not in effect.

Anyway, adding extra tempvars for accessing the method arguments seems to be a bug.

Agreed ;-)

(* and with the right preference, block)
_,,,^..^,,,_ (phone)





2015-11-20 8:31 GMT+01:00 Clément Bera <[hidden email]>:
You can use whileTrue: if it's a problem for you not to evaluate the limit at each iteration of the loop.

I don't need this. I just compared some compiled methods bytecodes after the latest fix for the wrong frame size calculation.
For this, I compared opal compilers result with that of the old compiler. And I noticed that some methods compiled with
opal had some "hidden" temps and couldn't explain way:)

 

If you look at OrderedCollection for example, there are implemented differently in Pharo/VW and Squeak. Pharo and VW use to:do: whereas Squeak uses whileTrue: to iterate over the collection. Hence, if you remove elements from the OrderedCollection while iterating over it, the behavior is different.


2015-11-20 1:34 GMT+01:00 Eliot Miranda <[hidden email]>:
Because were to:do: not inlined the expression passed as the to: actual argument would be evaluated exactly once (bound to the formal argument for to:) and so assigning to a temporary preserves the semantics in the most direct way.  If the expression has side effects then those side effects must occur only once.

Note that were the to: expression merely another variable then the compiler could only substitute the original variable if it's value could not change, e.g. in

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each].
    ^sum

It is ok to use count directly as the limit variable, but in this it is not:

exampleToDoArgumentLimitIsExpression
    | count sum |
    count := 10.
    sum := 0.
    1 to: count do: [ :each | sum := sum + each. count := #somethingElseEntirely].
    ^sum

_,,,^..^,,,_ (phone)

> On Nov 19, 2015, at 1:14 PM, Nicolai Hess <[hidden email]> wrote:
>
> What is the purpose of replacing expressions, used as limits in a
> to:do: call with by a new temporary variable?
>
> For example:
>
> OCOpalExamples>>#exampleToDoArgumentLimitIsExpression
> the code is
>
> exampleToDoArgumentLimitIsExpression
>     | count sum |
>     count := 10.
>     sum := 0.
>     1 to: count - 1 do: [ :each | sum := sum + each].
>     ^sum
>
> the decompiled bytecode :
>
> exampleToDoArgumentLimitIsExpression
>     | tmp1 tmp2 tmp3 |
>     tmp1 := 10.
>     tmp2 := 0.
>     tmp3 := tmp1 - 1.
>     1 to: tmp3 do: [ :tmp4 | tmp2 := tmp2 + tmp4 ].
>     ^ tmp2
>
> So, the expression (count - 1) in the to:do: loop is replaced by a new
> temporay (tmp3).
>
> Ok, but why ?







--
_,,,^..^,,,_
best, Eliot
Reply | Threaded
Open this post in threaded view
|

Re: opal optimize to do limits

Levente Uzonyi-2
In reply to this post by Clément Béra
On Fri, 20 Nov 2015, Clément Bera wrote:

> You can use whileTrue: if it's a problem for you not to evaluate the limit at each iteration of the loop.
> If you look at OrderedCollection for example, there are implemented differently in Pharo/VW and Squeak. Pharo and VW use to:do: whereas Squeak uses whileTrue: to iterate over the collection. Hence, if you remove elements from the OrderedCollection while iterating
> over it, the behavior is different.

One should refrain from modifying collections during iteration. In case of
OrderedCollection there's #removeAllSuchThat: to let one safely remove
elements from the collection while iterating over it.

Levente