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 <josh@tailscale.com>
pull/2687/head
Josh Bleecher Snyder 3 years ago committed by Josh Bleecher Snyder
parent af30897f0d
commit adf696172d

@ -921,8 +921,8 @@ func (e *userspaceEngine) getStatus() (*Status, error) {
errc <- err errc <- err
}() }()
pp := make(map[wgkey.Key]*ipnstate.PeerStatusLite) pp := make(map[wgkey.Key]ipnstate.PeerStatusLite)
p := &ipnstate.PeerStatusLite{} var p ipnstate.PeerStatusLite
var hst1, hst2, n int64 var hst1, hst2, n int64
@ -954,11 +954,10 @@ func (e *userspaceEngine) getStatus() (*Status, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("IpcGetOperation: invalid key in line %q", line) return nil, fmt.Errorf("IpcGetOperation: invalid key in line %q", line)
} }
p = &ipnstate.PeerStatusLite{} if !p.NodeKey.IsZero() {
pp[wgkey.Key(pk)] = p pp[wgkey.Key(p.NodeKey)] = p
}
key := tailcfg.NodeKey(pk) p = ipnstate.PeerStatusLite{NodeKey: tailcfg.NodeKey(pk)}
p.NodeKey = key
case "rx_bytes": case "rx_bytes":
n, err = mem.ParseInt(v, 10, 64) n, err = mem.ParseInt(v, 10, 64)
p.RxBytes = n p.RxBytes = n
@ -986,6 +985,9 @@ func (e *userspaceEngine) getStatus() (*Status, error) {
} // else leave at time.IsZero() } // else leave at time.IsZero()
} }
} }
if !p.NodeKey.IsZero() {
pp[wgkey.Key(p.NodeKey)] = p
}
if err := <-errc; err != nil { if err := <-errc; err != nil {
return nil, fmt.Errorf("IpcGetOperation: %v", err) return nil, fmt.Errorf("IpcGetOperation: %v", err)
} }
@ -993,10 +995,19 @@ func (e *userspaceEngine) getStatus() (*Status, error) {
e.mu.Lock() e.mu.Lock()
defer e.mu.Unlock() 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 { for _, pk := range e.peerSequence {
if p, ok := pp[pk]; ok { // ignore idle ones not in wireguard-go's config if p, ok := pp[pk]; ok { // ignore idle ones not in wireguard-go's config
peers = append(peers, *p) peers = append(peers, p)
} }
} }

Loading…
Cancel
Save