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.