我正在对 Go 中的此类代码进行压力测试(使用 loader.io),以创建一个包含 100 个项目的数组以及一些其他基本变量,并在模板中解析它们:

package main

import (
    "html/template"
    "net/http"
)

var templates map[string]*template.Template

// Load templates on program initialisation
func init() {
    if templates == nil {
        templates = make(map[string]*template.Template)
    }

    templates["index.html"] = template.Must(template.ParseFiles("index.html"))
}

func handler(w http.ResponseWriter, r *http.Request) {
    type Post struct {
        Id int
        Title, Content string
    }

    var Posts [100]Post

    // Fill posts
    for i := 0; i < 100; i++ {
        Posts[i] = Post{i, "Sample Title", "Lorem Ipsum Dolor Sit Amet"}
    }

    type Page struct {
        Title, Subtitle string
        Posts [100]Post
    }

    var p Page

    p.Title = "Index Page of My Super Blog"
    p.Subtitle = "A blog about everything"
    p.Posts = Posts

    tmpl := templates["index.html"]

    tmpl.ExecuteTemplate(w, "index.html", p)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8888", nil)
}

我对 Loader 的测试是在 1 分钟内使用 5k 并发连接数/秒。问题是,在开始测试几秒钟后,我得到了很高的平均延迟(几乎 10 秒),结果是 5k 成功响应,测试停止,因为它达到了 50% 的错误率(超时)。

在同一台机器上,PHP 给出 50k+。

我知道这不是 Go 性能问题,而是可能与 html/template 相关的问题。 Go 可以轻松地管理足够难的计算,当然比 PHP 之类的任何东西都要快得多,但是当涉及到将数据解析为模板时,为什么会如此糟糕?

任何解决方法,或者我可能只是做错了(我是 Go 新手)?

附言实际上,即使有 1 个项目,它也完全相同...... 5-6k 并在大量超时后停止。但这可能是因为带有帖子的数组保持相同的长度。

我的模板代码(index.html):
{{ .Title }}
{{ .Subtitle }}

{{ range .Posts }}
        {{ .Title }}
        {{ .Content }}
{{ end }}

这是 github.com/pkg/profile 的分析结果:
root@Test:~# go tool pprof app /tmp/profile311243501/cpu.pprof
Possible precedence issue with control flow operator at /usr/lib/go/pkg/tool/linux_amd64/pprof line 3008.
Welcome to pprof!  For help, type 'help'.
(pprof) top10
Total: 2054 samples
      97   4.7%   4.7%      726  35.3% reflect.Value.call
      89   4.3%   9.1%      278  13.5% runtime.mallocgc
      85   4.1%  13.2%       86   4.2% syscall.Syscall
      66   3.2%  16.4%       75   3.7% runtime.MSpan_Sweep
      58   2.8%  19.2%     1842  89.7% text/template.(*state).walk
      54   2.6%  21.9%      928  45.2% text/template.(*state).evalCall
      51   2.5%  24.3%       53   2.6% settype
      47   2.3%  26.6%       47   2.3% runtime.stringiter2
      44   2.1%  28.8%      149   7.3% runtime.makeslice
      40   1.9%  30.7%      223  10.9% text/template.(*state).evalField

这些是优化代码后的分析结果(如 icza 的回答中所建议的):
root@Test:~# go tool pprof app /tmp/profile501566907/cpu.pprof
Possible precedence issue with control flow operator at /usr/lib/go/pkg/tool/linux_amd64/pprof line 3008.
Welcome to pprof!  For help, type 'help'.
(pprof) top10
Total: 2811 samples
     137   4.9%   4.9%      442  15.7% runtime.mallocgc
     126   4.5%   9.4%      999  35.5% reflect.Value.call
     113   4.0%  13.4%      115   4.1% syscall.Syscall
     110   3.9%  17.3%      122   4.3% runtime.MSpan_Sweep
     102   3.6%  20.9%     2561  91.1% text/template.(*state).walk
      74   2.6%  23.6%      337  12.0% text/template.(*state).evalField
      68   2.4%  26.0%       72   2.6% settype
      66   2.3%  28.3%     1279  45.5% text/template.(*state).evalCall
      65   2.3%  30.6%      226   8.0% runtime.makeslice
      57   2.0%  32.7%       57   2.0% runtime.stringiter2
(pprof)

最佳答案

使用 html/template 的等效应用程序比 PHP 变体慢的主要原因有两个。

首先,html/template 提供了比 PHP 更多的功能。主要区别在于 html/template 将根据变量在结果 HTML 输出中的位置(我认为这很酷!)使用正确的转义规则(HTML、JS、CSS 等)自动转义变量。

其次,html/template 渲染代码大量使用反射和具有可变数量参数的方法,并且它们不如静态编译代码快。

在引擎盖下以下模板

{{ .Title }}
{{ .Subtitle }}

{{ range .Posts }}
    {{ .Title }}
    {{ .Content }}
{{ end }}

转换为类似的东西
{{ .Title | html_template_htmlescaper }}
{{ .Subtitle | html_template_htmlescaper }}

{{ range .Posts }}
    {{ .Title | html_template_htmlescaper }}
    {{ .Content | html_template_htmlescaper }}
{{ end }}

在循环中使用反射调用 html_template_htmlescaper 会降低性能。

说了这么多,这个 html/template 的微基准不应该被用来决定是否使用 Go。一旦您将使用数据库的代码添加到请求处理程序中,我怀疑模板呈现时间几乎不会被注意到。

另外我很确定随着时间的推移,Go 反射和 html/template 包都会变得更快。

如果在实际应用中您会发现 html/template 是一个瓶颈,那么仍然可以切换到 text/template 并为其提供已经转义的数据。

关于go - Go lang 中 html/模板的性能缓慢,有什么解决方法吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31361745/

10-17 01:31