perf(proxy): avoid unnecessary alloc in Yield (#7708)

This commit is contained in:
Ville Vesilehto
2025-11-24 18:20:30 +02:00
committed by GitHub
parent 63eb9f70e5
commit fe7335e634
2 changed files with 38 additions and 3 deletions

View File

@@ -128,9 +128,15 @@ const yieldTimeout = 25 * time.Millisecond
func (t *Transport) Yield(pc *persistConn) { func (t *Transport) Yield(pc *persistConn) {
pc.used = time.Now() // update used time pc.used = time.Now() // update used time
// Make this non-blocking, because in the case of a very busy forwarder we will *block* on this yield. This // Optimization: Try to return the connection immediately without creating a timer.
// blocks the outer go-routine and stuff will just pile up. We timeout when the send fails to as returning // If the receiver is not ready, we fall back to a timeout-based send to avoid blocking forever.
// these connection is an optimization anyway. // Returning the connection is just an optimization, so dropping it on timeout is fine.
select {
case t.yield <- pc:
return
default:
}
select { select {
case t.yield <- pc: case t.yield <- pc:
return return

View File

@@ -1,6 +1,7 @@
package proxy package proxy
import ( import (
"runtime"
"testing" "testing"
"time" "time"
@@ -107,3 +108,31 @@ func TestCleanupAll(t *testing.T) {
t.Error("Expected no cached connections") t.Error("Expected no cached connections")
} }
} }
func BenchmarkYield(b *testing.B) {
s := dnstest.NewServer(func(w dns.ResponseWriter, r *dns.Msg) {
ret := new(dns.Msg)
ret.SetReply(r)
w.WriteMsg(ret)
})
defer s.Close()
tr := newTransport("BenchmarkYield", s.Addr)
tr.Start()
defer tr.Stop()
c, _, _ := tr.Dial("udp")
b.ReportAllocs()
for b.Loop() {
tr.Yield(c)
// Drain the yield channel so we can yield again without blocking/timing out
// We need to simulate the consumer side slightly to keep Yield flowing
select {
case <-tr.yield:
default:
}
runtime.Gosched()
}
}