wgengine/bench: improved rate selection.

The old decay-based one took a while to converge. This new one (based
very loosely on TCP BBR) seems to converge quickly on what seems to be
the best speed.

Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
pull/1583/head
Avery Pennarun 3 years ago
parent a92b9647c5
commit a7fe1d7c46

@ -90,7 +90,7 @@ func main() {
traf.Start(Addr1.IP, Addr2.IP, PayloadSize+ICMPMinSize, 0) traf.Start(Addr1.IP, Addr2.IP, PayloadSize+ICMPMinSize, 0)
var cur, prev Snapshot var cur, prev Snapshot
var pps float64 var pps int64
i := 0 i := 0
for { for {
i += 1 i += 1
@ -104,7 +104,7 @@ func main() {
if prev.WhenNsec == 0 { if prev.WhenNsec == 0 {
logf("tx=%-6d rx=%-6d", d.TxPackets, d.RxPackets) logf("tx=%-6d rx=%-6d", d.TxPackets, d.RxPackets)
} else { } else {
logf("%v @%7.0f pkt/sec", d, pps) logf("%v @%7d pkt/s", d, pps)
} }
} }

@ -81,7 +81,7 @@ func runOnce(b *testing.B, setup SetupFunc, payload int) {
traf.Start(Addr1.IP, Addr2.IP, payload, int64(b.N)) traf.Start(Addr1.IP, Addr2.IP, payload, int64(b.N))
var cur, prev Snapshot var cur, prev Snapshot
var pps float64 var pps int64
i := 0 i := 0
for traf.Running() { for traf.Running() {
i += 1 i += 1
@ -93,7 +93,7 @@ func runOnce(b *testing.B, setup SetupFunc, payload int) {
d := cur.Sub(prev) d := cur.Sub(prev)
if prev.WhenNsec != 0 { if prev.WhenNsec != 0 {
logf("%v @%7.0f pkt/sec", d, pps) logf("%v @%7d pkt/sec", d, pps)
} }
} }

@ -74,9 +74,9 @@ type TrafficGen struct {
// caller wants to go. // caller wants to go.
nsPerPacket int64 nsPerPacket int64
// bestPPS is the "best observed packets-per-second" in recent // ppsHistory is the observed packets-per-second from recent
// memory. // samples.
bestPPS float64 ppsHistory [5]int64
} }
// NewTrafficGen creates a new, initially locked, TrafficGen. // NewTrafficGen creates a new, initially locked, TrafficGen.
@ -221,28 +221,42 @@ func (t *TrafficGen) GotPacket(b []byte, ofs int) {
// 1% to receive them, leading to a misleading throughput calculation. // 1% to receive them, leading to a misleading throughput calculation.
// //
// Call this function multiple times per second. // Call this function multiple times per second.
func (t *TrafficGen) Adjust() (pps float64) { func (t *TrafficGen) Adjust() (pps int64) {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
d := t.cur.Sub(t.prev)
// don't adjust rate until the first full period *after* receiving // don't adjust rate until the first full period *after* receiving
// the first packet. This skips any handshake time in the underlying // the first packet. This skips any handshake time in the underlying
// transport. // transport.
if t.prev.LastSeqRx == 0 { if t.prev.LastSeqRx == 0 || d.DurationNsec == 0 {
t.prev = t.cur t.prev = t.cur
return 0 // no estimate yet, continue at max speed return 0 // no estimate yet, continue at max speed
} }
d := t.cur.Sub(t.prev) pps = int64(d.RxPackets) * 1e9 / int64(d.DurationNsec)
t.bestPPS *= 0.97
pps = float64(d.RxPackets) * 1e9 / float64(d.DurationNsec) // We use a rate selection algorithm based loosely on TCP BBR.
if pps > 0 && t.prev.WhenNsec > 0 { // Basically, we set the transmit rate to be a bit higher than
if pps > t.bestPPS { // the best observed transmit rate in the last several time
t.bestPPS = pps // periods. This guarantees some packet loss, but should converge
// quickly on a rate near the sustainable maximum.
bestPPS := pps
for _, p := range t.ppsHistory {
if p > bestPPS {
bestPPS = p
} }
t.nsPerPacket = int64(1e9 / t.bestPPS) }
if pps > 0 && t.prev.WhenNsec > 0 {
copy(t.ppsHistory[1:], t.ppsHistory[0:len(t.ppsHistory)-1])
t.ppsHistory[0] = pps
}
if bestPPS > 0 {
pps = bestPPS * 103 / 100
t.nsPerPacket = int64(1e9 / pps)
} }
t.prev = t.cur t.prev = t.cur
return t.bestPPS return pps
} }

Loading…
Cancel
Save