But GenerateRndPtr and GenerateRndVal generate the same amount of information!
Compile-time escape analysis
Priority #1: try to allocate new objects in the stack
Compile-time escape analysis
Priority #1: try to allocate new objects in the stack
#2: allocate in the heap the values that "escape" the stack
What do "to escape" means?
func a() *Obj {
r := Obj{}
// ... do something
return &r;
}
func b() {
o := a()
// ... do something
}
What do "to escape" means?
Stack
func a() *Obj {
r := Obj{} +----------+
// ... do something b() | o:*Obj |
return &r; +----------+
}
func b() {
o := a()
// ... do something
}
What do "to escape" means?
Stack
func a() *Obj {
r := Obj{} +----------+
// ... do something b() | o:*Obj |
return &r; +----------+
} a() | r:Obj |
func b() { +----------+
o := a()
// ... do something
}
What do "to escape" means?
Stack
func a() *Obj {
r := Obj{} +----------+
// ... do something b() | o:*Obj -----+
return &r; +----------| |
} a() | r:Obj <-----+
func b() { +----------+
o := a()
// ... do something
}
What do "to escape" means?
Stack
func a() *Obj {
r := Obj{} +----------+
// ... do something b() | o:*Obj -----+
return &r; +----------+ |
} <-----+
func b() {
o := a()
// ... do something
}
Unsafe memory access!!
Object creation is escaped to the heap
Stack Heap
func a() *Obj {
r := Obj{} +----------+ +----------+
// ... do something b() | o:*Obj ------------> r:Obj |
return &r; +----------+ +----------+
}
func b() {
o := a()
// ... do something
}
Getting insights from the go compiler
Add -gcflags="-m" to the go build, go test or go run commands.
5: funca() *Obj { | ./dummy.go:5:6: can inline a
6: r := Obj{} | ./dummy.go:10:8: inlining call to a
7: return &r | ./dummy.go:7:9: &r escapes to heap
8: } | ./dummy.go:6:2: moved to heap: r
9: funcb() { | ./dummy.go:11:13: o escapes to heap
10: o := a() | ./dummy.go:10:8: &r escapes to heap
11: fmt.Printf("%#v\n", o) | ./dummy.go:10:8: moved to heap: r
12: } | ./dummy.go:11:12: b ... argument
13: funcmain() { | does not escape
14: b()
15: }
Getting insights from the go compiler
Using -gcflags="-m" to improve the efficiency of our code.
5: funca()Obj { | ./dumm2.go:5:6: can inline a
6: r := Obj{} | ./dumm2.go:12:8: inlining call to a
7: return r | ./dumm2.go:11:13: o escapes to heap
8: } | ./dumm2.go:11:12: b ... argument
9: funcb() { | does not escape
10: o := a()
11: fmt.Printf("%#v\n", o)
12: }
13: funcmain() {
14: b()
15: }
Compile-time escape analysis
Priority #1: try to allocate new objects in the stack
#2: allocate in the heap the values that "escape" the stack
Even if they don't actually escape, they will be allocated in the heap if the compiler is not 100% sure
Conclusions
#1: putting some light on a common misconception for memory-managed languages
"Moving pointers is faster than moving structs"
#1: putting some light on a common misconception for memory-managed languages
"Moving pointers is faster than moving structs"
Yes, but... t(heap_alloc) >> t(cache_line_copy)
#2: Don't try premature optimizations
Readability first
Compiler optimizations may disable your "optimizations"
Internal implementation details may disable compiler optimizations: log.Debug(value)
#3: walktrhough by some nice Go performance tools
-benchmem to also benchmark memory generation
-cpuprofile (and -memprofile) to trace garbage collection (amongst others)
pprof to see which parts of the code are the most time/memory consuming
-gcflags="-m" to get insights about compiler optimization decisions