Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
288 views
in Technique[技术] by (71.8m points)

go - Mix print and fmt.Println and stack growing

I'm watching lectures about Go. One about stack growing with this example:

package main

import "fmt"

const size = 1024

func main() {
    fmt.Println("Start")
    s:= "HELLO"
    stackCopy(&s, 0, [size]int{})
}

func stackCopy(s *string, c int, a [size]int){
    println("println: ", s, *s)
    //fmt.Println("fmt:     ", s, *s)
    c++
    if c == 10 {
    return
    }
    stackCopy(s, c, a)
}

When use only println, address of "s" change (stack is growing and data move to other place):

Start
println:  0xc000107f58 HELLO
println:  0xc000107f58 HELLO
println:  0xc000117f58 HELLO
println:  0xc000117f58 HELLO
println:  0xc000117f58 HELLO
println:  0xc000117f58 HELLO
println:  0xc00019ff58 HELLO
println:  0xc00019ff58 HELLO
println:  0xc00019ff58 HELLO
println:  0xc00019ff58 HELLO

When I mix println and fmt.Println or only fmt.Println adress of "s" not change:

Start
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO
println:  0xc00010a040 HELLO
fmt:      0xc00010a040 HELLO

Why this happen?

question from:https://stackoverflow.com/questions/65874745/mix-print-and-fmt-println-and-stack-growing

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The difference is on using the builtin println() function and the fmt.Println() function from the standard lib.

println() is a builtin function and the compiler knows println() does not retain any arguments passed to it, so arguments passed to it does not escape to heap.

fmt.Prinln() is from the standard lib and it is handled as any of your custom functions: the compiler makes no assumptions that it does not retain passed arguments, so arguments you pass to it may escape to heap, and so they are allocated on the heap and not on the stack.

This is an important difference. Next what causes the deviation is that Go has dynamic stack: it starts small, and if needed, it can grow. For details, see Does Go have an "infinite call stack" equivalent? Since you pass a considerably large argument to the recursive function stackCopy() (array of size 1024, element type of int which is 4 or 8 bytes), the initial small stack will be insufficient, and a larger stack will be allocated, causing stack-allocated variables to be moved, hence their address changing. This won't happen when fmt.Println() is used, because the compiler deduces s might escape, so it's allocated on the heap, and growing the stack does not results in moving s.

You may use the -gcflags '-m' flags to print the compiler's escape analysis.

In the first case:

./play.go:8:13: inlining call to fmt.Println
./play.go:13:16: s does not escape
./play.go:8:14: "Start" escapes to heap
./play.go:8:13: []interface {} literal does not escape
<autogenerated>:1: .this does not escape

What matters here is: s does not escape.

In the second case:

./play.go:15:13: inlining call to fmt.Println
./play.go:8:13: inlining call to fmt.Println
./play.go:13:16: leaking param: s
./play.go:15:14: "fmt:     " escapes to heap
./play.go:15:30: *s escapes to heap
./play.go:15:13: []interface {} literal does not escape
./play.go:9:2: moved to heap: s
./play.go:8:14: "Start" escapes to heap
./play.go:8:13: []interface {} literal does not escape
<autogenerated>:1: .this does not escape

As you can see, the compiler deduces that s is a leaking param, *s escapes to heap.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...