mirror of
https://github.com/caddyserver/caddy.git
synced 2026-08-31 07:46:42 +00:00
caddyhttp: presize buffers in hot paths to reduce allocations
Right-size three per-request allocations that previously grew from a
nil/empty backing array, causing repeated reallocation:
- CleanPath: pre-Grow the strings.Builder used when slash collapsing is
disabled, instead of letting it reallocate as runes are appended
- headers: presize the rewritten value slice in HeaderOps.ApplyTo's Set
loop instead of appending from nil
- reverseproxy: presize the weights slice in WeightedRoundRobinSelection
.Select
This commit also adds benchmarks exercising each path. Measured with benchstat
with count=20 and GOMAXPROCS=1, with p=0.000:
sec/op B/op allocs/op
CleanPathNoCollapse 1013.4n -> 828.6n (-18.23%) 120->64 (-46.67%) 4->1 (-75.00%)
HeaderOpsApplyToSet 1262.0n -> 955.8n (-24.27%) 255->152 (-40.39%) 9->6 (-33.33%)
WeightedRoundRobinSelect 215.8n -> 200.3n ( -7.20%) 112->96 (-14.29%) 2->2 (N/A)
This commit is contained in:
parent
13a4c3f43c
commit
3e419a5ab1
6 changed files with 66 additions and 5 deletions
|
|
@ -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)
|
||||
|
|
|
|||
11
modules/caddyhttp/caddyhttp_bench_test.go
Normal file
11
modules/caddyhttp/caddyhttp_bench_test.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -243,11 +243,11 @@ func (ops *HeaderOps) ApplyTo(hdr http.Header, repl *caddy.Replacer) {
|
|||
// set
|
||||
for fieldName, vals := range ops.Set {
|
||||
fieldName = repl.ReplaceKnown(fieldName, "")
|
||||
var newVals []string
|
||||
// use a new slice so we don't overwrite
|
||||
// the original values in ops.Set
|
||||
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], "")
|
||||
}
|
||||
hdr.Set(fieldName, strings.Join(newVals, ","))
|
||||
}
|
||||
|
|
|
|||
24
modules/caddyhttp/headers/headers_bench_test.go
Normal file
24
modules/caddyhttp/headers/headers_bench_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -149,7 +149,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 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue