go-tarantool icon indicating copy to clipboard operation
go-tarantool copied to clipboard

v3: minimize allocations

Open oleg-jukovec opened this issue 2 years ago • 5 comments

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.

oleg-jukovec avatar Apr 18 '23 13:04 oleg-jukovec

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:

  1. 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.

  1. 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.

  1. In https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L1244-L1267
./connection.go:1263:17: make([]byte, length) escapes to heap
  1. 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.

  1. 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:

  1. In https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode.go#L84-L88
./decode.go:85:10: new(Decoder) escapes to heap
  1. In https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode.go#L617-L624
./decode.go:624:12: make([]byte, 0, 64) escapes to heap
  1. In https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode_string.go#L54-L60
./decode_string.go:59:16: string(b) escapes to heap
  1. 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.

DerekBum avatar Sep 14 '23 10:09 DerekBum

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/

oleg-jukovec avatar Sep 14 '23 17:09 oleg-jukovec

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

  3. 1 allocation when creating a pointer to a Response struct https://github.com/tarantool/go-tarantool/blob/d8df65dcd0f29a6a5d07472115cbcf2753b12609/connection.go#L894

  4. 2 allocations while calling NewFuture https://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
  1. 2 allocations while calling NewSelectRequest 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
  1. 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
  1. 2 allocations: 1 for creating a []byte array and 1 for creating a pointer to Response structure: 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. 1 allocation in https://github.com/vmihailenco/msgpack/blob/0ac2a568aee92ef815c8d3e06b3fb3e2f79c18f6/decode.go#L84-L88
./decode.go:85:10: new(Decoder) escapes to heap
  1. 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. 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. 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.

DerekBum avatar Sep 20 '23 16:09 DerekBum

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!

oleg-jukovec avatar Sep 21 '23 08:09 oleg-jukovec