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:
parent
e6ec3852a9
commit
8114c9db5f
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue