@marcel91200 / caddy Mirror · read-only / commits / 22e976e5
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 07:15 UTC

reverseproxy: preallocate upstream slices with known sizes (#7926)

Preallocate the sources slice in MultiUpstreams.Provision and the netAddrs slice in UpstreamResolver.ParseAddresses now that their final lengths are known, avoiding the intermediate append regrowth allocations. For a 4-address resolver this reduces ParseAddresses from 11 to 9 allocs/op and 528 to 384 B/op; the benefit scales with the number of sources/addresses. | before | after | | sec/op | sec/op vs base | UpstreamResolverParseAddresses-8 | 2.289µ |1.946µ ~ (p=0.165) noisy | B/op: 528 → 384 -27.27% (p=0.000) allocs/op: 11 → 9 -18.18% (p=0.000) Adds BenchmarkUpstreamResolverParseAddresses as per policy.

Julien Voisin committed Aug 10, 2026 at 07:51 UTC 22e976e5ae7b3115300610b9d0f848ed562daf74
2 files changed +37 -1
modules/caddyhttp/reverseproxy/upstreams.go
+4 -1
@@ -482,7 +482,9 @@ func (mu *MultiUpstreams) Provision(ctx caddy.Context) error {
482 if err != nil {
483 return fmt.Errorf("loading upstream source modules: %v", err)
484 }
485 - for _, src := range mod.([]any) {
485 + sources := mod.([]any)
486 + mu.sources = make([]UpstreamSource, 0, len(sources))
487 + for _, src := range sources {
488 mu.sources = append(mu.sources, src.(UpstreamSource))
489 }
490 }
@@ -535,6 +537,7 @@ type UpstreamResolver struct {
537 // ParseAddresses parses all the configured network addresses
538 // and ensures they're ready to be used.
539 func (u *UpstreamResolver) ParseAddresses() error {
540 + u.netAddrs = make([]caddy.NetworkAddress, 0, len(u.Addresses))
541 for _, v := range u.Addresses {
542 addr, err := caddy.ParseNetworkAddressWithDefaults(v, "udp", 53)
543 if err != nil {
modules/caddyhttp/reverseproxy/upstreams_bench_test.go new
+33
@@ -0,0 +1,33 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package reverseproxy
16 +
17 +import "testing"
18 +
19 +func BenchmarkUpstreamResolverParseAddresses(b *testing.B) {
20 + addrs := []string{"8.8.8.8", "1.1.1.1", "9.9.9.9", "208.67.222.222"}
21 +
22 + b.ReportAllocs()
23 + b.ResetTimer()
24 + for i := 0; i < b.N; i++ {
25 + // fresh resolver each iteration: ParseAddresses is called once
26 + // per resolver in practice, and this keeps the before/after
27 + // comparison fair (no unbounded append growth across iterations)
28 + r := UpstreamResolver{Addresses: addrs}
29 + if err := r.ParseAddresses(); err != nil {
30 + b.Fatal(err)
31 + }
32 + }
33 +}