From 2035d42734929b4f4d66ec549ebbab44ddf67b7b Mon Sep 17 00:00:00 2001 From: flamingcow Date: Tue, 4 Aug 2026 13:37:19 -0700 Subject: [PATCH] Fold the leftover formatters into the SI ones and stop reallocating the draw scratch --- .gitignore | 1 - fb.go | 46 ++++++++++++++++++++++++---------------------- main.go | 28 ++++++++++++++-------------- main_test.go | 4 ++-- probe.go | 2 +- render.go | 19 ------------------- ui.go | 2 +- 7 files changed, 42 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index fc46f00..75b7bc1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -/cabletest /shots/ diff --git a/fb.go b/fb.go index 3c6b832..673566e 100644 --- a/fb.go +++ b/fb.go @@ -32,13 +32,14 @@ type scanout struct { // w and h are the logical canvas, which is portrait; pw and ph are the panel, // which is landscape. type framebuffer struct { - fd int - back []byte - w int - h int - pw int - ph int - stride int + fd int + back []byte + scratch []byte + w int + h int + pw int + ph int + stride int bufs [scanoutBuffers]scanout front int @@ -252,6 +253,7 @@ func openFramebuffer() (*framebuffer, error) { } } fb.back = make([]byte, fb.stride*fb.ph) + fb.scratch = make([]byte, fb.stride) set := drm.ModeCrtc{ SetConnectorsPtr: uint64(uintptr(unsafe.Pointer(&fb.connID))), @@ -316,15 +318,22 @@ type rgb struct { func pixel(c rgb) uint32 { return drm.Pack(c.r, c.g, c.b) } -func (fb *framebuffer) fill(c rgb) { +// A run of one repeated pixel, reused between calls: a whole panel row is the +// longest anything here needs, and every draw is on the one render goroutine. +func (fb *framebuffer) pixelRun(n int, c rgb) []byte { v := pixel(c) - row := make([]byte, fb.stride) - for x := 0; x+4 <= fb.stride; x += 4 { - row[x+0] = byte(v) - row[x+1] = byte(v >> 8) - row[x+2] = byte(v >> 16) - row[x+3] = byte(v >> 24) + s := fb.scratch[:n] + for i := 0; i+4 <= n; i += 4 { + s[i+0] = byte(v) + s[i+1] = byte(v >> 8) + s[i+2] = byte(v >> 16) + s[i+3] = byte(v >> 24) } + return s +} + +func (fb *framebuffer) fill(c rgb) { + row := fb.pixelRun(fb.stride, c) for y := 0; y < fb.ph; y++ { copy(fb.back[y*fb.stride:], row) } @@ -337,14 +346,7 @@ func (fb *framebuffer) rect(x0, y0, w, h int, c rgb) { if x0 >= x1 || y0 >= y1 { return } - v := pixel(c) - span := make([]byte, (y1-y0)*4) - for i := 0; i+4 <= len(span); i += 4 { - span[i+0] = byte(v) - span[i+1] = byte(v >> 8) - span[i+2] = byte(v >> 16) - span[i+3] = byte(v >> 24) - } + span := fb.pixelRun((y1-y0)*4, c) for x := x0; x < x1; x++ { copy(fb.back[fb.offset(x, y1-1):], span) } diff --git a/main.go b/main.go index 345a816..4efedcb 100644 --- a/main.go +++ b/main.go @@ -57,7 +57,7 @@ type direction struct { base counterSet heldFrames heldValue - heldSent heldValue + heldBytes heldValue nic atomic.Uint64 poller *nicPoller } @@ -253,7 +253,7 @@ func (d *direction) reset() { d.mu.Unlock() d.heldFrames = heldValue{} - d.heldSent = heldValue{} + d.heldBytes = heldValue{} d.cable.reset() } @@ -297,12 +297,12 @@ var intervalCols = []colSpec{ // Shared by the console table and the framebuffer so both show the same // figures. type view struct { - txPPS, rxPPS float64 - txGbps, rxGbps float64 - rxFrames, rxGot uint64 - since errs - window errs - cable cableView + txPPS, rxPPS float64 + txGbps, rxGbps float64 + rxFrames, rxBytes uint64 + since errs + window errs + cable cableView } func errsBetween(b, n counterSet) errs { @@ -326,7 +326,7 @@ func errsBetween(b, n counterSet) errs { func (d *direction) counters(now counterSet) view { return view{ rxFrames: now.s.rxFrames - d.base.s.rxFrames, - rxGot: now.s.rxBytes - d.base.s.rxBytes, + rxBytes: now.s.rxBytes - d.base.s.rxBytes, cable: d.cable.view(), since: errsBetween(d.base, now), } @@ -340,7 +340,7 @@ func totalView(views []view) view { t.txGbps += v.txGbps t.rxGbps += v.rxGbps t.rxFrames += v.rxFrames - t.rxGot += v.rxGot + t.rxBytes += v.rxBytes t.since = t.since.add(v.since) t.window = t.window.add(v.window) } @@ -393,7 +393,7 @@ func (d *direction) displayView(t time.Time) view { d.mu.Unlock() v.rxFrames = d.heldFrames.get(t, v.rxFrames) - v.rxGot = d.heldSent.get(t, v.rxGot) + v.rxBytes = d.heldBytes.get(t, v.rxBytes) return v } @@ -678,9 +678,9 @@ func run(aName, bName string, nsPerM float64) error { {"probe", fmt.Sprintf("ethertype 0x%04x every %s", probeEther, probeInterval)}, {"batch", fmt.Sprintf("%d frames per syscall", batchSize)}, {"calibration", fmt.Sprintf("%g ns/m, zero taken from the shortest delay seen so far", nsPerM)}, - {"buffers", fmt.Sprintf("sndbuf %s, rcvbuf %s", - humanBytes(uint64(sockBufSize(dirs[0].txFDs[0], unix.SO_SNDBUF))), - humanBytes(uint64(sockBufSize(dirs[0].rxFDs[0], unix.SO_RCVBUF))))}, + {"buffers", fmt.Sprintf("sndbuf %sB, rcvbuf %sB", + scaleCount(uint64(sockBufSize(dirs[0].txFDs[0], unix.SO_SNDBUF))), + scaleCount(uint64(sockBufSize(dirs[0].rxFDs[0], unix.SO_RCVBUF))))}, })) fmt.Println() diff --git a/main_test.go b/main_test.go index 37b1666..c1703aa 100644 --- a/main_test.go +++ b/main_test.go @@ -26,8 +26,8 @@ func TestResetDoesNotUnderflowTotals(t *testing.T) { if v.rxFrames != 0 { t.Errorf("rxFrames = %d, want 0", v.rxFrames) } - if v.rxGot != 0 { - t.Errorf("rxGot = %d, want 0", v.rxGot) + if v.rxBytes != 0 { + t.Errorf("rxBytes = %d, want 0", v.rxBytes) } // The rolling window and the rates are about now rather than since the diff --git a/probe.go b/probe.go index 11711cf..1c0a6b3 100644 --- a/probe.go +++ b/probe.go @@ -49,7 +49,7 @@ func (v cableView) minText() string { if !v.ok { return "-" } - return commasInt(v.min) + return commas(uint64(v.min)) } // Averaging the two directions cancels the phy asymmetry between them, which is diff --git a/render.go b/render.go index 84cbf67..1bf83c9 100644 --- a/render.go +++ b/render.go @@ -71,25 +71,6 @@ func commas(v uint64) string { return strings.Join(append([]string{s}, parts...), ",") } -func commasInt(v int64) string { - if v < 0 { - return "-" + commas(uint64(-v)) - } - return commas(uint64(v)) -} - -func humanBytes(b uint64) string { - const unit = 1000.0 - v := float64(b) - for _, suffix := range []string{"B", "kB", "MB", "GB", "TB"} { - if v < unit { - return fmt.Sprintf("%.1f %s", v, suffix) - } - v /= unit - } - return fmt.Sprintf("%.1f PB", v) -} - // The magnitude letter goes with the figure so the unit can stay a fixed word // on the label. Below a thousand no letter is left dangling, since a trailing // space would push the figure off centre. diff --git a/ui.go b/ui.go index 4ae5bfb..bd91fe8 100644 --- a/ui.go +++ b/ui.go @@ -344,7 +344,7 @@ func (d *display) render(v view, elapsed time.Duration, cable string) error { d.stats(d.gridB, x, w, d.sinceYs[0], []statCell{ {scaleTime(elapsed), "elapsed", uiFg}, {scaleCount(v.rxFrames), "packets", uiFg}, - {scaleCount(v.rxGot), "bytes", uiFg}, + {scaleCount(v.rxBytes), "bytes", uiFg}, {cable, "m", uiFg}, }) d.errCounts(x, w, d.sinceYs[1], v.since)