Go: net / http:在Go 1.8中,HTTP / 1服务器的速度降低了0.5μs

创建于 2017-02-06  ·  35评论  ·  资料来源: golang/go

在提交问题之前,请先回答这些问题。 谢谢!

您正在使用什么版本的Go( go version )?

去1.7.5、1.8 rc3和git

您正在使用什么操作系统和处理器体系结构( go env )?

Arch Linux 64位

你做了什么?

go run https://gist.github.com/OneOfOne/4d7e13977886ddab825870bc3422a901
切换终端并运行wrk -c 20 -d 30s http://localhost:8081

您期望看到什么?

吞吐量等于1.7或更快。

您看到了什么?

去提示fd37b8ccf2

Running 30s test @ http://localhost:8081
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   195.30us  470.12us  16.30ms   95.11%
    Req/Sec    85.62k     6.00k   95.21k    80.83%
  5110713 requests in 30.01s, 721.35MB read
Requests/sec: 170322.67
Transfer/sec:     24.04MB

去1.8rc3 758a7281ab

Running 30s test @ http://localhost:8081
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   192.49us  451.74us  15.14ms   95.02%
    Req/Sec    85.91k     6.37k   97.60k    83.50%
  5130079 requests in 30.01s, 724.08MB read
Requests/sec: 170941.23
Transfer/sec:     24.13MB

去1.7.5

Running 30s test @ http://localhost:8081
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   210.16us  528.53us  14.78ms   94.13%
    Req/Sec    94.34k     4.31k  103.56k    73.83%
  5631803 requests in 30.01s, 794.89MB read
Requests/sec: 187673.03
Transfer/sec:     26.49MB
FrozenDueToAge NeedsInvestigation Performance help wanted

最有用的评论

@bradfitz

只是局外人的一个简短问题,所以如果我错过了明显的事情,请忍受我。
为什么解决这个问题为时已晚? 这不是释放候选人的全部理由吗? 要在发布下一个主要版本之前找到最终的主要问题?

如果没有,请教育我。

另外,感谢您为这个项目所做的工作,我认识许多人喜欢这种语言以及周围的社区。 🎉

所有35条评论

对于Go 1.8来说为时已晚,但是我们可以研究Go 1.9期间的性能。

有人想调查差异吗? Go 1.7 vs Go 1.8 CPU配置文件是什么?

使用pprof更新了播放: https :

我运行了go tool pprof http://localhost:6060/debug/pprof/profile ,更改了条款并运行了wrk -c 20 -d 30s http://localhost:6060/

去小费

(pprof) top
36600ms of 80000ms total (45.75%)
Dropped 297 nodes (cum <= 400ms)
Showing top 10 nodes out of 135 (cum >= 970ms)
      flat  flat%   sum%        cum   cum%
   26360ms 32.95% 32.95%    27340ms 34.17%  syscall.Syscall
    2280ms  2.85% 35.80%     5740ms  7.17%  runtime.mallocgc
    1310ms  1.64% 37.44%     1310ms  1.64%  runtime.heapBitsSetType
    1260ms  1.57% 39.01%     1260ms  1.57%  runtime._ExternalCode
    1030ms  1.29% 40.30%     7280ms  9.10%  net/http.(*chunkWriter).writeHeader
     970ms  1.21% 41.51%      970ms  1.21%  runtime.epollwait
     900ms  1.12% 42.64%      920ms  1.15%  runtime.mapiternext
     880ms  1.10% 43.74%      880ms  1.10%  runtime.usleep
     820ms  1.03% 44.76%     1490ms  1.86%  runtime.deferreturn
     790ms  0.99% 45.75%      970ms  1.21%  runtime.mapaccess1_faststr
(pprof) top -cum
27.89s of 80s total (34.86%)
Dropped 297 nodes (cum <= 0.40s)
Showing top 10 nodes out of 135 (cum >= 23.44s)
      flat  flat%   sum%        cum   cum%
     0.01s 0.013% 0.013%     73.46s 91.83%  runtime.goexit
     0.55s  0.69%   0.7%     69.55s 86.94%  net/http.(*conn).serve
     0.30s  0.38%  1.07%     35.91s 44.89%  net/http.(*response).finishRequest
     0.15s  0.19%  1.26%     32.10s 40.12%  bufio.(*Writer).Flush
    26.36s 32.95% 34.21%     27.34s 34.17%  syscall.Syscall
     0.10s  0.12% 34.34%     24.56s 30.70%  net/http.checkConnErrorWriter.Write
         0     0% 34.34%     24.44s 30.55%  net.(*conn).Write
     0.23s  0.29% 34.62%     24.44s 30.55%  net.(*netFD).Write
     0.06s 0.075% 34.70%     23.50s 29.38%  syscall.Write
     0.13s  0.16% 34.86%     23.44s 29.30%  syscall.write

