Go-ing Easy on Memory
Writing GC-friendly code
Senior SW Eng. @Platform.sh
👋 Hi!
I’m Sümer Cip.
A software engineer and a full-time dad of 3.
I contribute to Open Source as much as I can.
I’m interested in observability, distributed systems, and databases.
sumercip.com
Motivation
Real-world data
“Sometimes I joke about avoiding dynamic memory management essentially being the the only thing that matters for low latency…”
Real-world data
Bryan Boreham, FOSDEM 2018, Make your Go Faster!
Real-world data
Andrey Sibiryov, SreCON17 Asia/Australia: Golang's Garbage
Real-world data
~%50
~%25
CPU Profile Flamegraph
Real-world data
https://www.datadoghq.com/blog/engineering/timeseries-indexing-at-scale/
from Datadog’s Blog:
Real-world data
GC-friendly libraries
Real-world data
Other languages
Understanding Memory
Basics
Stack & Heap
Basics
Stack & Heap
Understanding GC
Basics
How GC works?
Basics
How GC works?
Basics
How GC works?
Basics
How GC works?
Basics
GC Unpredictability
Before GC
After GC
Wrap-up
Let’s keep GC Happy
“Reuse, Reduce, Recycle.”
Reduce
Reduce
https://sumercip.com/posts/making-python-fitter-and-faster/#2-better-memory-management
Reduce
- type Reader interface {
- Read(n int) ([]byte, err error)
- }
+ type Reader interface {
+ Read(p []byte) (n int, err error)
+}
Stack vs Heap
GopherCon SG 2019 - Understanding Allocations: the Stack and the Heap - Jakob Walker
https://www.youtube.com/watch?v=ZMZpH4yT7M0
Reduce
func doStuff() func() {
bigArray := make([]int, 1_000)
return func() {
// do stuff with bigArray
}
}
Stack vs Heap
Reduce
interface{} and generics
Reduce
Avoid Pointers
Reduce
Avoid Pointers
type Time struct {
wall uint64
ext int64
loc *Location
}
type StringHeader struct {
Data uintptr
Len int
}
Reduce
type TenantKey struct {
tenantID int
regionID int
}
tenantKey = TenantKey{1, 2}
- myMap[tenantKeyStr] = value
+ myMap[tenantKey] = value
Avoid Pointers
Reduce
type MyStruct struct {
- a [17]int64 // >128 bytes
+ a [16]int64 // <=128 bytes
}
m := make(map[int]MyStruct)
Avoid Pointers
Reduce
type MyStruct struct {
A string // 16 bytes
B string // 16 bytes
C string // 16 bytes
D string // 16 bytes
}
- func DoStuff(a *MyStruct) {}
+ func DoStuff(a MyStruct) {}
Avoid Pointers
Reduce
Avoid Pointers
type MyNode struct {
- value int
- next *MyNode
}
+ values []int
Reduce
type Small struct {}
type Big struct {
...
smallStruct Small
...
}
- cache(&bigStruct.smallStruct)
+ cache(bigStruct.smallStruct)
Avoid Pointers
Reduce
Avoid Pointers
Reuse
Reuse
var pool = sync.Pool{
New: func() interface{} {
return new(MyStruct)
},
}
func processData(data []byte) {
buf := pool.Get().(*MyStruct)
defer pool.Put(buf)
// use buffer
}
sync.Pool
Reuse
var pool = sync.Pool{
New: func() interface{} {
- return []byte{}
+ return new([]byte)
},
}
func processData(data []byte) {
- buf := pool.Get().([]byte)
+ buf := pool.Get().(*[]byte)
...
pool.Put(buf)
}
sync.Pool
Reuse
sync.Pool
Reuse
a := []int{1, 2, 3, 4, 5}
- a = append(a, 10, 20, 30)
+ a = append(a[:0], 10, 20, 30)
- largeMap := make(map[int]string)
+ largeMap := make(
map[int]string, 1000)
- s = s + “hi!”
+ builder.WriteString(“hi”)
Recycle
Recycle
Tune GC
Recycle
Tune GC
Tools Overview
Tools Overview
Profiling Memory
Tools Overview
Profiling
Bryan Boreham, FOSDEM 2018, Make your Go Faster!
Tools Overview
Escape Analysis
Tools Overview
Execution tracer
https://go.dev/blog/execution-traces-2024
Tools Overview
Execution tracer
Tools Overview
GODEBUG=gctrace=1
Wrap-up
Wrap-up
Bonus
Bonus
Memory regions (Nov 8, 2024)
Bonus
Memory regions
Thanks!
sumercip.com
🎊