Example run:
% go1.22.6 run .
29764202
% go version
go version go1.23.1 darwin/arm64
% go run .
3001476733
That's an over 100x increase.
| package main | |
| import ( | |
| "fmt" | |
| "math/rand" | |
| "runtime" | |
| "runtime/metrics" | |
| ) | |
| //go:noinline | |
| func garbage() []byte { | |
| //return make([]byte, rand.Intn(32)) | |
| return make([]byte, 4) | |
| } | |
| //go:noinline | |
| func left(depth int) { | |
| if depth == 0 { | |
| garbage() | |
| return | |
| } | |
| if depth < 16 { | |
| // Keep the leaf end of the stack fixed to blow up the difference | |
| // with having deeper stacks capture more recursion further up. | |
| left(depth - 1) | |
| return | |
| } | |
| if rand.Intn(2) == 0 { | |
| left(depth - 1) | |
| } else { | |
| right(depth - 1) | |
| } | |
| } | |
| //go:noinline | |
| func right(depth int) { | |
| if depth == 0 { | |
| garbage() | |
| return | |
| } | |
| if depth < 16 { | |
| right(depth - 1) | |
| return | |
| } | |
| if rand.Intn(2) == 0 { | |
| left(depth - 1) | |
| } else { | |
| right(depth - 1) | |
| } | |
| } | |
| func main() { | |
| runtime.MemProfileRate = 1 | |
| defer func() { | |
| s := make([]metrics.Sample, 1) | |
| s[0].Name = "/memory/classes/profiling/buckets:bytes" | |
| metrics.Read(s) | |
| fmt.Printf("%v\n", s[0].Value.Uint64()) | |
| // "Read" the profile so profiling doesn't get stripped. | |
| // These profiles get big so actually serializing them is really slow. | |
| runtime.MemProfile(nil, false) | |
| }() | |
| for range 10000000 { | |
| depth := 128 | |
| if rand.Intn(2) == 0 { | |
| left(depth) | |
| } else { | |
| right(depth) | |
| } | |
| } | |
| } |