From e1c1d4799110f43b443d988a94b26d402bb39cce Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 13 May 2022 13:56:55 -0700 Subject: [PATCH] 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 --- tsweb/tsweb.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/tsweb/tsweb.go b/tsweb/tsweb.go index 191a1e9e3..7c117ff90 100644 --- a/tsweb/tsweb.go +++ b/tsweb/tsweb.go @@ -23,6 +23,7 @@ import ( "runtime" "strconv" "strings" + "sync" "time" "go4.org/mem" @@ -302,15 +303,38 @@ func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if h.opts.StatusCodeCounters != nil { - key := fmt.Sprintf("%dxx", msg.Code/100) - h.opts.StatusCodeCounters.Add(key, 1) + h.opts.StatusCodeCounters.Add(responseCodeString(msg.Code/100), 1) } 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 // response code that gets sent, if any. type loggingResponseWriter struct {