Commit the receive counters once per batch instead of once per frame

This commit is contained in:
flamingcow
2026-08-04 21:25:04 -07:00
parent 21788fdd4f
commit 49de171ad0
2 changed files with 59 additions and 17 deletions
+39 -7
View File
@@ -39,21 +39,46 @@ type rxBucket struct {
// Only the owning worker writes its own buckets, so a slot coming round again is
// simply zeroed before it is claimed for the new epoch.
func (s *rxStats) observe(stamp int64, n uint64) {
e := stamp / rateBucketNs
func (s *rxStats) commit(e int64, frames, bytes uint64) {
b := &s.buckets[e&(rateBuckets-1)]
if b.epoch.Load() != e {
b.frames.Store(0)
b.bytes.Store(0)
b.epoch.Store(e)
}
b.frames.Add(1)
b.bytes.Add(n)
b.frames.Add(frames)
b.bytes.Add(bytes)
if e > s.newest.Load() {
s.newest.Store(e)
}
}
// Frames drained together that fell in the same epoch, so the buckets take one
// pair of adds per epoch a batch spans rather than one per frame.
type rateRun struct {
stats *rxStats
epoch int64
frames uint64
bytes uint64
}
func (r *rateRun) add(stamp int64, n uint64) {
if e := stamp / rateBucketNs; e != r.epoch {
r.flush()
r.epoch = e
}
r.frames++
r.bytes += n
}
func (r *rateRun) flush() {
if r.frames == 0 {
return
}
r.stats.commit(r.epoch, r.frames, r.bytes)
r.frames, r.bytes = 0, 0
}
// What this worker counted into one epoch, or nothing if that epoch has already
// fallen out of the ring.
func (s *rxStats) bucket(e int64) (frames, bytes uint64) {
@@ -83,6 +108,7 @@ func (w *rxWorker) run(done *atomic.Bool) {
}
}
hdrs, oob := newRxMmsghdrs(bufs)
run := rateRun{stats: w.stats}
w.ready.Done()
@@ -99,6 +125,7 @@ func (w *rxWorker) run(done *atomic.Bool) {
}
continue
}
var frames, bytes uint64
for i := 0; i < n; i++ {
buf := bufs[i][:int(hdrs[i].len)]
p, st := parseHeader(buf)
@@ -110,10 +137,10 @@ func (w *rxWorker) run(done *atomic.Bool) {
}
continue
}
w.stats.frames.Add(1)
w.stats.bytes.Add(uint64(len(buf)))
frames++
bytes += uint64(len(buf))
if ts, ok := hwTimestamp(oob[i][:hdrs[i].hdr.Controllen]); ok {
w.stats.observe(ts, uint64(len(buf)))
run.add(ts, uint64(len(buf)))
}
// The ethertype this socket is bound to already says which stream the
@@ -134,5 +161,10 @@ func (w *rxWorker) run(done *atomic.Bool) {
w.stats.crcErr.Add(1)
}
}
// Nothing reads these between frames, and they share a cache line, so a
// batch commits once rather than locking the line for every frame.
w.stats.frames.Add(frames)
w.stats.bytes.Add(bytes)
run.flush()
}
}