Runtime
Goroutine ID
GID provides an extremely fast (1.2 ns) method to get the ID of the current goroutine.
More precisely speaking, it’s the address of the current g (i.e. goroutine). Therefore, the ID makes sense only during the life cycle of the current goroutine.
It’s implemented by using Go Assembly.
Supported CPU architectures: 386, amd64, arm, arm64, mips, mipsle, mips64, mips64le, ppc64, ppc64le, riscv64, s390x.
Why
Normally you don’t need to get the ID of the current goroutine, unless you’re implementing a goroutine local storage.
Use
Benchmark
Goroutine Local Storage
GLS provides an extremely fast Goroutine Local Storage.
Why
Normally you don’t need goroutine local storage, because Context is better.
Unless you want to implement distributed tracing (or serverless, or other context-passing cases) at minimal cost for a legacy codebase where Context is not used widely or even not used at all.
Key Design
Key points to design a goroutine local storage:
- How to get the ID of the current goroutine? This is vital to relating goroutines and goroutine local storages. And this is vital to performance.
- What to use as goroutine local storage? This is vital to performance.
- How to detect goroutine creation? This is vital to creating goroutine local storage.
- How to detect goroutine exit? This is vital to removing goroutine local storage instantly and therefore minimizing memory footprint.
- Minimizing cost to apply goroutine local storage in a legacy codebase.
As follows are my choices:
- Use Go Assembly to get the address of the current g (i.e. goroutine), and use it as the ID of the current goroutine. It’s extremely fast. See Goroutine ID → .
- Use std Value Context (context.WithValue) as goroutine local storage. In context-passing cases such as distributed tracing, serverless, etc, a goroutine local storage is copied mostly, read mostly, and written rarely.
Value Context is pointer, and therefore can be copied almost costlessly.
Value Context is immutable because it provides no API to modify itself. If you want to “modify” a Value Context, you can always create another based on the current one. (It feels like immutability in functional programming.) Therefore Value Context is concurrency-safe by nature.
In conclusion, Value Context is perfect for goroutine local storage. - Use a custom function
Go
instead of the keywordgo
to spawn a goroutine. Therefore we know exactly when a goroutine is created or exits. - See 3.
- Use Go AST to create a code generator which scans a codebase and replaces all keyword
go
with functionGo
. See G2G →
Use
Benchmark
G2G
G2G is a code generator which scans a codebase and replaces all keyword go
with function Go
.
It also takes care of the closure problem of for/range.
It’s implemented by using Go AST.