From adf696172dfe6bbf036f93fa9e928252f50da68a Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 18 Aug 2021 14:30:14 -0700 Subject: [PATCH] wgengine/userspace: reduce allocations in getStatus Two optimizations. Use values instead of pointers. We were using pointers to make track the "peer in progress" easier. It's not too hard to do it manually, though. Make two passes through the data, so that we can size our return value accurately from the beginning. This is cheap enough compared to the allocation, which grows linearly in the number of peers, that it is worth doing. Signed-off-by: Josh Bleecher Snyder --- wgengine/userspace.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 4a22c888a..b301c7b94 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -921,8 +921,8 @@ func (e *userspaceEngine) getStatus() (*Status, error) { errc <- err }() - pp := make(map[wgkey.Key]*ipnstate.PeerStatusLite) - p := &ipnstate.PeerStatusLite{} + pp := make(map[wgkey.Key]ipnstate.PeerStatusLite) + var p ipnstate.PeerStatusLite var hst1, hst2, n int64 @@ -954,11 +954,10 @@ func (e *userspaceEngine) getStatus() (*Status, error) { if err != nil { return nil, fmt.Errorf("IpcGetOperation: invalid key in line %q", line) } - p = &ipnstate.PeerStatusLite{} - pp[wgkey.Key(pk)] = p - - key := tailcfg.NodeKey(pk) - p.NodeKey = key + if !p.NodeKey.IsZero() { + pp[wgkey.Key(p.NodeKey)] = p + } + p = ipnstate.PeerStatusLite{NodeKey: tailcfg.NodeKey(pk)} case "rx_bytes": n, err = mem.ParseInt(v, 10, 64) p.RxBytes = n @@ -986,6 +985,9 @@ func (e *userspaceEngine) getStatus() (*Status, error) { } // else leave at time.IsZero() } } + if !p.NodeKey.IsZero() { + pp[wgkey.Key(p.NodeKey)] = p + } if err := <-errc; err != nil { return nil, fmt.Errorf("IpcGetOperation: %v", err) } @@ -993,10 +995,19 @@ func (e *userspaceEngine) getStatus() (*Status, error) { e.mu.Lock() defer e.mu.Unlock() - var peers []ipnstate.PeerStatusLite + // Do two passes, one to calculate size and the other to populate. + // This code is sensitive to allocations. + npeers := 0 + for _, pk := range e.peerSequence { + if _, ok := pp[pk]; ok { // ignore idle ones not in wireguard-go's config + npeers++ + } + } + + peers := make([]ipnstate.PeerStatusLite, 0, npeers) for _, pk := range e.peerSequence { if p, ok := pp[pk]; ok { // ignore idle ones not in wireguard-go's config - peers = append(peers, *p) + peers = append(peers, p) } }