Reputation: 24260
I've been doing some profiling and benchmarking in order to optimise writing out to a temporary bytes.Buffer
to catch any errors from template.ExecuteTemplate
.
Specifically, we're writing to the buffer, checking for any errors, and if none, writing out to our http.ResponseWriter
. The problem, however, is that the temporary buffer has a request overhead that's somewhat noticeable:
Of course, 21k req/s is still a lot of requests, but a 22% perf. hit is also a fairly large impact.
func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
// Ensure the template exists in the map.
tmpl, ok := templates[name]
if !ok {
return ErrTemplateDoesNotExist
}
// Create a buffer to temporarily write to and check if any errors were encountered.
buf := bytes.NewBuffer(make([]byte, 0, 10000))
err := tmpl.ExecuteTemplate(buf, "base", data)
if err != nil {
return err
}
// Set the header and write the buffer to the http.ResponseWriter
w.Header().Set("Content-Type", "text/html; charset=utf-8")
buf.WriteTo(w)
return nil
}
The 10K buffer size is a rough estimation of the typical max page size of most of my responses, although I've yet to test this beyond a small handful of pages just yet. A response larger than the buffer size typically results in another 20% hit to performance.
Is there a better way to write to a temporary buffer in every request? Another gopher pointed out the upcoming sync.Pool in Go 1.3, but I'm not sure where to start when it comes to writing that out.
Added: using http://godoc.org/github.com/oxtoacart/bpool at the moment yields 33k req/s at 36ms per request:
var bufpool *bpool.BufferPool
func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
...
buf := bufpool.Get()
err := tmpl.ExecuteTemplate(buf, "base", data)
if err != nil {
return err
}
// Set the header and write the buffer to the http.ResponseWriter
w.Header().Set("Content-Type", "text/html; charset=utf-8")
buf.WriteTo(w)
bufpool.Put(buf)
return nil
}
func init() {
bufpool = bpool.NewBufferPool(48)
}
Upvotes: 1
Views: 3272
Reputation: 49205
[copied from comments as an answer]
Just pool your buffers using an available pool not from the standard library. This one looks like it will work (search godoc a bit for a few other alternatives):
http://godoc.org/github.com/oxtoacart/bpool
Yyou should probably also see an increase in throughput regardless of size, just by reducing the garbage collector pressure.
Upvotes: 3