Skip to content

Go R1 Day 76

progress🔗

Lots of more technical detail on Memory & Data Semantics Section.

I value this as my background in databases means that things like Stack/Heap weren't part of my education as much. This has been useful as there are new concepts, paradigms, and ways of thinking and it's providing a lot more foundational concepts for me to better understanding the system and compiler behavior in Go.

Ultimate Go: Pointers-Part-3: Escape Analysis🔗

Notes🔗

  • Stacks are self-cleaning
  • Compiler knows what goes on stack and what goes on heap at compile time. Knowing where the value is constructed is part of the static analysis the compiler performs.
  • Bill said this is a really powerful feature that will probably impact future languages.
  • When you see a pointer in the return value, this could be phrased "shared up the callstack".
  • This means that the construction inside the function would be on the heap not the stack.
  • If you are trying to access items further up on the stack, it requires pointers.
  • Bill said what's cool about this is the ability to access an item on the heap via this pointer, as if we were working with pointers to a stack value. It abstracts the machine level details away so we benefit from easy out of frame access to values with pointers.
  • We don't have to worry about where the location of the value being constructed is. Due to escape analysis, the compiler will determine the best place, but the reality is some items have to go to the heap, and the compiler is smart enough to help determine this.
  • Why does this matter? Bill says that if performance matters, understanding the concepts matter because garbage collection and other latencies are impacted by these concepts.
  • Can view escape analysis from compiler with: go build -gcflags -m=2.
    • I tested this on one of my projects, and found that function complexity can prevent inlining, lots of escape to heap references and more.
    • While not needed for basic build automation type helpers, I could see the value in scalable design for Go in examining this in more details. It's almost like using SQL Server execution plans to optimize for higher performance.

Takeaways🔗

Don't hide the cost🔗

If we are doing construction to a variable, we use value construction. Avoid pointer semantic construction if not in the return.

Example:

// clear visible cost of the allocation by value construction and passing of pointer back up the call stack
func createSomething() *something {
  u := something{
    name: "example",
  }
  return &u // <--- This makes clear the cost and allocation back up the callstack.
}
// cost is obscured by construction being a pointer
// and returning a value that is not clear to reader if value or pointer
func createSomething()*something {
  u := &something{
    name: "example",
  }
  return u // <--- Not good. Hides the cost, and require reading function further to find that this is a pointer.
}

Making cost obvious and visible is a big priority for readable maintainable code with a team.