Evaluation order of return

瀏覽次數:248 次
跳到第一則未讀訊息

cs.ali...@gmail.com

未讀,
2020年9月21日 中午12:09:162020/9/21
收件者:golang-nuts
Why does the compiler change evaluation order of return when adding a new field to a struct?  I didn't check the compiler output, I only guess it somehow changes the order. 
play
package main
import (
"fmt"
)
type Number interface {
Value() int
}
type Integer struct {
value int
additional int // remove this for different result
}
func (integer Integer) Value() int {
return integer.value
}
func main() {
number, _ := getNumber()
fmt.Printf("number val: %d\n", number.Value())
}
func getNumber() (Number, error) {
var intNumber Integer
return intNumber, setInteger(&intNumber)
}
func setInteger(num *Integer) error {
num.value = 2
return nil
}

Axel Wagner

未讀,
2020年9月21日 中午12:34:232020/9/21
收件者:cs.ali...@gmail.com、golang-nuts
The evaluation order is defined here:
The important part is that the order of evaluation in a return statement is only defined for function calls, method calls and communication statements, but not in relation to other operations. So, in
return intNumber, setInteger(&intNumber)
It is not defined whether `intNumber` or `setInteger(&intNumber)` is evaluated first, as the former is none of those. I can't really tell you why, but it has been discussed a couple of times, you might find something using the search function on golang-nuts.

If you want reliable behavior, you should assign the result first
var intNumber Integer
err := setInteger(&intNumber)
return intNumber, err


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/b7077f03-b0f4-454a-a587-ab9757164409n%40googlegroups.com.

Ian Lance Taylor

未讀,
2020年9月21日 下午1:06:312020/9/21
收件者:Axel Wagner、cs.ali...@gmail.com、golang-nuts
On Mon, Sep 21, 2020 at 9:34 AM 'Axel Wagner' via golang-nuts
<golan...@googlegroups.com> wrote:
>
> The evaluation order is defined here:
> https://golang.org/ref/spec#Order_of_evaluation
> The important part is that the order of evaluation in a return statement is only defined for function calls, method calls and communication statements, but not in relation to other operations. So, in
> return intNumber, setInteger(&intNumber)
> It is not defined whether `intNumber` or `setInteger(&intNumber)` is evaluated first, as the former is none of those. I can't really tell you why, but it has been discussed a couple of times, you might find something using the search function on golang-nuts.
>
> If you want reliable behavior, you should assign the result first
> var intNumber Integer
> err := setInteger(&intNumber)
> return intNumber, err

This flexibility is in the language to permit better code
optimization. The compiler can choose how to order memory loads and
function calls. It is not required to do a memory load, save it
somewhere, and then do the function call.

Of course, this does have the downside that different compilers will
produce different behavior for the same code. So far we've decided
that that is OK.

My personal attitude is if a single statement writes to a variable
other than by assigning to it directly, and the statement also reads
from that same variable, then the program is already confusing. While
we could lock down a specific order of evaluation, that won't help the
fact that the program is hard to read and hard to understand. So I
don't see a good argument for forcing many well written programs to
run very slightly slower in order to make poorly written programs
behave consistently. But I understand that other people feel
differently.

Ian
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHsSFfjPzoOTVz7Q4fE%3DziCXhA5mbUWzSkQ5DADMyzWFw%40mail.gmail.com.

cs.ali...@gmail.com

未讀,
2020年9月23日 凌晨4:09:492020/9/23
收件者:golang-nuts
Thanks Axel, Ian!

I am not actually questioning the current design, as you both said it is not a good practice to call a return statement as I wrote above, I am trying to understand the relation between memory, interface and order of evaluation. It is clear that the compiler takes account of whether a return statement is an interface or a struct and  the memory size of the returned value, If I return a struct rather than an interface, it changes the order, If I add fields to the structs it changes the order. Is there a paper that I can find why the compiler considers them for ordering, why it is important for performance or anything else? 

Jesper Louis Andersen

未讀,
2020年9月23日 清晨5:44:202020/9/23
收件者:cs.ali...@gmail.com、golang-nuts
On Wed, Sep 23, 2020 at 10:09 AM cs.ali...@gmail.com <cs.ali...@gmail.com> wrote:
Is there a paper that I can find why the compiler considers them for ordering, why it is important for performance or anything else? 


A CPU has a limit to how many load/store instructions it can issue in a given clock cycle or in a given window of clock cycles. By reordering memory loads, you can spread them out such that they don't create conflicts against each other. This covers the part of memory ordering optimizations. As for function calls, you often have a call convention where some registers are saved by the caller and some registers are saved by the callee. Hence it can be beneficial to arrange the evaluation order in a way to avoid having to save registers. I think Go's current ABI rules are to use the stack for everything in its calling convention, but the same general rules apply: you may want to reorder the work in order to save on register pressure. It becomes especially interesting if your calling convention allows for multiple return values passed in registers.

Note that https://go.googlesource.com/proposal/+/refs/changes/78/248178/1/design/40724-register-calling.md proposes a register-based calling convention for the Go compiler, which hammers through the need of flexibility in this area. 

--
J.

Ian Lance Taylor

未讀,
2020年9月23日 晚上8:28:102020/9/23
收件者:cs.ali...@gmail.com、golang-nuts
On Wed, Sep 23, 2020 at 1:10 AM cs.ali...@gmail.com
<cs.ali...@gmail.com> wrote:
>
> I am not actually questioning the current design, as you both said it is not a good practice to call a return statement as I wrote above, I am trying to understand the relation between memory, interface and order of evaluation. It is clear that the compiler takes account of whether a return statement is an interface or a struct and the memory size of the returned value, If I return a struct rather than an interface, it changes the order, If I add fields to the structs it changes the order. Is there a paper that I can find why the compiler considers them for ordering, why it is important for performance or anything else?

I'm not aware of any paper specific to the Go compiler.

I find it most useful to consider a compiler as creating a set of
constraints derived from the input based on the language semantics.
These are constraints like in "a = b; b = c" the read of b in the
first assignment must be completed before the store to b in the second
assignment. Once the set of constraints is created, the compiler must
solve those constraints given an instruction architecture including a
set of registers. The goal is to optimize execution time while
minimizing compilation time without violating any constraints.
Because compilation time matters, compilers do not fully analyze all
possible solutions; instead, when it comes to things like memory
load/store order, instruction selection, and register allocation, they
are full of heuristics that tend to give good results in practice.

When you view a compiler in that way, a question like "why does adding
fields to a struct change the order of memory loads and stores"
becomes uninteresting. The reason has to do with the details of all
the constraints that applied while compiling that particular package.
There is no rule that says "if the struct has more fields, do this."
It's just that the set of heuristics happened to produce a particular
result. Changing some other piece of code in some other part of the
package might produce a different result. Or a different version of
the compiler might apply different heuristics and get different
results.

Ian

cs.ali...@gmail.com

未讀,
2020年9月25日 凌晨1:35:512020/9/25
收件者:golang-nuts
I understood perfectly now, thanks for the explanations and the link! I really appreciate you guys!

Michael Jones

未讀,
2020年9月25日 下午1:59:412020/9/25
收件者:cs.ali...@gmail.com、golang-nuts
Another way to understand the general topic is by comparison to Lindenmayer systems. The compiler is an iterated rewrite framework and the exact code that you get can be a surprise the same way L-System graphics vary. 

--


You received this message because you are subscribed to the Google Groups "golang-nuts" group.


To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.


--
Michael T. Jones
michae...@gmail.com
回覆所有人
回覆作者
轉寄
0 則新訊息