去1.7.5

(pprof) top
40520ms of 82240ms total (49.27%)
Dropped 281 nodes (cum <= 411.20ms)
Showing top 10 nodes out of 128 (cum >= 860ms)
      flat  flat%   sum%        cum   cum%
   29480ms 35.85% 35.85%    30920ms 37.60%  syscall.Syscall
    2550ms  3.10% 38.95%     5710ms  6.94%  runtime.mallocgc
    1560ms  1.90% 40.84%     1590ms  1.93%  runtime.heapBitsSetType
    1220ms  1.48% 42.33%     1220ms  1.48%  runtime.epollwait
    1050ms  1.28% 43.60%     2750ms  3.34%  runtime.mapassign1
    1050ms  1.28% 44.88%     1080ms  1.31%  runtime.mapiternext
    1000ms  1.22% 46.10%     7890ms  9.59%  net/http.(*chunkWriter).writeHeader
     880ms  1.07% 47.17%     2910ms  3.54%  net/http.DetectContentType
     870ms  1.06% 48.22%     1010ms  1.23%  runtime.mapaccess1_faststr
     860ms  1.05% 49.27%      860ms  1.05%  runtime.futex
(pprof) top -cum
31.67s of 82.24s total (38.51%)
Dropped 281 nodes (cum <= 0.41s)
Showing top 10 nodes out of 128 (cum >= 27.69s)
      flat  flat%   sum%        cum   cum%
         0     0%     0%     75.77s 92.13%  runtime.goexit
     0.44s  0.54%  0.54%     74.26s 90.30%  net/http.(*conn).serve
     0.27s  0.33%  0.86%     37.08s 45.09%  net/http.(*response).finishRequest
     0.18s  0.22%  1.08%     36.44s 44.31%  bufio.(*Writer).Flush
     0.25s   0.3%  1.39%     36.26s 44.09%  bufio.(*Writer).flush
    29.48s 35.85% 37.23%     30.92s 37.60%  syscall.Syscall
     0.12s  0.15% 37.38%     27.99s 34.03%  net/http.checkConnErrorWriter.Write
     0.69s  0.84% 38.22%     27.85s 33.86%  net/http.(*conn).readRequest
     0.08s 0.097% 38.31%     27.77s 33.77%  net.(*conn).Write
     0.16s  0.19% 38.51%     27.69s 33.67%  net.(*netFD).Write

让我知道您是否要我做任何具体的事情。

哎呀,我看到的是同一件事,在使用1.8r3的网络服务器基准测试中,速度降低了约20%,二进制文件的大小也略有增加

我意识到发布周期很晚,但是对许多人来说,http的20%回归是一个巨大的回归。

如果您告诉我需要什么@bradfitz,我愿意帮助调试。

如果您告诉我需要什么@bradfitz,我愿意帮助调试。

步骤1是调试为什么变慢。 如果您有任何线索,请告诉我。

@OneOfOne对于非Hello World应用程序如何工作? 您在那看到任何问题吗? 直到1.9为止,您都可以接受Hello World应用程序的20%吗?

