| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- package batch
- import (
- "testing"
- "github.com/slackhq/nebula/test"
- )
- // stagePackets builds the stagedPacket entries Commit would have produced, so dispatch benchmarks
- // bypass staging and the sort entirely.
- func stagePackets(pkts [][]byte) []stagedPacket {
- staged := make([]stagedPacket, len(pkts))
- for i, p := range pkts {
- pp := testPP(p)
- staged[i] = stagedPacket{
- pkt: p,
- key: SortKey{Epoch: 1, Counter: uint64(i + 1)},
- proto: pp.Protocol,
- fragAny: pp.FragAny,
- ipHdrLen: uint16(pp.IPHdrLen),
- }
- }
- return staged
- }
- func flushLanes(b *testing.B, m *MultiCoalescer) {
- b.Helper()
- if m.tcp != nil {
- if err := m.tcp.Flush(); err != nil {
- b.Fatal(err)
- }
- }
- if m.udp != nil {
- if err := m.udp.Flush(); err != nil {
- b.Fatal(err)
- }
- }
- if err := m.pt.Flush(); err != nil {
- b.Fatal(err)
- }
- }
- // runDispatchBench measures dispatch plus the per-batch lane flush: the post-sort half of the
- // batcher, which is where the production profile concentrates.
- func runDispatchBench(b *testing.B, pkts [][]byte, batchSize int) {
- b.Helper()
- m := NewMultiCoalescer(nopTunWriter{}, test.NewLogger())
- staged := stagePackets(pkts)
- b.ReportAllocs()
- b.SetBytes(int64(len(pkts[0])))
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- if err := m.dispatch(staged[i%len(staged)]); err != nil {
- b.Fatal(err)
- }
- if (i+1)%batchSize == 0 {
- flushLanes(b, m)
- }
- }
- b.StopTimer()
- flushLanes(b, m)
- }
- // BenchmarkDispatchSingleFlow is the bulk steady state: every packet past the seed appends.
- func BenchmarkDispatchSingleFlow(b *testing.B) {
- runDispatchBench(b, buildTCPv4BulkFlow(tcpCoalesceMaxSegs, 1200), tcpCoalesceMaxSegs)
- }
- // BenchmarkDispatchInterleaved16 stresses the openSlots map: 16 flows round-robined defeats the
- // lastSlot cache on every packet.
- func BenchmarkDispatchInterleaved16(b *testing.B) {
- pkts := buildTCPv4Interleaved(16, tcpCoalesceMaxSegs, 1200)
- runDispatchBench(b, pkts, len(pkts))
- }
- // BenchmarkDispatchAckHeavy alternates MSS data with pure ACKs on one flow — the RX shape of a
- // bidirectional transfer (the peer's data and its ACKs of our data share the tunnel direction).
- func BenchmarkDispatchAckHeavy(b *testing.B) {
- pay := make([]byte, 1200)
- var pkts [][]byte
- seq := uint32(1000)
- for range tcpCoalesceMaxSegs / 2 {
- pkts = append(pkts, buildTCPv4(seq, tcpAck, pay))
- seq += uint32(len(pay))
- pkts = append(pkts, buildTCPv4(seq, tcpAck, nil))
- }
- runDispatchBench(b, pkts, len(pkts))
- }
- // BenchmarkDispatchUDPFlow is the QUIC-ish bulk UDP shape.
- func BenchmarkDispatchUDPFlow(b *testing.B) {
- pay := make([]byte, 1200)
- pkts := make([][]byte, udpCoalesceMaxSegs)
- for i := range pkts {
- pkts[i] = buildUDPv4(2000, 443, pay)
- }
- runDispatchBench(b, pkts, len(pkts))
- }
- // BenchmarkDispatchSeedHeavy sets PSH on every packet so each one seeds and immediately closes
- // its own slot — the small-write RPC shape, and the upper bound on what the seed path (including
- // the parsedTCP-to-slot field transfer) can cost.
- func BenchmarkDispatchSeedHeavy(b *testing.B) {
- pay := make([]byte, 1200)
- pkts := make([][]byte, tcpCoalesceMaxSegs)
- seq := uint32(1000)
- for i := range pkts {
- pkts[i] = buildTCPv4(seq, tcpAckPsh, pay)
- seq += uint32(len(pay))
- }
- runDispatchBench(b, pkts, len(pkts))
- }
|