go-tarantool
go-tarantool copied to clipboard
v3: minimize allocations
Now we do a lot of allocations per a request:
$ go test -bench BenchmarkClientSerialTyped -benchmem
BenchmarkClientSerialTyped-12 12250 94545 ns/op 898 B/op 16 allocs/op
There is no need for 0, but with 80-20 rule we could have a soft goal 8 allocs per a request.
Right now output is different (with 15 allocs):
$ go test -bench BenchmarkClientSerialTyped -benchmem
BenchmarkClientSerialTyped-8 13958 77821 ns/op 920 B/op 15 allocs/op
Here are all allocations:
- In https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/future.go#L120-L127
./future.go:122:8: &Future{} escapes to heap
./future.go:125:19: make([]*Response, 0) escapes to heap
We create pointers to the Future and an array of pointers. They are stored in heap.
- In https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/request.go#L862-L874
./request.go:864:12: new(SelectRequest) escapes to heap
./request.go:870:25: []interface {}{} escapes to heap
We allocate memory for the SelectRequest.
We actually can just not initialize key field and reduce total allocations by 1.
- In https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L1244-L1267
./connection.go:1263:17: make([]byte, length) escapes to heap
- In https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L878-L932
./connection.go:894:11: &Response{...} escapes to heap
Creating a pointer to a Response. Later, this pointer might be used in the Future-s AppendPush and SetResponse methods. They accept a pointer and change the value of the corresponding structure.
- In https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/response.go#L278-L337
./response.go:288:19: func literal escapes to heap
Some other allocations happens inside of msgpack package:
- In https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode.go#L84-L88
./decode.go:85:10: new(Decoder) escapes to heap
- In https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode.go#L617-L624
./decode.go:624:12: make([]byte, 0, 64) escapes to heap
- In https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode_string.go#L54-L60
./decode_string.go:59:16: string(b) escapes to heap
- In https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode_map.go#L72-L88
./decode_map.go:87:31: unexpectedCodeError{...} escapes to heap
Rest of the allocations (4) I could not find, perhaps they are somewhere deep inside of the msgpack calls.
Or there might be some allocations tied to use of interface{} as function arguments (Values stored in interfaces might be arbitrarily large, but only one word is dedicated to holding the value in the interface structure, so the assignment allocates a chunk of memory on the heap and records the pointer in the one-word slot. from https://research.swtch.com/interfaces). And we use interfaces a lot in the functions. But this is not detected by the go build -gcflags='-m=3' output.
You could also try to run test with memory profiler. See -memprofile option to ensure that rest of allocation belongs to msgpack:
https://habr.com/en/companies/badoo/articles/301990/
Updated list of all 15 allocations:
-
2 allocations for creating a channel here https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L879
-
1 allocation while passing an argument to a goroutine function https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L882
-
1 allocation when creating a pointer to a
Responsestruct https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L894 -
2 allocations while calling
NewFuturehttps://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L1284 https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/future.go#L120-L127
./future.go:122:8: &Future{} escapes to heap
./future.go:125:19: make([]*Response, 0) escapes to heap
- 2 allocations while calling
NewSelectRequesthttps://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/request.go#L862-L874
./request.go:864:12: new(SelectRequest) escapes to heap
./request.go:870:25: []interface {}{} escapes to heap
- 1 allocation in In https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L1244-L1267
./connection.go:1263:17: make([]byte, length) escapes to heap
- 2 allocations: 1 for creating a
[]bytearray and 1 for creating a pointer toResponsestructure: https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L1102 https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L1139
./connection.go:1102:21: make([]byte, 0, 128) escapes to heap
./connection.go:1139:12: &Response{...} escapes to heap:
Some other allocations happens inside of msgpack package:
- 1 allocation in https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode.go#L84-L88
./decode.go:85:10: new(Decoder) escapes to heap
- 1 allocation in https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode.go#L617-L624
./decode.go:624:12: make([]byte, 0, 64) escapes to heap
- 1 allocation in https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode_string.go#L54-L60
./decode_string.go:59:16: string(b) escapes to heap
- 1 allocation in https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode_map.go#L72-L88
./decode_map.go:87:31: unexpectedCodeError{...} escapes to heap
15 allocations total.
Updated list of all 15 allocations:
1. 2 allocations for creating a channel here https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L879 2. 1 allocation while passing an argument to a goroutine function https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L882
It does not look like a per request allocations. But let stop here. Thank you!