diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go index aacafc92e..761c37a54 100644 --- a/modules/caddyhttp/caddyhttp.go +++ b/modules/caddyhttp/caddyhttp.go @@ -286,6 +286,7 @@ func CleanPath(p string, collapseSlashes bool) string { // and then remove the remaining temporary characters. const tmpCh = 0xff var sb strings.Builder + sb.Grow(len(p) + 1) for i, ch := range p { if ch == '/' && i > 0 && p[i-1] == '/' { sb.WriteByte(tmpCh) diff --git a/modules/caddyhttp/caddyhttp_bench_test.go b/modules/caddyhttp/caddyhttp_bench_test.go new file mode 100644 index 000000000..213b3ae05 --- /dev/null +++ b/modules/caddyhttp/caddyhttp_bench_test.go @@ -0,0 +1,25 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package caddyhttp + +import "testing" + +func BenchmarkCleanPathNoCollapse(b *testing.B) { + const p = "/foo/bar/baz/qux/some/longer/path/segment/here/index.html" + b.ReportAllocs() + for b.Loop() { + _ = CleanPath(p, false) + } +} diff --git a/modules/caddyhttp/headers/headers.go b/modules/caddyhttp/headers/headers.go index a1afaa548..1c396b799 100644 --- a/modules/caddyhttp/headers/headers.go +++ b/modules/caddyhttp/headers/headers.go @@ -259,11 +259,9 @@ func (ops *HeaderOps) ApplyTo(hdr http.Header, repl *caddy.Replacer) { if expanded := repl.ReplaceKnown(fieldName, ""); expanded != fieldName { fieldName = http.CanonicalHeaderKey(expanded) } - var newVals []string + newVals := make([]string, len(vals)) for i := range vals { - // append to new slice so we don't overwrite - // the original values in ops.Set - newVals = append(newVals, repl.ReplaceKnown(vals[i], "")) + newVals[i] = repl.ReplaceKnown(vals[i], "") } if fieldName == "Set-Cookie" && len(newVals) > 1 { hdr.Del(fieldName) diff --git a/modules/caddyhttp/headers/headers_bench_test.go b/modules/caddyhttp/headers/headers_bench_test.go new file mode 100644 index 000000000..e75ca6b46 --- /dev/null +++ b/modules/caddyhttp/headers/headers_bench_test.go @@ -0,0 +1,38 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package headers + +import ( + "net/http" + "testing" + + "github.com/caddyserver/caddy/v2" +) + +func BenchmarkHeaderOpsApplyToSet(b *testing.B) { + ops := &HeaderOps{ + Set: http.Header{ + "Content-Type": []string{"text/html; charset=utf-8"}, + "Cache-Control": []string{"public", "max-age=3600", "immutable"}, + "X-Custom-Header": []string{"value-one", "value-two"}, + }, + } + repl := caddy.NewReplacer() + b.ReportAllocs() + for b.Loop() { + hdr := make(http.Header) + ops.ApplyTo(hdr, repl) + } +} diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 89452b24d..a53c5bdb5 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -552,6 +552,7 @@ func (MatchPath) matchPatternWithEscapeSequence(escapedPath, matchPath string) b // of the path, then switching to the escaped parts where // the pattern hints to us wherever % is present. var sb strings.Builder + sb.Grow(len(escapedPath)) // iterate the pattern and escaped path in lock-step; // increment iPattern every time we consume a char from the pattern, diff --git a/modules/caddyhttp/reverseproxy/selectionpolicies.go b/modules/caddyhttp/reverseproxy/selectionpolicies.go index 431a2fde0..20276bec9 100644 --- a/modules/caddyhttp/reverseproxy/selectionpolicies.go +++ b/modules/caddyhttp/reverseproxy/selectionpolicies.go @@ -152,7 +152,7 @@ func (r *WeightedRoundRobinSelection) Select(pool UpstreamPool, _ *http.Request, return pool[0] } var index, totalWeight int - var weights []int + weights := make([]int, 0, len(r.Weights)) for _, w := range r.Weights { if w > 0 { diff --git a/modules/caddyhttp/reverseproxy/selectionpolicies_bench_test.go b/modules/caddyhttp/reverseproxy/selectionpolicies_bench_test.go new file mode 100644 index 000000000..15d1c16cf --- /dev/null +++ b/modules/caddyhttp/reverseproxy/selectionpolicies_bench_test.go @@ -0,0 +1,39 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reverseproxy + +import ( + "net/http" + "testing" +) + +func BenchmarkWeightedRoundRobinSelect(b *testing.B) { + pool := UpstreamPool{ + {Host: new(Host), Dial: "0.0.0.1"}, + {Host: new(Host), Dial: "0.0.0.2"}, + {Host: new(Host), Dial: "0.0.0.3"}, + {Host: new(Host), Dial: "0.0.0.4"}, + {Host: new(Host), Dial: "0.0.0.5"}, + } + wrrPolicy := &WeightedRoundRobinSelection{ + Weights: []int{5, 4, 3, 2, 1}, + totalWeight: 15, + } + req, _ := http.NewRequest("GET", "/", nil) + b.ReportAllocs() + for b.Loop() { + _ = wrrPolicy.Select(pool, req, nil) + } +} diff --git a/modules/caddyhttp/rewrite/rewrite.go b/modules/caddyhttp/rewrite/rewrite.go index c463fe254..df5d32369 100644 --- a/modules/caddyhttp/rewrite/rewrite.go +++ b/modules/caddyhttp/rewrite/rewrite.go @@ -374,6 +374,7 @@ func escapePathPreservingSlashes(path string) string { // duplicate keys rather than replaces. func buildQueryString(qs string, repl *caddy.Replacer) string { var sb strings.Builder + sb.Grow(len(qs)) // first component must be key, which is the same // as if we just wrote a value in previous iteration diff --git a/modules/caddyhttp/rewrite/rewrite_bench_test.go b/modules/caddyhttp/rewrite/rewrite_bench_test.go new file mode 100644 index 000000000..d13af82be --- /dev/null +++ b/modules/caddyhttp/rewrite/rewrite_bench_test.go @@ -0,0 +1,30 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rewrite + +import ( + "testing" + + "github.com/caddyserver/caddy/v2" +) + +func BenchmarkBuildQueryString(b *testing.B) { + repl := caddy.NewReplacer() + const qs = "foo=bar&baz=qux&search=hello+world&page=2&sort=desc&filter=active&id=12345" + b.ReportAllocs() + for b.Loop() { + _ = buildQueryString(qs, repl) + } +}