test(benchmarks): add conn and conceal benchmark suites

Add benchmark harnesses for the conn and conceal packages with
deterministic crypto/rand, fake transports, and no live sockets in
timed loops.

Keep a compact default benchmark matrix for routine benchstat runs and
preserve the exhaustive matrix behind AMNEZWG_BENCH_FULL=1.
This commit is contained in:
Frog Rocky
2026-03-27 12:56:45 +01:00
parent baeb11c168
commit 3075cbdd1f
4 changed files with 3355 additions and 0 deletions
+581
View File
@@ -0,0 +1,581 @@
package conceal
import (
"bytes"
crand "crypto/rand"
"encoding/binary"
"io"
"net"
"os"
"sync"
"syscall"
"testing"
"time"
"golang.org/x/net/ipv4"
)
const (
benchmarkMaxPacketSize = 65535
benchmarkBatchPoolSize = 128
benchmarkUDPListenPort = 51820
benchmarkFixtureRingSize = 256
)
var (
benchmarkFramedOpts = FramedOpts{
H1: benchmarkMustHeader("777"),
H2: benchmarkMustHeader("778"),
H3: benchmarkMustHeader("779"),
H4: benchmarkMustHeader("780"),
S1: 8,
S2: 8,
S3: 8,
S4: 16,
}
benchmarkFramedCompatOpts = func() FramedOpts {
opts := benchmarkFramedOpts
opts.HeaderCompat = true
return opts
}()
benchmarkMasqueradeRules = benchmarkMustRules("<dz be 2><d>")
benchmarkPreludeOneRule = PreludeOpts{
RulesArr: [5]Rules{
benchmarkMustRules("<b 0xaabb>"),
},
}
benchmarkPreludeFiveRules = PreludeOpts{
RulesArr: [5]Rules{
benchmarkMustRules("<b 0xa1>"),
benchmarkMustRules("<b 0xa2a3>"),
benchmarkMustRules("<b 0xa4a5a6>"),
benchmarkMustRules("<b 0xa7a8a9aa>"),
benchmarkMustRules("<b 0xabacadaeaf>"),
},
}
benchmarkPreludeRulesPlusJunk = PreludeOpts{
Jc: 1,
Jmin: 3,
Jmax: 3,
RulesArr: [5]Rules{
benchmarkMustRules("<b 0xaabb>"),
},
}
benchmarkPayloads = benchmarkBuildPayloadProfiles()
benchmarkUDPAddr = &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: benchmarkUDPListenPort,
}
benchmarkRandMu sync.Mutex
)
type benchmarkPayloadProfiles struct {
initiation []byte
response []byte
cookie []byte
transportKeepalive []byte
transportSmall []byte
transportMTU []byte
compatInitiation []byte
compatResponse []byte
compatCookie []byte
compatTransportKeepalive []byte
compatTransportSmall []byte
compatTransportMTU []byte
}
func benchmarkBuildPayloadProfiles() benchmarkPayloadProfiles {
return benchmarkPayloadProfiles{
initiation: benchmarkMakePayload(WireguardMsgInitiationSize, WireguardMsgInitiationType),
response: benchmarkMakePayload(WireguardMsgResponseSize, WireguardMsgResponseType),
cookie: benchmarkMakePayload(WireguardMsgCookieReplySize, WireguardMsgCookieReplyType),
transportKeepalive: benchmarkMakePayload(WireguardMsgTransportMinSize, WireguardMsgTransportType),
transportSmall: benchmarkMakePayload(256, WireguardMsgTransportType),
transportMTU: benchmarkMakePayload(1280, WireguardMsgTransportType),
compatInitiation: benchmarkMakePayload(WireguardMsgInitiationSize, benchmarkFramedCompatOpts.H1.start),
compatResponse: benchmarkMakePayload(WireguardMsgResponseSize, benchmarkFramedCompatOpts.H2.start),
compatCookie: benchmarkMakePayload(WireguardMsgCookieReplySize, benchmarkFramedCompatOpts.H3.start),
compatTransportKeepalive: benchmarkMakePayload(WireguardMsgTransportMinSize, benchmarkFramedCompatOpts.H4.start),
compatTransportSmall: benchmarkMakePayload(256, benchmarkFramedCompatOpts.H4.start),
compatTransportMTU: benchmarkMakePayload(1280, benchmarkFramedCompatOpts.H4.start),
}
}
func benchmarkMakePayload(size int, header uint32) []byte {
payload := make([]byte, size)
binary.LittleEndian.PutUint32(payload[:4], header)
for i := 4; i < len(payload); i++ {
payload[i] = byte(i)
}
return payload
}
func benchmarkMustRules(spec string) Rules {
rules, err := ParseRules(spec)
if err != nil {
panic(err)
}
return rules
}
func benchmarkMustHeader(spec string) *RangedHeader {
header, err := NewRangedHeader(spec)
if err != nil {
panic(err)
}
return header
}
func benchmarkNewBufferPool() *sync.Pool {
return &sync.Pool{
New: func() any {
return make([]byte, benchmarkMaxPacketSize)
},
}
}
func benchmarkNewMsgsPool() *sync.Pool {
return &sync.Pool{
New: func() any {
msgs := make([]ipv4.Message, benchmarkBatchPoolSize)
for i := range msgs {
msgs[i].Buffers = make([][]byte, 1)
msgs[i].OOB = make([]byte, 0)
}
return &msgs
},
}
}
func benchmarkMaxFramePadding(opts FramedOpts) int {
maxPadding := opts.S1
if opts.S2 > maxPadding {
maxPadding = opts.S2
}
if opts.S3 > maxPadding {
maxPadding = opts.S3
}
if opts.S4 > maxPadding {
maxPadding = opts.S4
}
return maxPadding
}
func benchmarkEncodeFramedRecord(opts FramedOpts, payload []byte) []byte {
enc, ok := newFrameEncoding(opts)
if !ok {
panic("framed benchmark encoding unavailable")
}
buf := make([]byte, len(payload)+benchmarkMaxFramePadding(opts))
n := enc.Encode(buf, payload)
if n == 0 {
panic("framed benchmark encoding failed")
}
return append([]byte(nil), buf[:n]...)
}
func benchmarkEncodeMasqueradeRecord(rules Rules, payload []byte) []byte {
pool := benchmarkNewBufferPool()
ctx := &writeContext{
FlexBuffer: WrapFlexBuffer(payload),
BufferPool: WrapBufferPool(pool),
}
tmp := pool.Get().([]byte)
defer pool.Put(tmp)
w := bytes.NewBuffer(tmp[:0])
if err := rules.Write(w, ctx); err != nil {
panic(err)
}
return append([]byte(nil), w.Bytes()...)
}
func benchmarkEncodeStreamRecords(rules Rules, records ...[]byte) []byte {
var out bytes.Buffer
for _, record := range records {
out.Write(benchmarkEncodeMasqueradeRecord(rules, record))
}
return out.Bytes()
}
func benchmarkRepeatPayload(payload []byte, count int) [][]byte {
payloads := make([][]byte, count)
for i := range payloads {
payloads[i] = payload
}
return payloads
}
func benchmarkInitiationBatch(batchSize int) [][]byte {
payloads := make([][]byte, batchSize)
if batchSize == 0 {
return payloads
}
payloads[0] = benchmarkPayloads.initiation
for i := 1; i < len(payloads); i++ {
payloads[i] = benchmarkPayloads.transportSmall
}
return payloads
}
func benchmarkTotalBytes(payloads [][]byte) int {
total := 0
for _, payload := range payloads {
total += len(payload)
}
return total
}
func benchmarkAverageBytes(payloads ...[]byte) int {
if len(payloads) == 0 {
return 0
}
total := 0
for _, payload := range payloads {
total += len(payload)
}
return total / len(payloads)
}
func benchmarkAverageInts(values ...int) int {
if len(values) == 0 {
return 0
}
total := 0
for _, value := range values {
total += value
}
return total / len(values)
}
func benchmarkFullMatrixEnabled() bool {
return os.Getenv("AMNEZWG_BENCH_FULL") != ""
}
func benchmarkRunLoop(b *testing.B, bytesPerOp int, reset func(), op func() error) {
b.Helper()
b.ReportAllocs()
if bytesPerOp > 0 {
b.SetBytes(int64(bytesPerOp))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i > 0 && reset != nil {
b.StopTimer()
reset()
b.StartTimer()
}
if err := op(); err != nil {
b.Fatal(err)
}
}
}
func benchmarkRunLoopWithFixtureRing[T any](b *testing.B, bytesPerOp int, fixtures []T, reset func(T), op func(T) error) {
b.Helper()
if len(fixtures) == 0 {
b.Fatal("benchmark fixture ring must not be empty")
}
b.ReportAllocs()
if bytesPerOp > 0 {
b.SetBytes(int64(bytesPerOp))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i > 0 && i%len(fixtures) == 0 && reset != nil {
b.StopTimer()
for _, fixture := range fixtures {
reset(fixture)
}
b.StartTimer()
}
if err := op(fixtures[i%len(fixtures)]); err != nil {
b.Fatal(err)
}
}
}
func benchmarkUseDeterministicRand(b *testing.B) {
b.Helper()
benchmarkRandMu.Lock()
oldReader := crand.Reader
crand.Reader = &benchmarkDeterministicReader{state: 1}
b.Cleanup(func() {
crand.Reader = oldReader
benchmarkRandMu.Unlock()
})
}
type benchmarkDeterministicReader struct {
state uint64
}
func (r *benchmarkDeterministicReader) Read(p []byte) (int, error) {
x := r.state
if x == 0 {
x = 1
}
for i := range p {
x ^= x << 13
x ^= x >> 7
x ^= x << 17
p[i] = byte(x)
}
r.state = x
return len(p), nil
}
type benchmarkStreamConn struct {
readBuf []byte
readOff int
readChunks [][]byte
readChunkAt int
readChunkOff int
}
func newBenchmarkStreamConn(readBuf []byte) *benchmarkStreamConn {
return &benchmarkStreamConn{readBuf: readBuf}
}
func newBenchmarkStreamChunksConn(readChunks [][]byte) *benchmarkStreamConn {
return &benchmarkStreamConn{readChunks: readChunks}
}
func (c *benchmarkStreamConn) ResetRead() {
c.readOff = 0
c.readChunkAt = 0
c.readChunkOff = 0
}
func (c *benchmarkStreamConn) Read(p []byte) (int, error) {
if len(c.readChunks) > 0 {
chunk := c.readChunks[c.readChunkAt]
if c.readChunkOff >= len(chunk) {
c.readChunkAt++
if c.readChunkAt == len(c.readChunks) {
c.readChunkAt = 0
}
c.readChunkOff = 0
chunk = c.readChunks[c.readChunkAt]
}
n := copy(p, chunk[c.readChunkOff:])
c.readChunkOff += n
if c.readChunkOff == len(chunk) {
c.readChunkAt++
if c.readChunkAt == len(c.readChunks) {
c.readChunkAt = 0
}
c.readChunkOff = 0
}
return n, nil
}
if len(c.readBuf) == 0 {
return 0, io.EOF
}
if c.readOff >= len(c.readBuf) {
c.readOff = 0
}
n := copy(p, c.readBuf[c.readOff:])
c.readOff += n
return n, nil
}
func (c *benchmarkStreamConn) Write(p []byte) (int, error) {
return len(p), nil
}
func (c *benchmarkStreamConn) Close() error {
return nil
}
func (c *benchmarkStreamConn) LocalAddr() net.Addr {
return benchmarkUDPAddr
}
func (c *benchmarkStreamConn) RemoteAddr() net.Addr {
return benchmarkUDPAddr
}
func (c *benchmarkStreamConn) SetDeadline(time.Time) error {
return nil
}
func (c *benchmarkStreamConn) SetReadDeadline(time.Time) error {
return nil
}
func (c *benchmarkStreamConn) SetWriteDeadline(time.Time) error {
return nil
}
type benchmarkUDPConn struct {
readPayloads [][]byte
readIndex int
}
func newBenchmarkUDPConn(readPayloads [][]byte) *benchmarkUDPConn {
return &benchmarkUDPConn{readPayloads: readPayloads}
}
func (c *benchmarkUDPConn) ResetRead() {
c.readIndex = 0
}
func (c *benchmarkUDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
n, _, _, udpAddr, err := c.ReadMsgUDP(p, nil)
return n, udpAddr, err
}
func (c *benchmarkUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
udpAddr, _ := addr.(*net.UDPAddr)
n, _, err = c.WriteMsgUDP(p, nil, udpAddr)
return n, err
}
func (c *benchmarkUDPConn) Close() error {
return nil
}
func (c *benchmarkUDPConn) LocalAddr() net.Addr {
return benchmarkUDPAddr
}
func (c *benchmarkUDPConn) SetDeadline(time.Time) error {
return nil
}
func (c *benchmarkUDPConn) SetReadDeadline(time.Time) error {
return nil
}
func (c *benchmarkUDPConn) SetWriteDeadline(time.Time) error {
return nil
}
func (c *benchmarkUDPConn) SyscallConn() (syscall.RawConn, error) {
return nil, nil
}
func (c *benchmarkUDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
if len(c.readPayloads) == 0 {
return 0, 0, 0, nil, io.EOF
}
payload := c.readPayloads[c.readIndex]
c.readIndex++
if c.readIndex == len(c.readPayloads) {
c.readIndex = 0
}
n = copy(b, payload)
return n, 0, 0, benchmarkUDPAddr, nil
}
func (c *benchmarkUDPConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
return len(b), len(oob), nil
}
type benchmarkBatchPacket struct {
payload []byte
oob []byte
addr *net.UDPAddr
}
type benchmarkBatchConn struct {
readBatches [][]benchmarkBatchPacket
readIndex int
}
func newBenchmarkBatchConn(readBatches [][]benchmarkBatchPacket) *benchmarkBatchConn {
return &benchmarkBatchConn{readBatches: readBatches}
}
func (c *benchmarkBatchConn) ResetRead() {
c.readIndex = 0
}
func (c *benchmarkBatchConn) ReadBatch(ms []ipv4.Message, flags int) (int, error) {
if len(c.readBatches) == 0 {
return 0, io.EOF
}
batch := c.readBatches[c.readIndex]
c.readIndex++
if c.readIndex == len(c.readBatches) {
c.readIndex = 0
}
for i := range batch {
packet := batch[i]
msg := &ms[i]
msg.N = copy(msg.Buffers[0], packet.payload)
if cap(msg.OOB) < len(packet.oob) {
panic("benchmark batch OOB buffer too small")
}
msg.OOB = msg.OOB[:len(packet.oob)]
copy(msg.OOB, packet.oob)
msg.NN = len(packet.oob)
msg.Addr = packet.addr
}
for i := len(batch); i < len(ms); i++ {
ms[i].N = 0
ms[i].NN = 0
ms[i].Addr = nil
ms[i].OOB = ms[i].OOB[:0]
}
return len(batch), nil
}
func (c *benchmarkBatchConn) WriteBatch(ms []ipv4.Message, flags int) (int, error) {
return len(ms), nil
}
func benchmarkBatchPackets(payloads [][]byte) []benchmarkBatchPacket {
packets := make([]benchmarkBatchPacket, len(payloads))
for i, payload := range payloads {
packets[i] = benchmarkBatchPacket{
payload: payload,
addr: benchmarkUDPAddr,
}
}
return packets
}
func benchmarkNewBatchReadMessages(count int, oobCap int) []ipv4.Message {
msgs := make([]ipv4.Message, count)
for i := range msgs {
msgs[i].Buffers = make([][]byte, 1)
msgs[i].Buffers[0] = make([]byte, benchmarkMaxPacketSize)
msgs[i].OOB = make([]byte, 0, oobCap)
}
return msgs
}
type benchmarkBatchWriteFixture struct {
msgs []ipv4.Message
payloads [][]byte
addr *net.UDPAddr
}
func newBenchmarkBatchWriteFixture(payloads [][]byte, oobCap int) *benchmarkBatchWriteFixture {
fixture := &benchmarkBatchWriteFixture{
msgs: make([]ipv4.Message, len(payloads)),
payloads: payloads,
addr: benchmarkUDPAddr,
}
for i := range fixture.msgs {
fixture.msgs[i].Buffers = make([][]byte, 1)
fixture.msgs[i].OOB = make([]byte, 0, oobCap)
}
fixture.Reset()
return fixture
}
func (f *benchmarkBatchWriteFixture) Reset() {
for i, payload := range f.payloads {
f.msgs[i].Buffers[0] = payload
f.msgs[i].N = len(payload)
f.msgs[i].NN = 0
f.msgs[i].Addr = f.addr
f.msgs[i].OOB = f.msgs[i].OOB[:0]
}
}
+901
View File
@@ -0,0 +1,901 @@
package conceal
import "testing"
func BenchmarkTCPRawConn(b *testing.B) {
benchmarkUseDeterministicRand(b)
if benchmarkFullMatrixEnabled() {
benchmarkTCPRawConnFull(b)
return
}
readPayloads := [][]byte{
benchmarkPayloads.initiation,
benchmarkPayloads.transportSmall,
}
b.Run("Read/mixed", func(b *testing.B) {
conn := newBenchmarkStreamChunksConn(readPayloads)
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, benchmarkAverageBytes(readPayloads...), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
writePayloads := [][]byte{
benchmarkPayloads.initiation,
benchmarkPayloads.transportSmall,
benchmarkPayloads.transportMTU,
}
b.Run("Write/mixed", func(b *testing.B) {
conn := newBenchmarkStreamConn(nil)
next := 0
benchmarkRunLoop(b, benchmarkAverageBytes(writePayloads...), nil, func() error {
payload := writePayloads[next]
next++
if next == len(writePayloads) {
next = 0
}
_, err := conn.Write(payload)
return err
})
})
}
func benchmarkTCPRawConnFull(b *testing.B) {
readCases := []struct {
name string
payload []byte
}{
{name: "Read/initiation", payload: benchmarkPayloads.initiation},
{name: "Read/transport_small", payload: benchmarkPayloads.transportSmall},
}
for _, tc := range readCases {
b.Run(tc.name, func(b *testing.B) {
conn := newBenchmarkStreamConn(tc.payload)
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
}
writeCases := []struct {
name string
payload []byte
}{
{name: "Write/initiation", payload: benchmarkPayloads.initiation},
{name: "Write/transport_small", payload: benchmarkPayloads.transportSmall},
{name: "Write/transport_mtu", payload: benchmarkPayloads.transportMTU},
}
for _, tc := range writeCases {
b.Run(tc.name, func(b *testing.B) {
conn := newBenchmarkStreamConn(nil)
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.Write(tc.payload)
return err
})
})
}
}
func BenchmarkTCPRecordMasquerade(b *testing.B) {
benchmarkUseDeterministicRand(b)
if benchmarkFullMatrixEnabled() {
benchmarkTCPRecordMasqueradeFull(b)
return
}
readPayloads := [][]byte{
benchmarkPayloads.initiation,
benchmarkPayloads.transportSmall,
}
readEncoded := make([][]byte, len(readPayloads))
for i, payload := range readPayloads {
readEncoded[i] = benchmarkEncodeMasqueradeRecord(benchmarkMasqueradeRules, payload)
}
b.Run("ReadRecord/mixed", func(b *testing.B) {
source := newBenchmarkStreamChunksConn(readEncoded)
pool := benchmarkNewBufferPool()
conn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected stream masquerade benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, benchmarkAverageBytes(readPayloads...), nil, func() error {
_, err := conn.ReadRecord(buf)
return err
})
})
writePayloads := [][]byte{
benchmarkPayloads.initiation,
benchmarkPayloads.transportSmall,
benchmarkPayloads.transportMTU,
}
b.Run("WriteRecord/mixed", func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
conn, ok := NewMasqueradeConn(sink, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected stream masquerade benchmark conn")
}
next := 0
benchmarkRunLoop(b, benchmarkAverageBytes(writePayloads...), nil, func() error {
payload := writePayloads[next]
next++
if next == len(writePayloads) {
next = 0
}
_, err := conn.WriteRecord(payload)
return err
})
})
}
func benchmarkTCPRecordMasqueradeFull(b *testing.B) {
readCases := []struct {
name string
payload []byte
}{
{name: "ReadRecord/initiation", payload: benchmarkPayloads.initiation},
{name: "ReadRecord/transport_small", payload: benchmarkPayloads.transportSmall},
}
for _, tc := range readCases {
b.Run(tc.name, func(b *testing.B) {
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(benchmarkMasqueradeRules, tc.payload))
pool := benchmarkNewBufferPool()
conn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected stream masquerade benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.ReadRecord(buf)
return err
})
})
}
writeCases := []struct {
name string
payload []byte
}{
{name: "WriteRecord/initiation", payload: benchmarkPayloads.initiation},
{name: "WriteRecord/transport_small", payload: benchmarkPayloads.transportSmall},
{name: "WriteRecord/transport_mtu", payload: benchmarkPayloads.transportMTU},
}
for _, tc := range writeCases {
b.Run(tc.name, func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
conn, ok := NewMasqueradeConn(sink, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected stream masquerade benchmark conn")
}
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.WriteRecord(tc.payload)
return err
})
})
}
}
func BenchmarkTCPFramed(b *testing.B) {
benchmarkUseDeterministicRand(b)
if benchmarkFullMatrixEnabled() {
benchmarkTCPFramedFull(b)
return
}
compatOffPayloads := [][]byte{
benchmarkPayloads.initiation,
benchmarkPayloads.response,
benchmarkPayloads.cookie,
benchmarkPayloads.transportKeepalive,
benchmarkPayloads.transportMTU,
}
compatOffEncoded := make([][]byte, len(compatOffPayloads))
for i, payload := range compatOffPayloads {
compatOffEncoded[i] = benchmarkEncodeFramedRecord(benchmarkFramedOpts, payload)
}
b.Run("Read/compat_off/mixed", func(b *testing.B) {
source := newBenchmarkStreamChunksConn(compatOffEncoded)
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(source, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected framed benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, benchmarkAverageBytes(compatOffPayloads...), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
b.Run("Write/compat_off/mixed", func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(sink, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected framed benchmark conn")
}
next := 0
benchmarkRunLoop(b, benchmarkAverageBytes(compatOffPayloads...), nil, func() error {
payload := compatOffPayloads[next]
next++
if next == len(compatOffPayloads) {
next = 0
}
_, err := conn.Write(payload)
return err
})
})
compatOnPayloads := [][]byte{
benchmarkPayloads.compatInitiation,
benchmarkPayloads.compatTransportMTU,
}
compatOnEncoded := make([][]byte, len(compatOnPayloads))
for i, payload := range compatOnPayloads {
compatOnEncoded[i] = benchmarkEncodeFramedRecord(benchmarkFramedCompatOpts, payload)
}
b.Run("Read/compat_on/mixed", func(b *testing.B) {
source := newBenchmarkStreamChunksConn(compatOnEncoded)
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(source, pool, benchmarkFramedCompatOpts)
if !ok {
b.Fatal("expected compat framed benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, benchmarkAverageBytes(compatOnPayloads...), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
b.Run("Write/compat_on/mixed", func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(sink, pool, benchmarkFramedCompatOpts)
if !ok {
b.Fatal("expected compat framed benchmark conn")
}
next := 0
benchmarkRunLoop(b, benchmarkAverageBytes(compatOnPayloads...), nil, func() error {
payload := compatOnPayloads[next]
next++
if next == len(compatOnPayloads) {
next = 0
}
_, err := conn.Write(payload)
return err
})
})
}
func benchmarkTCPFramedFull(b *testing.B) {
readCompatOff := []struct {
name string
payload []byte
}{
{name: "Read/compat_off/initiation", payload: benchmarkPayloads.initiation},
{name: "Read/compat_off/response", payload: benchmarkPayloads.response},
{name: "Read/compat_off/cookie", payload: benchmarkPayloads.cookie},
{name: "Read/compat_off/transport_keepalive", payload: benchmarkPayloads.transportKeepalive},
{name: "Read/compat_off/transport_mtu", payload: benchmarkPayloads.transportMTU},
}
for _, tc := range readCompatOff {
b.Run(tc.name, func(b *testing.B) {
source := newBenchmarkStreamConn(benchmarkEncodeFramedRecord(benchmarkFramedOpts, tc.payload))
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(source, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected framed benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
}
writeCompatOff := []struct {
name string
payload []byte
}{
{name: "Write/compat_off/initiation", payload: benchmarkPayloads.initiation},
{name: "Write/compat_off/response", payload: benchmarkPayloads.response},
{name: "Write/compat_off/cookie", payload: benchmarkPayloads.cookie},
{name: "Write/compat_off/transport_keepalive", payload: benchmarkPayloads.transportKeepalive},
{name: "Write/compat_off/transport_mtu", payload: benchmarkPayloads.transportMTU},
}
for _, tc := range writeCompatOff {
b.Run(tc.name, func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(sink, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected framed benchmark conn")
}
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.Write(tc.payload)
return err
})
})
}
readCompatOn := []struct {
name string
payload []byte
}{
{name: "Read/compat_on/initiation", payload: benchmarkPayloads.compatInitiation},
{name: "Read/compat_on/transport_mtu", payload: benchmarkPayloads.compatTransportMTU},
}
for _, tc := range readCompatOn {
b.Run(tc.name, func(b *testing.B) {
source := newBenchmarkStreamConn(benchmarkEncodeFramedRecord(benchmarkFramedCompatOpts, tc.payload))
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(source, pool, benchmarkFramedCompatOpts)
if !ok {
b.Fatal("expected compat framed benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
}
writeCompatOn := []struct {
name string
payload []byte
}{
{name: "Write/compat_on/initiation", payload: benchmarkPayloads.compatInitiation},
{name: "Write/compat_on/transport_mtu", payload: benchmarkPayloads.compatTransportMTU},
}
for _, tc := range writeCompatOn {
b.Run(tc.name, func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
conn, ok := NewFramedConn(sink, pool, benchmarkFramedCompatOpts)
if !ok {
b.Fatal("expected compat framed benchmark conn")
}
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.Write(tc.payload)
return err
})
})
}
}
func BenchmarkTCPPrelude(b *testing.B) {
benchmarkUseDeterministicRand(b)
if benchmarkFullMatrixEnabled() {
benchmarkTCPPreludeFull(b)
return
}
b.Run("ReadCold/mixed_decoys_then_initiation", func(b *testing.B) {
fixtures := make([]*benchmarkTCPPreludeReadFixture, benchmarkFixtureRingSize)
for i := range fixtures {
decoys := [][]byte{{0xaa}}
if i%2 == 1 {
decoys = [][]byte{{0xaa}, {0xab}, {0xac}, {0xad}, {0xae}}
}
fixtures[i] = newBenchmarkTCPPreludeReadFixture(decoys)
}
benchmarkRunLoopWithFixtureRing(b, len(benchmarkPayloads.initiation), fixtures, func(f *benchmarkTCPPreludeReadFixture) {
f.Reset()
}, func(f *benchmarkTCPPreludeReadFixture) error {
return f.Read()
})
})
b.Run("ReadHot/transport_small", func(b *testing.B) {
pool := benchmarkNewBufferPool()
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(
benchmarkMasqueradeRules,
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.transportSmall),
))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
prelude.seenValid = true
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(benchmarkPayloads.transportSmall), nil, func() error {
_, err := prelude.Read(buf)
return err
})
})
b.Run("Write/initiation_mixed", func(b *testing.B) {
fixtures := make([]*benchmarkTCPPreludeWriteFixture, benchmarkFixtureRingSize)
for i := range fixtures {
opts := benchmarkPreludeOneRule
if i%2 == 1 {
opts = benchmarkPreludeFiveRules
}
fixtures[i] = newBenchmarkTCPPreludeWriteFixture(
opts,
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation),
)
}
benchmarkRunLoopWithFixtureRing(b, len(benchmarkPayloads.initiation), fixtures, nil, func(f *benchmarkTCPPreludeWriteFixture) error {
return f.Write()
})
})
b.Run("Write/transport_small/passthrough", func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
recordConn, ok := NewMasqueradeConn(sink, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
payload := benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.transportSmall)
benchmarkRunLoop(b, len(payload), nil, func() error {
_, err := prelude.Write(payload)
return err
})
})
}
func benchmarkTCPPreludeFull(b *testing.B) {
b.Run("ReadCold/decoy1_then_initiation", func(b *testing.B) {
pool := benchmarkNewBufferPool()
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(
benchmarkMasqueradeRules,
[]byte{0xaa},
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation),
))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
reset := func() {
source.ResetRead()
prelude.seenValid = false
}
benchmarkRunLoop(b, len(benchmarkPayloads.initiation), reset, func() error {
_, err := prelude.Read(buf)
return err
})
})
b.Run("ReadCold/decoy5_then_initiation", func(b *testing.B) {
pool := benchmarkNewBufferPool()
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(
benchmarkMasqueradeRules,
[]byte{0xaa},
[]byte{0xab},
[]byte{0xac},
[]byte{0xad},
[]byte{0xae},
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation),
))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
reset := func() {
source.ResetRead()
prelude.seenValid = false
}
benchmarkRunLoop(b, len(benchmarkPayloads.initiation), reset, func() error {
_, err := prelude.Read(buf)
return err
})
})
b.Run("ReadHot/transport_small", func(b *testing.B) {
pool := benchmarkNewBufferPool()
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(
benchmarkMasqueradeRules,
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.transportSmall),
))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
prelude.seenValid = true
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(benchmarkPayloads.transportSmall), nil, func() error {
_, err := prelude.Read(buf)
return err
})
})
writeCases := []struct {
name string
payload []byte
opts PreludeOpts
}{
{
name: "Write/initiation/1_rule",
payload: benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation),
opts: benchmarkPreludeOneRule,
},
{
name: "Write/initiation/5_rules",
payload: benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation),
opts: benchmarkPreludeFiveRules,
},
{
name: "Write/transport_small/passthrough",
payload: benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.transportSmall),
opts: benchmarkPreludeOneRule,
},
}
for _, tc := range writeCases {
b.Run(tc.name, func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
recordConn, ok := NewMasqueradeConn(sink, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, tc.opts)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := prelude.Write(tc.payload)
return err
})
})
}
}
func BenchmarkTCPPipeline(b *testing.B) {
benchmarkUseDeterministicRand(b)
if benchmarkFullMatrixEnabled() {
benchmarkTCPPipelineFull(b)
return
}
b.Run("Read/full_pipeline/cold_initiation", func(b *testing.B) {
fixtures := make([]*benchmarkTCPPipelineReadFixture, benchmarkFixtureRingSize)
for i := range fixtures {
fixtures[i] = newBenchmarkTCPPipelineReadFixture([][]byte{{0xaa, 0xbb}})
}
benchmarkRunLoopWithFixtureRing(b, len(benchmarkPayloads.initiation), fixtures, func(f *benchmarkTCPPipelineReadFixture) {
f.Reset()
}, func(f *benchmarkTCPPipelineReadFixture) error {
return f.Read()
})
})
b.Run("Read/full_pipeline/hot_transport_small", func(b *testing.B) {
pool := benchmarkNewBufferPool()
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(
benchmarkMasqueradeRules,
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.transportSmall),
))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
prelude.seenValid = true
conn, ok := NewFramedConn(prelude, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected pipeline benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(benchmarkPayloads.transportSmall), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
writePayloads := [][]byte{
benchmarkPayloads.initiation,
benchmarkPayloads.transportSmall,
benchmarkPayloads.transportMTU,
}
b.Run("Write/full_pipeline/mixed", func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
recordConn, ok := NewMasqueradeConn(sink, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
conn, ok := NewFramedConn(prelude, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected pipeline benchmark conn")
}
next := 0
benchmarkRunLoop(b, benchmarkAverageBytes(writePayloads...), nil, func() error {
payload := writePayloads[next]
next++
if next == len(writePayloads) {
next = 0
}
_, err := conn.Write(payload)
return err
})
})
}
func benchmarkTCPPipelineFull(b *testing.B) {
b.Run("Read/full_pipeline/cold_initiation", func(b *testing.B) {
pool := benchmarkNewBufferPool()
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(
benchmarkMasqueradeRules,
[]byte{0xaa, 0xbb},
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation),
))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
conn, ok := NewFramedConn(prelude, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected pipeline benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
reset := func() {
source.ResetRead()
prelude.seenValid = false
}
benchmarkRunLoop(b, len(benchmarkPayloads.initiation), reset, func() error {
_, err := conn.Read(buf)
return err
})
})
b.Run("Read/full_pipeline/hot_transport_small", func(b *testing.B) {
pool := benchmarkNewBufferPool()
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(
benchmarkMasqueradeRules,
benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.transportSmall),
))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
prelude.seenValid = true
conn, ok := NewFramedConn(prelude, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected pipeline benchmark conn")
}
buf := make([]byte, benchmarkMaxPacketSize)
benchmarkRunLoop(b, len(benchmarkPayloads.transportSmall), nil, func() error {
_, err := conn.Read(buf)
return err
})
})
writeCases := []struct {
name string
payload []byte
}{
{name: "Write/full_pipeline/initiation", payload: benchmarkPayloads.initiation},
{name: "Write/full_pipeline/transport_small", payload: benchmarkPayloads.transportSmall},
{name: "Write/full_pipeline/transport_mtu", payload: benchmarkPayloads.transportMTU},
}
for _, tc := range writeCases {
b.Run(tc.name, func(b *testing.B) {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
recordConn, ok := NewMasqueradeConn(sink, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
b.Fatal("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
b.Fatal("expected prelude benchmark conn")
}
conn, ok := NewFramedConn(prelude, pool, benchmarkFramedOpts)
if !ok {
b.Fatal("expected pipeline benchmark conn")
}
benchmarkRunLoop(b, len(tc.payload), nil, func() error {
_, err := conn.Write(tc.payload)
return err
})
})
}
}
type benchmarkTCPPreludeReadFixture struct {
source *benchmarkStreamConn
prelude *PreludeConn
buf []byte
}
func newBenchmarkTCPPreludeReadFixture(decoys [][]byte) *benchmarkTCPPreludeReadFixture {
pool := benchmarkNewBufferPool()
records := make([][]byte, 0, len(decoys)+1)
records = append(records, decoys...)
records = append(records, benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation))
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(benchmarkMasqueradeRules, records...))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
panic("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
panic("expected prelude benchmark conn")
}
return &benchmarkTCPPreludeReadFixture{
source: source,
prelude: prelude,
buf: make([]byte, benchmarkMaxPacketSize),
}
}
func (f *benchmarkTCPPreludeReadFixture) Reset() {
f.source.ResetRead()
f.prelude.seenValid = false
}
func (f *benchmarkTCPPreludeReadFixture) Read() error {
_, err := f.prelude.Read(f.buf)
return err
}
type benchmarkTCPPreludeWriteFixture struct {
prelude *PreludeConn
payload []byte
}
func newBenchmarkTCPPreludeWriteFixture(opts PreludeOpts, payload []byte) *benchmarkTCPPreludeWriteFixture {
sink := newBenchmarkStreamConn(nil)
pool := benchmarkNewBufferPool()
recordConn, ok := NewMasqueradeConn(sink, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
panic("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, opts)
if !ok {
panic("expected prelude benchmark conn")
}
return &benchmarkTCPPreludeWriteFixture{
prelude: prelude,
payload: payload,
}
}
func (f *benchmarkTCPPreludeWriteFixture) Write() error {
_, err := f.prelude.Write(f.payload)
return err
}
type benchmarkTCPPipelineReadFixture struct {
source *benchmarkStreamConn
prelude *PreludeConn
conn *FramedConn
buf []byte
}
func newBenchmarkTCPPipelineReadFixture(decoys [][]byte) *benchmarkTCPPipelineReadFixture {
pool := benchmarkNewBufferPool()
records := make([][]byte, 0, len(decoys)+1)
records = append(records, decoys...)
records = append(records, benchmarkEncodeFramedRecord(benchmarkFramedOpts, benchmarkPayloads.initiation))
source := newBenchmarkStreamConn(benchmarkEncodeStreamRecords(benchmarkMasqueradeRules, records...))
recordConn, ok := NewMasqueradeConn(source, pool, MasqueradeOpts{
RulesIn: benchmarkMasqueradeRules,
RulesOut: benchmarkMasqueradeRules,
})
if !ok {
panic("expected record benchmark conn")
}
prelude, ok := NewPreludeConn(recordConn, pool, benchmarkFramedOpts, benchmarkPreludeOneRule)
if !ok {
panic("expected prelude benchmark conn")
}
conn, ok := NewFramedConn(prelude, pool, benchmarkFramedOpts)
if !ok {
panic("expected pipeline benchmark conn")
}
return &benchmarkTCPPipelineReadFixture{
source: source,
prelude: prelude,
conn: conn,
buf: make([]byte, benchmarkMaxPacketSize),
}
}
func (f *benchmarkTCPPipelineReadFixture) Reset() {
f.source.ResetRead()
f.prelude.seenValid = false
}
func (f *benchmarkTCPPipelineReadFixture) Read() error {
_, err := f.conn.Read(f.buf)
return err
}
File diff suppressed because it is too large Load Diff
+620
View File
@@ -0,0 +1,620 @@
package conn
import (
"encoding/binary"
"io"
"net"
"net/netip"
"syscall"
"testing"
"time"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
const benchmarkConnMaxPacketSize = 65535
func BenchmarkConnEndpointDstToBytes(b *testing.B) {
cases := []struct {
name string
ep Endpoint
}{
{
name: "StdNetEndpoint/IPv4",
ep: &StdNetEndpoint{
AddrPort: netip.MustParseAddrPort("127.0.0.1:51820"),
},
},
{
name: "StdNetEndpoint/IPv6",
ep: &StdNetEndpoint{
AddrPort: netip.MustParseAddrPort("[2001:db8::1]:51820"),
},
},
{
name: "streamEndpoint/IPv4",
ep: &streamEndpoint{
dst: netip.MustParseAddrPort("127.0.0.1:51820"),
},
},
{
name: "streamEndpoint/IPv6",
ep: &streamEndpoint{
dst: netip.MustParseAddrPort("[2001:db8::1]:51820"),
},
},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = tc.ep.DstToBytes()
}
})
}
}
func BenchmarkConnCoalesceMessages(b *testing.B) {
addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 51820}
ep := &StdNetEndpoint{
AddrPort: addr.AddrPort(),
src: make([]byte, stickyControlSize),
}
cases := []struct {
name string
bufs [][]byte
}{
{
name: "batch8/equal_transport",
bufs: benchmarkConnEqualPayloads(8, 256),
},
{
name: "batch8/mixed_transport",
bufs: benchmarkConnMixedPayloads(8),
},
{
name: "batch64/equal_transport",
bufs: benchmarkConnEqualPayloads(64, 256),
},
{
name: "batch64/mixed_transport",
bufs: benchmarkConnMixedPayloads(64),
},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
msgs := benchmarkConnMessages(len(tc.bufs), 2)
benchmarkConnRunLoop(b, benchmarkConnTotalBytes(tc.bufs), nil, func() error {
_ = coalesceMessages(addr, ep, tc.bufs, msgs, benchmarkConnSetGSOSize)
return nil
})
})
}
}
func BenchmarkConnSplitCoalescedMessages(b *testing.B) {
cases := []struct {
name string
build func() []ipv6.Message
firstMsgAt int
bytesPerOp int
reset func([]ipv6.Message)
}{
{
name: "gso0",
build: func() []ipv6.Message { return benchmarkConnSplitMessages(1, 256, 0, 0) },
firstMsgAt: 0,
bytesPerOp: 256,
},
{
name: "gso16_8msgs",
build: func() []ipv6.Message { return benchmarkConnSplitMessages(8, 16, 16, 7) },
firstMsgAt: 7,
bytesPerOp: 8 * 16,
reset: func(msgs []ipv6.Message) {
for i := 0; i < len(msgs)-1; i++ {
msgs[i].N = 0
msgs[i].NN = 0
}
msgs[7].N = 8 * 16
msgs[7].NN = 2
},
},
{
name: "gso128_64msgs",
build: func() []ipv6.Message { return benchmarkConnSplitMessages(64, 128, 128, 63) },
firstMsgAt: 63,
bytesPerOp: 64 * 128,
reset: func(msgs []ipv6.Message) {
for i := 0; i < len(msgs)-1; i++ {
msgs[i].N = 0
msgs[i].NN = 0
}
msgs[63].N = 64 * 128
msgs[63].NN = 2
},
},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
if tc.reset == nil {
msgs := tc.build()
benchmarkConnRunLoop(b, tc.bytesPerOp, nil, func() error {
_, err := splitCoalescedMessages(msgs, tc.firstMsgAt, benchmarkConnGetGSOSize)
return err
})
return
}
const fixtureRingSize = 256
fixtures := make([][]ipv6.Message, fixtureRingSize)
for i := range fixtures {
fixtures[i] = tc.build()
}
b.ReportAllocs()
if tc.bytesPerOp > 0 {
b.SetBytes(int64(tc.bytesPerOp))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i > 0 && i%fixtureRingSize == 0 {
b.StopTimer()
for j := range fixtures {
tc.reset(fixtures[j])
}
b.StartTimer()
}
_, err := splitCoalescedMessages(fixtures[i%fixtureRingSize], tc.firstMsgAt, benchmarkConnGetGSOSize)
if err != nil {
b.Fatal(err)
}
}
})
}
}
func BenchmarkConnStdNetBindSend(b *testing.B) {
cases := []struct {
name string
batchLen int
offload bool
}{
{name: "gso_off/batch1", batchLen: 1, offload: false},
{name: "gso_off/batch8", batchLen: 8, offload: false},
{name: "gso_on/batch8", batchLen: 8, offload: true},
{name: "gso_on/batch64", batchLen: 64, offload: true},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
bind := NewStdNetBind().(*StdNetBind)
bind.ipv4 = &benchmarkConnUDPConn{}
bind.ipv4PC = &benchmarkConnLinuxPacketConn{}
bind.ipv4TxOffload = tc.offload
endpoint := &StdNetEndpoint{
AddrPort: netip.MustParseAddrPort("127.0.0.1:51820"),
src: make([]byte, stickyControlSize),
}
if !tc.offload {
bufs := benchmarkConnSendPayloads(tc.batchLen, 256, false)
benchmarkConnRunLoop(b, benchmarkConnTotalBytes(bufs), nil, func() error {
return bind.Send(bufs, endpoint)
})
return
}
fixtures := make([]benchmarkConnSendFixture, benchmarkConnFixtureRingSize)
for i := range fixtures {
fixtures[i] = newBenchmarkConnSendFixture(tc.batchLen, 256, true)
}
benchmarkConnRunLoopWithFixtureRing(b, benchmarkConnTotalBytes(fixtures[0].bufs), fixtures, func(f benchmarkConnSendFixture) {
benchmarkConnResetPayloadLens(f.bufs, f.lens)
}, func(f benchmarkConnSendFixture) error {
return bind.Send(f.bufs, endpoint)
})
})
}
}
func BenchmarkConnStdNetBindReceive(b *testing.B) {
cases := []struct {
name string
batchSize int
rxOffload bool
reader *benchmarkConnLinuxPacketConn
conn *benchmarkConnUDPConn
}{
{
name: "single",
batchSize: 1,
rxOffload: false,
reader: &benchmarkConnLinuxPacketConn{},
conn: &benchmarkConnUDPConn{
readPayloads: [][]byte{benchmarkConnPayload(256)},
},
},
{
name: "batch",
batchSize: 8,
rxOffload: false,
reader: &benchmarkConnLinuxPacketConn{
readBatches: [][]benchmarkConnPacket{benchmarkConnBatchPackets(8, 256)},
},
conn: &benchmarkConnUDPConn{
readPayloads: [][]byte{benchmarkConnPayload(256)},
},
},
{
name: "batch_rx_offload",
batchSize: 64,
rxOffload: true,
reader: &benchmarkConnLinuxPacketConn{
readBatches: [][]benchmarkConnPacket{benchmarkConnCoalescedBatch(64, 256)},
},
conn: &benchmarkConnUDPConn{
readPayloads: [][]byte{benchmarkConnPayload(256)},
},
},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
bind := NewStdNetBind().(*StdNetBind)
bufs := make([][]byte, tc.batchSize)
for i := range bufs {
bufs[i] = make([]byte, benchmarkConnMaxPacketSize)
}
sizes := make([]int, tc.batchSize)
eps := make([]Endpoint, tc.batchSize)
benchmarkConnRunLoop(b, tc.batchSize*256, nil, func() error {
_, err := bind.receiveIP(tc.reader, tc.conn, tc.rxOffload, bufs, sizes, eps)
return err
})
})
}
}
func benchmarkConnRunLoop(b *testing.B, bytesPerOp int, reset func(), op func() error) {
b.Helper()
b.ReportAllocs()
if bytesPerOp > 0 {
b.SetBytes(int64(bytesPerOp))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i > 0 && reset != nil {
b.StopTimer()
reset()
b.StartTimer()
}
if err := op(); err != nil {
b.Fatal(err)
}
}
}
func benchmarkConnRunLoopWithFixtureRing[T any](b *testing.B, bytesPerOp int, fixtures []T, reset func(T), op func(T) error) {
b.Helper()
if len(fixtures) == 0 {
b.Fatal("benchmark fixture ring must not be empty")
}
b.ReportAllocs()
if bytesPerOp > 0 {
b.SetBytes(int64(bytesPerOp))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i > 0 && i%len(fixtures) == 0 && reset != nil {
b.StopTimer()
for _, fixture := range fixtures {
reset(fixture)
}
b.StartTimer()
}
if err := op(fixtures[i%len(fixtures)]); err != nil {
b.Fatal(err)
}
}
}
func benchmarkConnPayload(size int) []byte {
payload := make([]byte, size)
for i := range payload {
payload[i] = byte(i)
}
return payload
}
func benchmarkConnEqualPayloads(count, size int) [][]byte {
bufs := make([][]byte, count)
for i := range bufs {
capacity := size
if i == 0 {
capacity = size * count
}
buf := make([]byte, size, capacity)
for j := range buf {
buf[j] = byte(j)
}
bufs[i] = buf
}
return bufs
}
func benchmarkConnMixedPayloads(count int) [][]byte {
bufs := make([][]byte, count)
for i := range bufs {
size := 256
if i%2 == 1 {
size = 1280
}
capacity := size
if i == 0 {
capacity = size * count
}
buf := make([]byte, size, capacity)
for j := range buf {
buf[j] = byte(j)
}
bufs[i] = buf
}
return bufs
}
func benchmarkConnSendPayloads(count, size int, offload bool) [][]byte {
bufs := make([][]byte, count)
for i := range bufs {
capacity := size
if offload && i == 0 {
capacity = size * count
}
buf := make([]byte, size, capacity)
for j := range buf {
buf[j] = byte(j)
}
bufs[i] = buf
}
return bufs
}
func benchmarkConnLengths(bufs [][]byte) []int {
lens := make([]int, len(bufs))
for i := range bufs {
lens[i] = len(bufs[i])
}
return lens
}
func benchmarkConnResetPayloadLens(bufs [][]byte, lens []int) {
for i := range bufs {
bufs[i] = bufs[i][:lens[i]]
}
}
const benchmarkConnFixtureRingSize = 256
type benchmarkConnSendFixture struct {
bufs [][]byte
lens []int
}
func newBenchmarkConnSendFixture(count, size int, offload bool) benchmarkConnSendFixture {
bufs := benchmarkConnSendPayloads(count, size, offload)
return benchmarkConnSendFixture{
bufs: bufs,
lens: benchmarkConnLengths(bufs),
}
}
func benchmarkConnTotalBytes(bufs [][]byte) int {
total := 0
for _, buf := range bufs {
total += len(buf)
}
return total
}
func benchmarkConnMessages(count int, oobCap int) []ipv6.Message {
msgs := make([]ipv6.Message, count)
for i := range msgs {
msgs[i].Buffers = make([][]byte, 1)
msgs[i].OOB = make([]byte, 0, oobCap)
}
return msgs
}
func benchmarkConnResetMessages(msgs []ipv6.Message) {
for i := range msgs {
msgs[i].Addr = nil
msgs[i].N = 0
msgs[i].NN = 0
msgs[i].OOB = msgs[i].OOB[:0]
msgs[i].Buffers[0] = nil
}
}
func benchmarkConnSetGSOSize(control *[]byte, gsoSize uint16) {
*control = (*control)[:cap(*control)]
binary.LittleEndian.PutUint16(*control, gsoSize)
}
func benchmarkConnGetGSOSize(control []byte) (int, error) {
if len(control) < 2 {
return 0, nil
}
return int(binary.LittleEndian.Uint16(control)), nil
}
func benchmarkConnSplitMessages(numMsgs, segmentSize, gsoSize, sourceIndex int) []ipv6.Message {
msgs := make([]ipv6.Message, numMsgs)
for i := range msgs {
bufSize := segmentSize
if i == sourceIndex {
bufSize = numMsgs * segmentSize
}
msgs[i].Buffers = [][]byte{make([]byte, bufSize)}
msgs[i].OOB = make([]byte, 0, 2)
}
for i := 0; i < numMsgs*segmentSize; i++ {
msgs[sourceIndex].Buffers[0][i] = byte(i)
}
msgs[sourceIndex].N = numMsgs * segmentSize
if gsoSize > 0 {
msgs[sourceIndex].OOB = msgs[sourceIndex].OOB[:2]
binary.LittleEndian.PutUint16(msgs[sourceIndex].OOB, uint16(gsoSize))
msgs[sourceIndex].NN = 2
}
return msgs
}
type benchmarkConnPacket struct {
payload []byte
oob []byte
}
func benchmarkConnBatchPackets(count, size int) []benchmarkConnPacket {
packets := make([]benchmarkConnPacket, count)
for i := range packets {
packets[i].payload = benchmarkConnPayload(size)
}
return packets
}
func benchmarkConnCoalescedBatch(count, segmentSize int) []benchmarkConnPacket {
payload := make([]byte, count*segmentSize)
for i := range payload {
payload[i] = byte(i)
}
oob := make([]byte, 2)
binary.LittleEndian.PutUint16(oob, uint16(segmentSize))
return []benchmarkConnPacket{
{
payload: payload,
oob: oob,
},
}
}
type benchmarkConnLinuxPacketConn struct {
readBatches [][]benchmarkConnPacket
readIndex int
}
func (c *benchmarkConnLinuxPacketConn) ResetRead() {
c.readIndex = 0
}
func (c *benchmarkConnLinuxPacketConn) ReadBatch(ms []ipv4.Message, flags int) (int, error) {
if len(c.readBatches) == 0 {
return 0, io.EOF
}
batch := c.readBatches[c.readIndex]
c.readIndex++
if c.readIndex == len(c.readBatches) {
c.readIndex = 0
}
for i := range batch {
msg := &ms[i]
packet := batch[i]
msg.N = copy(msg.Buffers[0], packet.payload)
if cap(msg.OOB) < len(packet.oob) {
msg.OOB = make([]byte, len(packet.oob))
}
msg.OOB = msg.OOB[:len(packet.oob)]
copy(msg.OOB, packet.oob)
msg.NN = len(packet.oob)
msg.Addr = &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 51820,
}
}
for i := len(batch); i < len(ms); i++ {
ms[i].N = 0
ms[i].NN = 0
ms[i].Addr = nil
ms[i].OOB = ms[i].OOB[:0]
}
return len(batch), nil
}
func (c *benchmarkConnLinuxPacketConn) WriteBatch(ms []ipv4.Message, flags int) (int, error) {
return len(ms), nil
}
type benchmarkConnUDPConn struct {
readPayloads [][]byte
readIndex int
}
func (c *benchmarkConnUDPConn) ResetRead() {
c.readIndex = 0
}
func (c *benchmarkConnUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
n, _, _, addr, err := c.ReadMsgUDP(p, nil)
return n, addr, err
}
func (c *benchmarkConnUDPConn) WriteTo(p []byte, addr net.Addr) (int, error) {
udpAddr, _ := addr.(*net.UDPAddr)
n, _, err := c.WriteMsgUDP(p, nil, udpAddr)
return n, err
}
func (c *benchmarkConnUDPConn) Close() error {
return nil
}
func (c *benchmarkConnUDPConn) LocalAddr() net.Addr {
return &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 51820,
}
}
func (c *benchmarkConnUDPConn) SetDeadline(time.Time) error {
return nil
}
func (c *benchmarkConnUDPConn) SetReadDeadline(time.Time) error {
return nil
}
func (c *benchmarkConnUDPConn) SetWriteDeadline(time.Time) error {
return nil
}
func (c *benchmarkConnUDPConn) SyscallConn() (syscall.RawConn, error) {
return nil, nil
}
func (c *benchmarkConnUDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
if len(c.readPayloads) == 0 {
return 0, 0, 0, nil, io.EOF
}
payload := c.readPayloads[c.readIndex]
c.readIndex++
if c.readIndex == len(c.readPayloads) {
c.readIndex = 0
}
n = copy(b, payload)
return n, 0, 0, &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 51820,
}, nil
}
func (c *benchmarkConnUDPConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (int, int, error) {
return len(b), len(oob), nil
}