Files
lnd/brontide/bench_test.go
Olaoluwa Osuntokun 6e0083a369 brontide: add benchark for WriteMessage
In this commit, we add a benchmark for the WriteMessage method. This is
the first step in an upcoming optimizooor series of commits.

The baseline is:
```
goos: darwin
goarch: arm64
pkg: github.com/lightningnetwork/lnd/brontide
cpu: Apple M4 Max
BenchmarkWriteMessage-16    	   22736	     50667 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   23217	     50463 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   24241	     49941 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   23574	     51021 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   23784	     49926 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   24230	     50538 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   24058	     49971 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   23762	     50224 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   24266	     53034 ns/op	   73788 B/op	       5 allocs/op
BenchmarkWriteMessage-16    	   23317	     50045 ns/op	   73788 B/op	       5 allocs/op
PASS
ok  	github.com/lightningnetwork/lnd/brontide	17.433s
```
2025-09-24 11:51:59 -07:00

111 lines
3.2 KiB
Go

package brontide
import (
"bytes"
"io"
"math"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func BenchmarkReadHeaderAndBody(t *testing.B) {
// Create a test connection, grabbing either side of the connection
// into local variables. If the initial crypto handshake fails, then
// we'll get a non-nil error here.
localConn, remoteConn, err := establishTestConnection(t)
require.NoError(t, err, "unable to establish test connection: %v", err)
rand.Seed(time.Now().Unix())
noiseRemoteConn := remoteConn.(*Conn)
noiseLocalConn := localConn.(*Conn)
// Now that we have a local and remote side (to set up the initial
// handshake state, we'll have the remote side write out something
// similar to a large message in the protocol.
const pktSize = 60_000
msg := bytes.Repeat([]byte("a"), pktSize)
err = noiseRemoteConn.WriteMessage(msg)
require.NoError(t, err, "unable to write encrypted message: %v", err)
cipherHeader := noiseRemoteConn.noise.nextHeaderSend
cipherMsg := noiseRemoteConn.noise.nextBodySend
var (
benchErr error
msgBuf [math.MaxUint16]byte
)
t.ReportAllocs()
t.ResetTimer()
nonceValue := noiseLocalConn.noise.recvCipher.nonce
for i := 0; i < t.N; i++ {
pktLen, benchErr := noiseLocalConn.noise.ReadHeader(
bytes.NewReader(cipherHeader),
)
require.NoError(
t, benchErr, "#%v: failed decryption: %v", i, benchErr,
)
_, benchErr = noiseLocalConn.noise.ReadBody(
bytes.NewReader(cipherMsg), msgBuf[:pktLen],
)
require.NoError(
t, benchErr, "#%v: failed decryption: %v", i, benchErr,
)
// We reset the internal nonce each time as otherwise, we'd
// continue to increment it which would cause a decryption
// failure.
noiseLocalConn.noise.recvCipher.nonce = nonceValue
}
require.NoError(t, benchErr)
}
// BenchmarkWriteMessage benchmarks the performance of writing a maximum-sized
// message and flushing it to an io.Discard to measure the allocation and CPU
// overhead of the encryption and writing logic.
func BenchmarkWriteMessage(b *testing.B) {
localConn, remoteConn, err := establishTestConnection(b)
require.NoError(b, err, "unable to establish test connection: %v", err)
noiseLocalConn, ok := localConn.(*Conn)
require.True(b, ok, "expected *Conn type for localConn")
// Create the largest possible message we can write (MaxUint16 bytes).
// This is the maximum message size allowed by the protocol.
const maxMsgSize = math.MaxUint16
largeMsg := bytes.Repeat([]byte("a"), maxMsgSize)
// Use io.Discard to simulate writing to a network connection that
// continuously accepts data without needing resets.
discard := io.Discard
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Write our massive message, then call flush to actually write
// the encrypted message This simulates a full write operation
// to a network.
err := noiseLocalConn.noise.WriteMessage(largeMsg)
if err != nil {
b.Fatalf("WriteMessage failed: %v", err)
}
_, err = noiseLocalConn.noise.Flush(discard)
if err != nil {
b.Fatalf("Flush failed: %v", err)
}
}
// We'll make sure to clean up the connections at the end of the
// benchmark.
b.Cleanup(func() {
localConn.Close()
remoteConn.Close()
})
}