mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-10 14:17:56 +01:00
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 ```
111 lines
3.2 KiB
Go
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()
|
|
})
|
|
}
|