From 909c165382b8fc556207803fa6b1f66798934c5a Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 11 Aug 2020 16:40:36 -0700 Subject: [PATCH] derp: remove two key.Public allocations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reading and writing a [32]byte key to a bufio.Reader/bufio.Writer can easily by done without allocating. Do so. It is slower; on my machine, it adds about 100ns per read/write. However, the overall request takes a minimum of several µs, and it cuts allocations meaningfully, so it is probably worth it. name old time/op new time/op delta SendRecv/msgsize=10-8 9.21µs ± 9% 9.08µs ± 8% ~ (p=0.250 n=15+15) SendRecv/msgsize=100-8 6.51µs ± 9% 6.60µs ± 7% ~ (p=0.259 n=15+13) SendRecv/msgsize=1000-8 7.24µs ±13% 7.61µs ±36% ~ (p=1.000 n=11+15) SendRecv/msgsize=10000-8 19.5µs ±15% 19.9µs ±25% ~ (p=0.890 n=14+15) name old speed new speed delta SendRecv/msgsize=10-8 1.09MB/s ± 8% 1.10MB/s ± 8% ~ (p=0.286 n=15+15) SendRecv/msgsize=100-8 15.4MB/s ± 8% 15.1MB/s ± 6% ~ (p=0.129 n=15+12) SendRecv/msgsize=1000-8 139MB/s ±15% 135MB/s ±28% ~ (p=1.000 n=11+15) SendRecv/msgsize=10000-8 516MB/s ±17% 506MB/s ±21% ~ (p=0.880 n=14+15) name old alloc/op new alloc/op delta SendRecv/msgsize=10-8 170B ± 1% 108B ± 1% -36.63% (p=0.000 n=15+15) SendRecv/msgsize=100-8 265B ± 1% 203B ± 1% -23.34% (p=0.000 n=15+15) SendRecv/msgsize=1000-8 1.18kB ± 1% 1.12kB ± 0% -5.31% (p=0.000 n=14+14) SendRecv/msgsize=10000-8 18.8kB ± 2% 18.8kB ± 2% ~ (p=0.443 n=12+12) name old allocs/op new allocs/op delta SendRecv/msgsize=10-8 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=15+15) SendRecv/msgsize=100-8 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=15+15) SendRecv/msgsize=1000-8 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=15+15) SendRecv/msgsize=10000-8 5.00 ± 0% 3.00 ± 0% -40.00% (p=0.000 n=13+14) Signed-off-by: Josh Bleecher Snyder --- derp/derp_server.go | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/derp/derp_server.go b/derp/derp_server.go index a42733056..f9b073e0c 100644 --- a/derp/derp_server.go +++ b/derp/derp_server.go @@ -746,7 +746,7 @@ func (s *Server) recvPacket(br *bufio.Reader, frameLen uint32) (dstKey key.Publi if frameLen < keyLen { return zpub, nil, errors.New("short send packet frame") } - if _, err := io.ReadFull(br, dstKey[:]); err != nil { + if err := readPublicKey(br, &dstKey); err != nil { return zpub, nil, err } packetLen := frameLen - keyLen @@ -1053,7 +1053,8 @@ func (c *sclient) sendPacket(srcKey key.Public, contents []byte) (err error) { return err } if withKey { - if _, err = c.bw.Write(srcKey[:]); err != nil { + err := writePublicKey(c.bw, &srcKey) + if err != nil { return err } } @@ -1250,3 +1251,34 @@ func (s *Server) ConsistencyCheck() error { } return errors.New(strings.Join(errs, ", ")) } + +// readPublicKey reads key from br. +// It is ~4x slower than io.ReadFull(br, key), +// but it prevents key from escaping and thus being allocated. +// If io.ReadFull(br, key) does not cause key to escape, use that instead. +func readPublicKey(br *bufio.Reader, key *key.Public) error { + // Do io.ReadFull(br, key), but one byte at a time, to avoid allocation. + for i := range key { + b, err := br.ReadByte() + if err != nil { + return err + } + key[i] = b + } + return nil +} + +// writePublicKey writes key to bw. +// It is ~3x slower than bw.Write(key[:]), +// but it prevents key from escaping and thus being allocated. +// If bw.Write(key[:]) does not cause key to escape, use that instead. +func writePublicKey(bw *bufio.Writer, key *key.Public) error { + // Do bw.Write(key[:]), but one byte at a time to avoid allocation. + for _, b := range key { + err := bw.WriteByte(b) + if err != nil { + return err + } + } + return nil +}