filterFadeIn copies every LB endpoint per request even when no endpoint is fading in

perfloop/skipper · AVOIDABLE COPY

https://perfloop.ai/t/oss/case_p9asdj29n0

Verdict

VERIFIED · settled 2026-10-06

What happened: The paired measurements met the required improvement.

Hypothesis

proxy.selectEndpoint (proxy/proxy.go:653-667) runs for every request on an LB backend route and calls p.fadein.filterFadeIn(rt.LBEndpoints, rt) first. For any route with a fadeIn filter (rt.LBFadeInDuration > 0, proxy/fadein.go:27), filterFadeIn unconditionally does `filtered := make([]routing.LBEndpoint, 0, len(endpoints))` (proxy/fadein.go:36) and appends every endpoint whose fade-in score exceeds the threshold (lines 37-47). fadeInScore returns 1 for any endpoint whose age is >= the fade-in duration (proxy/fadein.go:17-21), and threshold is rnd.Float64() in [0,1), so once all endpoints are past their fade-in window (the steady state for a route) every endpoint is kept and the per-request copy reproduces rt.LBEndpoints exactly. routing.LBEndpoint is a struct of three strings plus a Metrics interface (routing/routing.go:163-166), so the per-request allocation scales with the endpoint count of the route. The downstream consumers (loadbalancer Apply implementations in loadbalancer/algorithm.go and filterHealthyEndpoints in proxy/healthy_endpoints.go) only read ctx.LBEndpoints, so returning the original slice when nothing is dropped is behavior-preserving. The existing BenchmarkFadeIn (proxy/fadein_internal_test.go:404-422) exercises exactly this steady state: 200 endpoints aged 2x the fade-in duration, through proxy.selectEndpoint, for four algorithms.

Change to test: In proxy/fadein.go filterFadeIn, allocate the filtered slice lazily: iterate the endpoints, and only on the first endpoint that fails the threshold allocate a slice and copy endpoints[:i] into it, appending subsequent kept endpoints. If no endpoint was dropped, return the input slice unchanged (same order, same contents as today). Keep the existing "all dropped -> return endpoints" fallback. No API change; the change is confined to filterFadeIn. Measure with the existing BenchmarkFadeIn in proxy/fadein_internal_test.go (with -benchmem), and run go test ./proxy/... ./loadbalancer/...

Where it lives

perfloop/skipper · proxy/proxy.go

Evidence

200 distinct LB endpoints, each served by a warmed local 204 backend, through Proxy.ServeHTTP with the default Proxy transport and access logging disabled; 1h fade-in duration, 2h endpoint age, GOMAXPROCS=1, sequential calls with rotating RemoteAddr; powerOfRandomNChoices · 10 sample pairs

metric baseline candidate paired median change confidence range required result
B/op 38976 25406 −34.8% (−13570) −13570 to −13570 < −1949 PASSED
allocs/op 182 181 −0.5% (−1) −1 to −1 ≤ 9.1 PASSED
ns/op 165278 150285 −8.7% (−14448) −19631 to −10372 ≤ 8264 PASSED

200 distinct LB endpoints, each served by a warmed local 204 backend, through Proxy.ServeHTTP with the default Proxy transport and access logging disabled; 1h fade-in duration, 2h endpoint age, GOMAXPROCS=1, sequential calls with rotating RemoteAddr; random · 10 sample pairs

metric baseline candidate paired median change confidence range required result
B/op 38976 25406 −34.8% (−13570) −13570 to −13570 < −1949 PASSED
allocs/op 182 181 −0.5% (−1) −1 to −1 ≤ 9.1 PASSED
ns/op 161399 150650 −6.4% (−10284) −25245 to −4785 ≤ 8070 PASSED

200 distinct LB endpoints, each served by a warmed local 204 backend, through Proxy.ServeHTTP with the default Proxy transport and access logging disabled; 1h fade-in duration, 2h endpoint age, GOMAXPROCS=1, sequential calls with rotating RemoteAddr; roundRobin · 10 sample pairs

metric baseline candidate paired median change confidence range required result
B/op 38976 25406 −34.8% (−13570) −13570 to −13570 < −1949 PASSED
allocs/op 182 181 −0.5% (−1) −1 to −1 ≤ 9.1 PASSED
ns/op 157627 145249 −7.2% (−11273) −36725 to −361 ≤ 7881 PASSED

200 distinct LB endpoints, each served by a warmed local 204 backend, through Proxy.ServeHTTP with the default Proxy transport and access logging disabled; 1h fade-in duration, 2h endpoint age, GOMAXPROCS=1, sequential calls with rotating RemoteAddr; consistentHash · 10 sample pairs

metric baseline candidate paired median change confidence range required result
B/op 39008 25438 −34.8% (−13570) −13570 to −13570 < −1950 PASSED
allocs/op 184 183 −0.5% (−1) −1 to −1 ≤ 9.2 PASSED
ns/op 156412 145177 −7.5% (−11678) −16002 to −7658 ≤ 7821 PASSED

Checks: 3 of 3 passed. Verification: no defect found.

Timeline