Always color output and fold settled values into constants

This commit is contained in:
flamingcow
2026-07-25 18:20:34 -07:00
parent 29c6ce0b56
commit 3ba68952bb
5 changed files with 29 additions and 79 deletions
+9 -37
View File
@@ -273,7 +273,7 @@ func buildDirection(label string, tx, rx endpoint, patIdx int, sizes []int, cfg
}
for i := 0; i < cfg.txWorkers; i++ {
fd, err := openTxSocket(tx.idx, cfg.sndbuf)
fd, err := openTxSocket(tx.idx)
if err != nil {
return nil, fmt.Errorf("%s tx socket: %w", label, err)
}
@@ -281,7 +281,7 @@ func buildDirection(label string, tx, rx endpoint, patIdx int, sizes []int, cfg
d.txStats = append(d.txStats, &txStats{})
}
for i := 0; i < cfg.rxWorkers; i++ {
fd, err := openRxSocket(rx.idx, cfg.rcvbuf, cfg.fanoutID, fm)
fd, err := openRxSocket(rx.idx, cfg.fanoutID, fm)
if err != nil {
return nil, fmt.Errorf("%s rx socket: %w", label, err)
}
@@ -313,7 +313,6 @@ func (d *direction) start(wg *sync.WaitGroup, doneTx, doneRx *atomic.Bool, cfg c
fd: fd,
batch: cfg.batch,
cpu: cpuAt(cfg.rxCPUs, i),
verify: cfg.verify,
spec: d.spec,
stats: d.rxStats[i],
streams: d.streams,
@@ -341,9 +340,6 @@ type config struct {
txWorkers int
rxWorkers int
batch int
verify bool
sndbuf int
rcvbuf int
fanout string
fanoutID int
txCPUs []int
@@ -360,46 +356,25 @@ func main() {
txN = flag.Int("tx", 4, "tx workers per direction")
rxN = flag.Int("rx", 4, "rx workers per direction")
batch = flag.Int("batch", 64, "frames per sendmmsg/recvmmsg call")
verify = flag.Bool("verify", true, "verify payload CRC32C on receive")
interval = flag.Duration("interval", time.Second, "report interval")
drain = flag.Duration("drain", 500*time.Millisecond, "keep receiving this long after tx stops, so in-flight frames are not counted as lost")
fanout = flag.String("fanout", "lb", "rx fanout mode: none, hash, lb, cpu, rollover")
duplex = flag.Bool("duplex", true, "run both directions simultaneously")
sndbuf = flag.Int("sndbuf", 8<<20, "SO_SNDBUFFORCE per tx socket")
rcvbuf = flag.Int("rcvbuf", 64<<20, "SO_RCVBUFFORCE per rx socket")
txCPUs = flag.String("txcpus", "", "comma-separated CPUs to pin tx workers to")
rxCPUs = flag.String("rxcpus", "", "comma-separated CPUs to pin rx workers to")
governor = flag.String("governor", "performance", "required cpufreq governor")
coalesce = flag.Uint("coalesce-usecs", 25, "required fixed rx/tx coalesce usecs, with adaptive coalescing off")
rxRing = flag.Uint("rx-ring", 8160, "required rx ring size, clamped to hardware maximum")
txRing = flag.Uint("tx-ring", 4096, "required tx ring size, clamped to hardware maximum")
color = flag.String("color", "auto", "colored output: auto, always, never")
)
flag.Parse()
if err := initColor(*color); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
tcfg := systemConfig{
governor: *governor,
rxUsecs: uint32(*coalesce),
txUsecs: uint32(*coalesce),
rxRing: uint32(*rxRing),
txRing: uint32(*txRing),
}
if err := run(*aName, *bName, *sizesArg, *patArg, *fanout, *txCPUs, *rxCPUs,
*duration, *interval, *drain, *txN, *rxN, *batch, *sndbuf, *rcvbuf, *verify, *duplex, tcfg); err != nil {
*duration, *interval, *txN, *rxN, *batch, *duplex); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
const drainTime = 500 * time.Millisecond
func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
duration, interval, drain time.Duration, txN, rxN, batch, sndbuf, rcvbuf int,
verify, duplex bool, tcfg systemConfig) error {
duration, interval time.Duration, txN, rxN, batch int, duplex bool) error {
if aName == "" || bName == "" {
return fmt.Errorf("both -a and -b are required")
@@ -441,7 +416,7 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
var fatal []string
var tuneRows [][]string
for _, r := range configureSystem(tcfg, ifnames) {
for _, r := range configureSystem(ifnames) {
tuneRows = append(tuneRows, []string{r.item, r.status(), r.detail()})
if r.fatal {
fatal = append(fatal, r.item)
@@ -458,9 +433,6 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
txWorkers: txN,
rxWorkers: rxN,
batch: batch,
verify: verify,
sndbuf: sndbuf,
rcvbuf: rcvbuf,
fanout: fanout,
txCPUs: txCPUs,
rxCPUs: rxCPUs,
@@ -516,7 +488,7 @@ func run(aName, bName, sizesArg, patArg, fanout, txCPUsArg, rxCPUsArg string,
{"ethertype", fmt.Sprintf("0x%04x", etherType)},
{"workers", fmt.Sprintf("%d tx, %d rx per direction", txN, rxN)},
{"batch", fmt.Sprintf("%d frames per syscall", batch)},
{"payload verify", fmt.Sprintf("%v", verify)},
{"payload verify", "crc32c on every frame"},
{"rx fanout", fanout},
{"duplex", fmt.Sprintf("%v", duplex)},
{"socket buffers", fmt.Sprintf("sndbuf %s, rcvbuf %s (granted)",
@@ -589,7 +561,7 @@ loop:
doneTx.Store(true)
elapsed := time.Since(start).Seconds()
time.Sleep(drain)
time.Sleep(drainTime)
doneRx.Store(true)
wg.Wait()
+1 -23
View File
@@ -2,7 +2,6 @@ package main
import (
"fmt"
"os"
"strings"
)
@@ -17,29 +16,8 @@ const (
cGrey = "\x1b[90m"
)
var useColor bool
func initColor(mode string) error {
switch mode {
case "always":
useColor = true
case "never":
useColor = false
case "auto":
if os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb" {
useColor = false
return nil
}
st, err := os.Stdout.Stat()
useColor = err == nil && st.Mode()&os.ModeCharDevice != 0
default:
return fmt.Errorf("unknown color mode %q (want auto, always, never)", mode)
}
return nil
}
func paint(s, code string) string {
if !useColor || s == "" {
if s == "" {
return s
}
return code + s + cReset
-4
View File
@@ -28,7 +28,6 @@ type rxWorker struct {
fd int
batch int
cpu int
verify bool
spec *frameSpec
stats *rxStats
streams []streamState
@@ -79,9 +78,6 @@ func (w *rxWorker) run(done *atomic.Bool) {
}
}
if !w.verify {
continue
}
if p.payLen > len(w.spec.ref) {
w.stats.badLen.Add(1)
continue
+9 -4
View File
@@ -31,7 +31,12 @@ func sockBufSize(fd, opt int) int {
return v
}
func openTxSocket(ifindex, sndbuf int) (int, error) {
const (
sndbufBytes = 8 << 20
rcvbufBytes = 64 << 20
)
func openTxSocket(ifindex int) (int, error) {
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, 0)
if err != nil {
return -1, fmt.Errorf("socket: %w", err)
@@ -44,20 +49,20 @@ func openTxSocket(ifindex, sndbuf int) (int, error) {
unix.Close(fd)
return -1, fmt.Errorf("qdisc bypass: %w", err)
}
if err := setBufForce(fd, unix.SO_SNDBUFFORCE, unix.SO_SNDBUF, sndbuf); err != nil {
if err := setBufForce(fd, unix.SO_SNDBUFFORCE, unix.SO_SNDBUF, sndbufBytes); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("sndbuf: %w", err)
}
return fd, nil
}
func openRxSocket(ifindex, rcvbuf, fanoutID, fanoutMode int) (int, error) {
func openRxSocket(ifindex, fanoutID, fanoutMode int) (int, error) {
proto := int(htons(etherType))
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, proto)
if err != nil {
return -1, fmt.Errorf("socket: %w", err)
}
if err := setBufForce(fd, unix.SO_RCVBUFFORCE, unix.SO_RCVBUF, rcvbuf); err != nil {
if err := setBufForce(fd, unix.SO_RCVBUFFORCE, unix.SO_RCVBUF, rcvbufBytes); err != nil {
unix.Close(fd)
return -1, fmt.Errorf("rcvbuf: %w", err)
}
+10 -11
View File
@@ -62,13 +62,12 @@ type ethtoolCoalesce struct {
rateSampleInterval uint32
}
type systemConfig struct {
governor string
rxUsecs uint32
txUsecs uint32
rxRing uint32
txRing uint32
}
const (
wantGovernor = "performance"
wantCoalesceUsecs = 25
wantRxRing = 8160
wantTxRing = 4096
)
type checkResult struct {
item string
@@ -311,15 +310,15 @@ func withIoctlSocket(fn func(fd int) []checkResult) []checkResult {
return fn(fd)
}
func configureSystem(cfg systemConfig, ifnames []string) []checkResult {
func configureSystem(ifnames []string) []checkResult {
return withIoctlSocket(func(fd int) []checkResult {
out := []checkResult{checkGovernor(cfg.governor)}
out := []checkResult{checkGovernor(wantGovernor)}
for _, ifname := range ifnames {
out = append(out, checkLinkUp(fd, ifname))
out = append(out, checkCoalesce(fd, ifname, cfg.rxUsecs, cfg.txUsecs))
out = append(out, checkCoalesce(fd, ifname, wantCoalesceUsecs, wantCoalesceUsecs))
carrierWait := 3 * time.Second
r, reset := checkRings(fd, ifname, cfg.rxRing, cfg.txRing)
r, reset := checkRings(fd, ifname, wantRxRing, wantTxRing)
out = append(out, r)
if reset {
carrierWait = 10 * time.Second