@bradfitz我认为Shutdown功能导致“ hello world”应用/测试的性能下降。

            select {
            case <-srv.getDoneChan():
return ErrServerClosed
//....

这里

在1.7,它只是一个for循环。

有人可以确认我的假设吗?

谢谢,
卡塔拉斯

我无法复制OP的结果。 我在Mac上,并且正在使用_slightly_旧版本的Go。

$ wrk -c 20 -d 30s http://localhost:8081  # go 1.8 RC2
Running 30s test @ http://localhost:8081
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     0.87ms    6.99ms 151.45ms   99.38%
    Req/Sec    25.22k     2.34k   33.28k    66.94%
  1510655 requests in 30.10s, 213.22MB read
Requests/sec:  50188.34
Transfer/sec:      7.08MB

$ wrk -c 20 -d 30s http://localhost:8081  # go 1.7.4
Running 30s test @ http://localhost:8081
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   840.73us    6.85ms 151.46ms   99.41%
    Req/Sec    26.05k     3.67k   33.43k    60.96%
  1560770 requests in 30.10s, 220.29MB read
Requests/sec:  51853.50
Transfer/sec:      7.32MB

略有差异,但小于20%,在这里几乎可以忽略不计。

@kataras ,您是否有证据支持该理论?

您可以自己确认:删除这些行并再次进行测量。

不过,我会感到惊讶。

@kataras看起来可能就是这样。 除了选择之外,您还具有互斥锁获取和延迟执行的解锁(我知道这最近有所加快,但是比直接解锁要慢一些)。

func (s *Server) getDoneChan() <-chan struct{} {
    s.mu.Lock()
    defer s.mu.Unlock()
    return s.getDoneChanLocked()
}

由于accept循环是一条很热的路径,因此有必要将关闭选择移到一个单独的goroutine中,并使用sync / atomic在accept循环中发出关闭信号。

编辑:
我不认为这就是这样,只是尝试了一次小费而没有全部选择的技巧,它增加了大约7us(5%〜)。

我知道这对于1.8来说为时已晚,但是为什么不在以后的1.8.1版本中解决此问题呢?

还有其他人能够重现这些结果吗?

Debian 8,amd64(1.7.5和rc3)的-11.2%

Running 30s test @ http://localhost:8081
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   457.65us    1.26ms  46.74ms   93.23%
    Req/Sec    65.08k     7.84k   99.65k    73.83%
  3891443 requests in 30.07s, 549.25MB read
Requests/sec: 129397.13
Transfer/sec:     18.26MB

Running 30s test @ http://localhost:8081
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   350.93us    0.92ms  39.50ms   94.72%
    Req/Sec    57.70k     5.34k   77.31k    74.50%
  3447358 requests in 30.03s, 486.57MB read
Requests/sec: 114800.24
Transfer/sec:     16.20MB

@kataras实现是正确的,这意味着作者很小心..在软件中发生的错误并不是很多。

就是说我还是错了,我刚才又看了一眼,发现选择仅在接受期间发生错误时才发生。 奇怪的是,当我从接受中删除程序时,它如何始终使我的程序变慢。 也许它取消了某些优化通过或内联的限制? 在某处可能会导致速度下降的问题,但在其他地方,需要的设置与我的不同。

@bradfitz简要地将

@codido ,感谢您的平分! 是的,这也是我也会怀疑的承诺。 很高兴看到确认。 这是一段时间以来net / http.Server中最大的体系结构更改之一。

更改之后,我再也没有进行任何基准测试(或优化)。

我们可以在Go 1.9中研究它。

@bradfitz

只是局外人的一个简短问题,所以如果我错过了明显的事情,请忍受我。
为什么解决这个问题为时已晚? 这不是释放候选人的全部理由吗? 要在发布下一个主要版本之前找到最终的主要问题?

如果没有,请教育我。

另外,感谢您为这个项目所做的工作,我认识许多人喜欢这种语言以及周围的社区。 🎉

人们为什么要回应和投票,好像这是一个主要问题? 对于每个请求,此更改似乎对最坏情况的性能影响约为40us。 这对我来说听起来很低,在现实世界中有什么要紧的吗?

(编辑:我错了,每个请求约0.5us,所以甚至更低)

我认为可能需要围绕此问题制定更多基准,以了解对世界以外的其他事物的影响。 诸如hello world之类的事情对内部构件施加了很多基准压力,因此可以放大减速的影响。 在现实世界的应用程序中,运行着更多的代码,从理论上讲,这样的效果使每个请求的效果大大减小-即,每个请求的效果降低了%,这意味着效果降低了。
只是我的2美分。
(如果有时间的话,我稍后会详细介绍)

@reimertz我记得在这里阅读过有关发布周期的信息: https :

发布候选版本后,仅应进行文档更改和更改以解决严重的错误。 通常,此时的错误修复标准甚至比次要版本中的错误修复标准稍高。 与发布具有新但未经生产测试的修复程序相比,我们可能更喜欢发布具有已知但非常罕见的崩溃的发布。

发行候选版本的标准之一是Google默认将该版本的代码用于新的生产版本:如果我们Google不愿意将其用于生产,则不应要求其他人使用。

@kmlx感谢您的链接! 我只去了这里: https

还有,哇! 如果Google可以在其生产服务器上运行该版本,并且可以接受每个请求40us的最坏情况(引用@tinco ),那么我认为世界其他地区也可以。 🙂

这就是为什么我们要求人们测试Beta版本的原因。 这样一来,如果他们认为找到问题了,可以提早投诉。

go1.8beta1于2016年12月1日发布,超过2个月前:

https://groups.google.com/d/topic/golang-nuts/QYuo0fai6YE/discussion

引用公告:

发布候选版本这一点很重要。
候选版本计划于一月的第一周。
您在测试此Beta版方面的帮助非常宝贵。

对不起,我看错了数字。 因此,通过@OneOfOne的测试, go tip在30秒内发出了5110713个请求,即每个请求5.87us。 Go 1.7.5在30秒内发出了5630803个请求,每个请求5.33us。 因此,将它们相互比较时,性能下降了11%。 但是,如果从绝对的角度来看它,那么每个请求的性能下降仅为半微秒。 我什至无法想象HTTP服务会在其中发挥作用。

@tinco我同意,从角度来看,这只是一个很小的回归。 但是,弄清楚它为什么会退化仍然非常重要。 除非您遇到以下情况,否则:#6853和Go 1.9会再降低11%。

话虽这么说,我不确定为什么不能通过补丁发布(相对于次要版本)来解决。

@tinco ,该计算机有多少个内核? 将0.5us乘以内核数。

2017年2月8日,星期三,4:13 PM,Sokolov Yura [email protected]
写道:

那台计算机有多少个核心? 将0.5us乘以内核数。

为什么内核数量与此有关。
每个请求仅由单个内核处理。

@minux 500万个请求在30秒内由N个内核处理。 因此,每个请求在每个核心上花费的实时时间为30 / 5M *N。N可能很小,可能少于10,因此这并不是很重要。

次要版本适用于严重的,不可避免的错误。 在一天的执行之后,您的程序就有1%的概率随机崩溃,这是我们在点发行版中修复的错误,它是最简单,最安全,最简单的修复。 次要发行版不是用于解决“您的程序运行慢了0.5us才能执行可能比总时间长得多的操作”。

我猜您指的是修补程序版本( 1.8.x )与次要版本( 1.x.0 )。
IMO发行补丁的全部目的是在不改变功能或任何行为的情况下解决回归问题。 尽管这看起来不大,但我认为没有理由不将其修复在1.8.patch

在Go项目中,我们将1.x称为主要版本,将1.xy称为次要(或有时是点)版本:Go 1.8是主要版本; Go 1.8.2是次要版本或点发行版本。 我们称Go 1.8为次要版本是没有意义的。

Go项目发布政策在https://golang.org/doc/devel/release.html#policy上有说明

每个主要的Go版本均已过时,并终止了对前一个版本的支持。 例如,如果已经发布了Go 1.5,则它是当前版本,并且不再支持Go 1.4和更早版本。 我们会通过发布次要修订(例如,Go 1.5.1,Go 1.5.2等)来解决当前版本中的关键问题。

Go 1.5向后兼容Go 1.4,但有一些警告:您的代码不必依赖未记录的行为(例如,sort.Sort选择的相等元素的顺序),您的代码必须使用键控结构常量,因此它不会如果添加了新的struct字段则中断,等等

尽最大可能,Go 1.5.1与Go 1.5向后兼容,没有任何此类警告:我们的目标是从Go 1.5升级到Go 1.5.1,以确保操作安全,这是非事件,如您所说:而不更改功能或任何行为”。

接受错误是编写软件不可避免的一部分,并且每当您进行更改时,就有风险会引入一个在发行前无法通过测试发现的错误,我们知道最好的降低风险的方法是禁止点释放的非关键更改。

修复仅在微基准测试中出现的0.5us减慢是一项非关键性的更改。

@tinco如果我们不考虑这个“所谓的次要”性能问题,并且每次我们忽略它,我们最终都会“走慢”。 IMO没有人希望这种情况发生。

@ rashidul0405这个问题仍然存在; 没有人会忽略它。 我在解释为​​什么它不值得匆忙修复到Go 1.8或Go 1.8.1中。

我很乐意将Go的速度降低1%,而不将速成的Go降低1%。

让我们继续讨论邮件列表和Twitter等。

如果您正在处理此问题,请仅在此处发表评论。

由于似乎没有人愿意对此进行处理,因此我将关闭它。 我们无法追踪所有可能的性能细节,而这个细节已经过时了。

此页面是否有帮助?
0 / 5 - 0 等级