tsweb: memoize the string forms of HTTP response codes.

Saves 1-2 allocs per HTTP request after warmup, thanks to
avoiding strconv and fmt.

Signed-off-by: David Anderson <danderson@tailscale.com>
pull/4685/head
David Anderson 3 years ago committed by Dave Anderson
parent 9343967317
commit e1c1d47991

@ -23,6 +23,7 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"go4.org/mem" "go4.org/mem"
@ -302,15 +303,38 @@ func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
if h.opts.StatusCodeCounters != nil { if h.opts.StatusCodeCounters != nil {
key := fmt.Sprintf("%dxx", msg.Code/100) h.opts.StatusCodeCounters.Add(responseCodeString(msg.Code/100), 1)
h.opts.StatusCodeCounters.Add(key, 1)
} }
if h.opts.StatusCodeCountersFull != nil { if h.opts.StatusCodeCountersFull != nil {
h.opts.StatusCodeCountersFull.Add(strconv.Itoa(msg.Code), 1) h.opts.StatusCodeCountersFull.Add(responseCodeString(msg.Code), 1)
} }
} }
func responseCodeString(code int) string {
if v, ok := responseCodeCache.Load(code); ok {
return v.(string)
}
var ret string
if code < 10 {
ret = fmt.Sprintf("%dxx", code)
} else {
ret = strconv.Itoa(code)
}
responseCodeCache.Store(code, ret)
return ret
}
// responseCodeCache memoizes the string form of HTTP response codes,
// so that the hot request-handling codepath doesn't have to allocate
// in strconv/fmt for every request.
//
// Keys are either full HTTP response code ints (200, 404) or "family"
// ints representing entire families (e.g. 2 for 2xx codes). Values
// are the string form of that code/family.
var responseCodeCache sync.Map
// loggingResponseWriter wraps a ResponseWriter and record the HTTP // loggingResponseWriter wraps a ResponseWriter and record the HTTP
// response code that gets sent, if any. // response code that gets sent, if any.
type loggingResponseWriter struct { type loggingResponseWriter struct {

Loading…
Cancel
Save