Skip to content

internal/poll: use noescape or reuse iovec obj in Writev to avoid alloc mem in heap every time  #26663

Closed
@lintanghui

Description

@lintanghui

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.10.3 linux/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOOS="linux"

What did you do?

I use writev to write buffer into tcpconn. in order to reduce gc, I try to reuse object as far as possible.
and i did it so. but when i run go tool pprof to analysis of the memory。I found Writev alloc memory every time is called.

0: 0 [2990: 95680] @ 0x49225f 0x58df7b 0x58dd76 0x57ebe7 0x77232f 0x773aad 0x784329 0x79d09b 0x7a0958 0x45a851
#	0x49225e	internal/poll.(*FD).Writev+0xde				/usr/local/Cellar/go/1.10/libexec/src/internal/poll/writev.go:25
#	0x58df7a	net.(*netFD).writeBuffers+0x3a				/usr/local/Cellar/go/1.10/libexec/src/net/writev_unix.go:26
#	0x58dd75	net.(*conn).writeBuffers+0x55				/usr/local/Cellar/go/1.10/libexec/src/net/writev_unix.go:18
#	0x57ebe6	net.(*Buffers).WriteTo+0x1a6				/usr/local/Cellar/go/1.10/libexec/src/net/net.go:638
#	0x77232e	overlord/lib/net.(*Conn).Writev+0x6e			/work/go/src/overlord/lib/net/conn.go:115
#	0x773aac	overlord/lib/bufio.(*Writer).Flush+0x8c			/work/go/src/overlord/lib/bufio/io.go:174

1: 256 [1: 256] @ 0x492616 0x58df7b 0x58dd76 0x57ebe7 0x77232f 0x773aad 0x784329 0x79d09b 0x7a0958 0x45a851
#	0x492615	internal/poll.(*FD).Writev+0x495			/usr/local/Cellar/go/1.10/libexec/src/internal/poll/writev.go:42
#	0x58df7a	net.(*netFD).writeBuffers+0x3a				/usr/local/Cellar/go/1.10/libexec/src/net/writev_unix.go:26
#	0x58dd75	net.(*conn).writeBuffers+0x55				/usr/local/Cellar/go/1.10/libexec/src/net/writev_unix.go:18
#	0x57ebe6	net.(*Buffers).WriteTo+0x1a6				/usr/local/Cellar/go/1.10/libexec/src/net/net.go:638
#	0x77232e	overlord/lib/net.(*Conn).Writev+0x6e			/work/go/src/overlord/lib/net/conn.go:115
#	0x773aac	overlord/lib/bufio.(*Writer).Flush+0x8c			/work/go/src/overlord/lib/bufio/io.go:174

and the go tool pprof show:

 $go tool pprof -alloc_space  http://127.0.0.1:2110/debug/pprof/heap
(pprof) cum
(pprof) top
Showing nodes accounting for 303.01MB, 91.27% of 331.99MB total
Dropped 51 nodes (cum <= 1.66MB)
Showing top 10 nodes out of 24
      flat  flat%   sum%        cum   cum%
  302.51MB 91.12% 91.12%   302.51MB 91.12%  internal/poll.(*FD).Writev
         0     0% 91.12%   302.51MB 91.12%  net.(*Buffers).WriteTo
         0     0% 91.12%   302.51MB 91.12%  net.(*conn).writeBuffers
         0     0% 91.12%   302.51MB 91.12%  net.(*netFD).writeBuffers
         0     0% 91.12%   302.51MB 91.12%  overlord/lib/bufio.(*Writer).Flush
         0     0% 91.12%   302.51MB 91.12%  overlord/lib/net.(*Conn).Writev
         0     0% 91.12%   170.02MB 51.21%  overlord/proxy.(*Handler).handle

as pprof showed. almost all memory was alloced by Writev

analysis of the object escape

./writev.go:16:43: leaking param: fd
./writev.go:16:43: 	from fd (passed to call[argument escapes]) at ./writev.go:17:24
./writev.go:42:48: &chunk[0] escapes to heap
./writev.go:42:48: 	from syscall.Iovec literal (struct literal element) at ./writev.go:42:41
./writev.go:42:48: 	from append(iovecs, syscall.Iovec literal) (appended to slice) at ./writev.go:42:19
./writev.go:16:43: leaking param content: v
./writev.go:16:43: 	from *v (indirection) at ./writev.go:38:25
./writev.go:16:43: 	from * (*v) (indirection) at ./writev.go:38:25
./writev.go:16:43: 	from chunk (range-deref) at ./writev.go:38:19
./writev.go:16:43: 	from chunk[0] (dot of pointer) at ./writev.go:42:54
./writev.go:16:43: 	from &chunk[0] (address-of) at ./writev.go:42:48
./writev.go:16:43: 	from syscall.Iovec literal (struct literal element) at ./writev.go:42:41
./writev.go:16:43: 	from append(iovecs, syscall.Iovec literal) (appended to slice) at ./writev.go:42:19
./writev.go:55:15: &iovecs escapes to heap
./writev.go:55:15: 	from fd.iovecs (star-dot-equals) at ./writev.go:55:13
./writev.go:25:6: moved to heap: iovecs
./writev.go:72:8: syscall.Errno(e0) escapes to heap
./writev.go:72:8: 	from err (assigned) at ./writev.go:72:8
./writev.go:72:8: 	from ~r2 (return) at ./writev.go:82:2
./writev.go:21:17: (*FD).Writev fd.pd does not escape
./writev.go:44:26: (*FD).Writev iovecs[len(iovecs) - 1] does not escape
./writev.go:47:25: (*FD).Writev iovecs[len(iovecs) - 1] does not escape
./writev.go:59:27: (*FD).Writev &iovecs[0] does not escape
./writev.go:68:18: (*FD).Writev fd.pd does not escape

At line42 it show &chunk[0] is escapes to heap.and in pprof it alloc mem every time.
it is that possilbe to make noescape to avoid obj alloc in heap or reuse these obj to avoid gc?

What did you expect to see?

the iovec object should be alloced in stack in oder to avoid gc, but it escaped to heap.

What did you see instead?

iovec was escape to heap and alloc mem every time and made gc frequently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Performance

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions