brontide: reduce memory allocs by using static buf for next header+msg
In this commit, we reduce the total number of allocations that a brontide session will incur over its lifetime. Profiling on one of my nodes showed that we were generating a lot of garbage due to re-creating a 65KB buffer to read the next message each time the ReadMessage method was called. To reduce the total number of memory allocations, we’ll now simply re-use a buffer for both the cipher text header, and the cipher text itself.
This commit is contained in:
parent
e5f9b28e39
commit
0c16ab6b32
@ -345,6 +345,20 @@ type Machine struct {
|
|||||||
ephemeralGen func() (*btcec.PrivateKey, error)
|
ephemeralGen func() (*btcec.PrivateKey, error)
|
||||||
|
|
||||||
handshakeState
|
handshakeState
|
||||||
|
|
||||||
|
// nextCipherHeader is a static buffer that we'll use to read in the
|
||||||
|
// next ciphertext header from the wire. The header is a 2 byte length
|
||||||
|
// (of the next ciphertext), followed by a 16 byte MAC.
|
||||||
|
nextCipherHeader [lengthHeaderSize + macSize]byte
|
||||||
|
|
||||||
|
// nextCipherText is a static buffer that we'll use to read in the
|
||||||
|
// bytes of the next cipher text message. As all messages in the
|
||||||
|
// protocol MUST be below 65KB plus our macSize, this will be
|
||||||
|
// sufficient to buffer all messages from the socket when we need to
|
||||||
|
// read the next one. Having a fixed buffer that's re-used also means
|
||||||
|
// that we save on allocations as we don't need to create a new one
|
||||||
|
// each time.
|
||||||
|
nextCipherText [math.MaxUint16 + macSize]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBrontideMachine creates a new instance of the brontide state-machine. If
|
// NewBrontideMachine creates a new instance of the brontide state-machine. If
|
||||||
@ -698,25 +712,25 @@ func (b *Machine) WriteMessage(w io.Writer, p []byte) error {
|
|||||||
// ReadMessage attempts to read the next message from the passed io.Reader. In
|
// ReadMessage attempts to read the next message from the passed io.Reader. In
|
||||||
// the case of an authentication error, a non-nil error is returned.
|
// the case of an authentication error, a non-nil error is returned.
|
||||||
func (b *Machine) ReadMessage(r io.Reader) ([]byte, error) {
|
func (b *Machine) ReadMessage(r io.Reader) ([]byte, error) {
|
||||||
var cipherLen [lengthHeaderSize + macSize]byte
|
if _, err := io.ReadFull(r, b.nextCipherHeader[:]); err != nil {
|
||||||
if _, err := io.ReadFull(r, cipherLen[:]); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to decrypt+auth the packet length present in the stream.
|
// Attempt to decrypt+auth the packet length present in the stream.
|
||||||
pktLenBytes, err := b.recvCipher.Decrypt(nil, nil, cipherLen[:])
|
pktLenBytes, err := b.recvCipher.Decrypt(
|
||||||
|
nil, nil, b.nextCipherHeader[:],
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, using the length read from the packet header, read the
|
// Next, using the length read from the packet header, read the
|
||||||
// encrypted packet itself.
|
// encrypted packet itself.
|
||||||
var cipherText [math.MaxUint16 + macSize]byte
|
|
||||||
pktLen := uint32(binary.BigEndian.Uint16(pktLenBytes)) + macSize
|
pktLen := uint32(binary.BigEndian.Uint16(pktLenBytes)) + macSize
|
||||||
if _, err := io.ReadFull(r, cipherText[:pktLen]); err != nil {
|
if _, err := io.ReadFull(r, b.nextCipherText[:pktLen]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): modify to let pass in slice
|
// TODO(roasbeef): modify to let pass in slice
|
||||||
return b.recvCipher.Decrypt(nil, nil, cipherText[:pktLen])
|
return b.recvCipher.Decrypt(nil, nil, b.nextCipherText[:pktLen])
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user