device: reduce allocs in Device.IpcGetOperation

Plenty more to go, but a start:

name       old time/op    new time/op    delta
UAPIGet-4    6.37µs ± 2%    5.56µs ± 1%  -12.70%  (p=0.000 n=8+8)

name       old alloc/op   new alloc/op   delta
UAPIGet-4    1.98kB ± 0%    1.22kB ± 0%  -38.71%  (p=0.000 n=10+10)

name       old allocs/op  new allocs/op  delta
UAPIGet-4      42.0 ± 0%      35.0 ± 0%  -16.67%  (p=0.000 n=10+10)

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2021-01-26 11:11:58 -08:00
parent e6ec3852a9
commit 8114c9db5f

View file

@ -7,12 +7,14 @@ package device
import ( import (
"bufio" "bufio"
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net" "net"
"strconv" "strconv"
"strings" "strings"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -41,12 +43,19 @@ func ipcErrorf(code int64, msg string, args ...interface{}) *IPCError {
return &IPCError{code: code, err: fmt.Errorf(msg, args...)} return &IPCError{code: code, err: fmt.Errorf(msg, args...)}
} }
var byteBufferPool = &sync.Pool{
New: func() interface{} { return new(bytes.Buffer) },
}
// IpcGetOperation implements the WireGuard configuration protocol "get" operation. // IpcGetOperation implements the WireGuard configuration protocol "get" operation.
// See https://www.wireguard.com/xplatform/#configuration-protocol for details. // See https://www.wireguard.com/xplatform/#configuration-protocol for details.
func (device *Device) IpcGetOperation(w io.Writer) error { func (device *Device) IpcGetOperation(w io.Writer) error {
lines := make([]string, 0, 100) buf := byteBufferPool.Get().(*bytes.Buffer)
send := func(line string) { buf.Reset()
lines = append(lines, line) defer byteBufferPool.Put(buf)
sendf := func(format string, args ...interface{}) {
fmt.Fprintf(buf, format, args...)
buf.WriteByte('\n')
} }
func() { func() {
@ -65,15 +74,15 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
// serialize device related values // serialize device related values
if !device.staticIdentity.privateKey.IsZero() { if !device.staticIdentity.privateKey.IsZero() {
send("private_key=" + device.staticIdentity.privateKey.ToHex()) sendf("private_key=%s", device.staticIdentity.privateKey.ToHex())
} }
if device.net.port != 0 { if device.net.port != 0 {
send(fmt.Sprintf("listen_port=%d", device.net.port)) sendf("listen_port=%d", device.net.port)
} }
if device.net.fwmark != 0 { if device.net.fwmark != 0 {
send(fmt.Sprintf("fwmark=%d", device.net.fwmark)) sendf("fwmark=%d", device.net.fwmark)
} }
// serialize each peer state // serialize each peer state
@ -82,38 +91,33 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
peer.RLock() peer.RLock()
defer peer.RUnlock() defer peer.RUnlock()
send("public_key=" + peer.handshake.remoteStatic.ToHex()) sendf("public_key=%s", peer.handshake.remoteStatic.ToHex())
send("preshared_key=" + peer.handshake.presharedKey.ToHex()) sendf("preshared_key=%s", peer.handshake.presharedKey.ToHex())
send("protocol_version=1") sendf("protocol_version=1")
if peer.endpoint != nil { if peer.endpoint != nil {
send("endpoint=" + peer.endpoint.DstToString()) sendf("endpoint=%s", peer.endpoint.DstToString())
} }
nano := atomic.LoadInt64(&peer.stats.lastHandshakeNano) nano := atomic.LoadInt64(&peer.stats.lastHandshakeNano)
secs := nano / time.Second.Nanoseconds() secs := nano / time.Second.Nanoseconds()
nano %= time.Second.Nanoseconds() nano %= time.Second.Nanoseconds()
send(fmt.Sprintf("last_handshake_time_sec=%d", secs)) sendf("last_handshake_time_sec=%d", secs)
send(fmt.Sprintf("last_handshake_time_nsec=%d", nano)) sendf("last_handshake_time_nsec=%d", nano)
send(fmt.Sprintf("tx_bytes=%d", atomic.LoadUint64(&peer.stats.txBytes))) sendf("tx_bytes=%d", atomic.LoadUint64(&peer.stats.txBytes))
send(fmt.Sprintf("rx_bytes=%d", atomic.LoadUint64(&peer.stats.rxBytes))) sendf("rx_bytes=%d", atomic.LoadUint64(&peer.stats.rxBytes))
send(fmt.Sprintf("persistent_keepalive_interval=%d", atomic.LoadUint32(&peer.persistentKeepaliveInterval))) sendf("persistent_keepalive_interval=%d", atomic.LoadUint32(&peer.persistentKeepaliveInterval))
for _, ip := range device.allowedips.EntriesForPeer(peer) { for _, ip := range device.allowedips.EntriesForPeer(peer) {
send("allowed_ip=" + ip.String()) sendf("allowed_ip=%s", ip.String())
} }
} }
}() }()
// send lines (does not require resource locks) // send lines (does not require resource locks)
if _, err := w.Write(buf.Bytes()); err != nil {
for _, line := range lines {
_, err := io.WriteString(w, line+"\n")
if err != nil {
return ipcErrorf(ipc.IpcErrorIO, "failed to write output: %w", err) return ipcErrorf(ipc.IpcErrorIO, "failed to write output: %w", err)
} }
}
return nil return nil
} }