dqq3623 2014-06-09 04:37
浏览 48
已采纳

将HTTP响应写入一个临时字节。

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:

  • About 6.2k req/s - 27.6k -> 21.4k with profiling on, and 29k -> 24k with it off;
  • A 9ms (40ms -> 49ms) increase in per request latency.

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)

}

展开全部

  • 写回答

1条回答 默认 最新

  • douxianwu2221 2014-06-09 05:44
    关注

    [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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
编辑
预览

报告相同问题?

手机看
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回
顶部