Go: net / http:HTTP / 1サーバーはGo1.8で0.5μs遅くなりました

作成日 2017年02月06日  ·  35コメント  ·  ソース: golang/go

問題を送信する前に、これらの質問に答えてください。 ありがとう!

どのバージョンのGoを使用していますか( go version )?

1.7.5、1.8rc3およびgitに移動します

どのオペレーティングシステムとプロセッサアーキテクチャを使用していますか( go env )?

Arch Linux64ビット

あなたは何をした?

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.8rc3758a7281abに移動します

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には遅すぎますが、Go1.9のパフォーマンスを調べることができます。

誰かが違いを調査したいですか? Go1.7とGo1.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を使用したWebサーバーベンチマークで約20%の速度低下が見られ、バイナリサイズもわずかに大きくなっています。

リリースサイクルが遅いことはわかっていますが、httpでの20%のリグレッションは、多くの人にとって大きなリグレッションです。

@bradfitzに必要なものを教えていただければ、デバッグのお手伝いをさせていただきます。

@bradfitzに必要なものを教えていただければ、デバッグのお手伝いをさせていただきます。

ステップ1は、速度が低下した理由をデバッグすることです。 手がかりを見つけたら、私に知らせてください。

@OneOfOneこれは

@bradfitz Shutdownの機能により、「 HelloWorld 」アプリ/テストの種類のパフォーマンスが

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

ここを見て

1.7では、それは単なるforループでした。

誰かが私の仮定を確認できますか?

ありがとう、
カタール

OPの結果を再現できません。 私はMacを使用しており、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()
}

受け入れループはそのようなホットパスであるため、シャットダウン選択を別のゴルーチンに移動し、同期/アトミックを使用して受け入れループのシャットダウンを通知することをお勧めします。

編集:
これだけではないと思います。選択せずにチップを試してみると、約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実装は正しいです。つまり、作者は注意を払っていました。ソフトウェアでバグが発生するのはそれほど大きな問題ではありません。

とにかく間違っていたと言っていましたが、今もう一度見て、受け入れ中にエラーが発生した場合にのみ選択が行われることに気付きました。 奇妙なのは、acceptからプログラムを削除すると、プログラムが一貫して遅くなることです。 多分それはいくつかの最適化パスまたはインライン化からそのブロックを失格にしますか? どこかで速度低下の原因となる問題があるかもしれませんが、それはどこか別の場所であり、私のものとは異なる設定が必要です。

@bradfitzこれを簡単に二等分し、コミットfaf882d1d427e8c8a9a1be00d8ddcab81d1e848eが少なくともその一部を引き起こしているようです。 このコミットでは、先行するコミットと比較して、このベンチマークで約10%のパフォーマンスヒットが見られます。

@codido

その変更後、ベンチマーク(または最適化)を行ったことはありません。

Go1.9で調べることができます。

こんにちは@bradfitz

部外者からの簡単な質問ですので、明らかなことを見逃した場合はご容赦ください。
この問題を修正するには遅すぎるのはなぜですか? これがリリース候補のすべての理由ではありませんか? 次のメジャーバージョンをリリースする前に、最終的な主要な問題を見つけるには?

そうでない場合は、私を教育してください。

また、このプロジェクトに取り組んでくれてありがとう、私はこの言語とその周りのコミュニティを愛する多くの人々を知っています。 🎉

なぜ人々はこれが大きな問題であるかのように反応し、投票するのですか? この変更は、リクエストごとに最大40usの最悪の場合のパフォーマンスへの影響があるようです。 それは私には非常に低いように聞こえますが、これが問題になる現実のシナリオはありますか?

(編集:私は間違っていました、それはリクエストごとに〜0.5usなので、さらに低くなります)

hello world以外のものへの影響を確認するには、これに関するベンチマークをさらに増やす必要があると思います。 ハローワールドのようなものは、内部に多くのベンチマーク圧力をかけているため、減速の影響を拡大する可能性があります。 実際のアプリケーションでは、実行されるコードがボートにロードされ、理論的にはこのようなものの効果がリクエストごとにはるかに小さくなります。つまり、リクエストごとの%が少なくなり、効果が低下します。
ちょうど私の2セント。
(時間があれば、後で正確にこれを見ていきます)

@reimertzここでリリースサイクルについて読んだことを覚えています: https

リリース候補が発行されたら、ドキュメントの変更と重大なバグに対処するための変更のみを行う必要があります。 一般に、この時点でのバグ修正の基準は、マイナーリリースのバグ修正の基準よりもわずかに高くなっています。 新しいが本番テストされていない修正を含むリリースを発行するよりも、既知であるが非常にまれなクラッシュを含むリリースを発行する方がよい場合があります。

リリース候補を発行するための基準の1つは、Googleがデフォルトでそのバージョンのコードを新しい本番ビルドに使用していることです。Googleで本番用に実行する意思がない場合は、他の人に依頼するべきではありません。

@kmlxリンクをありがとう! https://golang.org/doc/contribute.htmlにアクセスして「リリース」を検索したところ、何も見つかりませんでした。 私の悪い。

また、すごい! Googleが本番サーバーでこのバージョンを実行し、リクエストごとに最大40usのパフォーマンスへの最悪の影響で問題がない場合( @tincoを引用)、世界の他の地域でも同様に実行できると思います。 🙂

これが、ベータリリースをテストするように人々に依頼する理由です。 そのため、問題を見つけたと思った場合は、早期に不満を言うことができます。

go1.8beta1は、2か月以上前の2016年12月1日にリリースされました。

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

発表の引用:

リリース候補を発行する前にバグを見つけることが重要です。
リリース候補は1月の第1週に予定されています。
このベータ版のテストにおけるあなたの助けは非常に貴重です。

申し訳ありませんが、番号を間違えて見ました。 したがって、 @ OneOfOneのテストから、 go tipは30秒で5110713リクエストを作成しました。これは、リクエストあたり5.87usです。 Go 1.7.5は、30秒で5631803リクエストを実行し、リクエストごとに5.33usを実行しました。 したがって、これらを相互に比較すると、パフォーマンスが11%低下するようなものです。 しかし、絶対的な観点から見ると、リクエストごとにわずか0.5マイクロ秒のパフォーマンスヒットになります。 これが関係するHTTPサービスを想像することすらできません。

@tinco同意します。立てると、非常に小さな回帰です。 ただし、それが後退した理由を理解することは依然として非常に重要です。 次のような状況が必要な場合を除いて、#6853とGo 1.9ではさらに11%の減少が見られます。

そうは言っても、パッチリリース(マイナーリリースと比較して)でこれを修正できなかった理由はわかりません。

@tinco 、そのコンピューターにはいくつのコアがありますか? 0.5usにコア数を掛けます。

16:13の水曜日、2017年2月8日には、ソコロフ由良[email protected]
書きました:

そのコンピューターにはいくつのコアがありますか? 0.5usにコア数を掛けます。

コアの数がこれに関係しているのはなぜですか。
各リクエストは、単一のコアによってのみ処理されます。

@minux 500万のリクエストは、Nコアによって30秒で処理されます。 したがって、各コアでリクエストごとに費やされる実際の時間は30 / 5M * Nです。Nはおそらくかなり小さく、おそらく10未満であるため、実際には関係ありません。

マイナーリリースは、重大で避けられないバグ用です。 プログラムは、実行日後にランダムにクラッシュする可能性が1%あります。これは、ポイントリリースで修正する種類のバグであり、可能な限り最も単純で、最も安全で、最も簡単な修正です。 マイナーリリースは、「プログラムの実行速度が0.5us遅くなり、とにかく全体よりもはるかに長い時間がかかる可能性がある」を修正するためのものではありません。

パッチリリース( 1.8.x )とマイナー( 1.x.0 )を参照していると思います。
IMOのパッチリリースの要点は、機能や動作を変更せずにリグレッションに対処することでした。 これは大きなもののようには見えませんが、 1.8.patch修正しない理由はありません。

Goプロジェクトでは、1.xをメジャーリリース、1.xyをマイナー(またはポイント)リリースと呼びます。Go1.8はメジャーリリースです。 Go 1.8.2は、マイナーリリースまたはポイントリリースです。 Go1.8をマイナーリリースと呼ぶのは意味がありません。

Goプロジェクトのリリースポリシーはhttps://golang.org/doc/devel/release.html#policyで文書化されてい

各メジャーGoリリースは廃止され、前のリリースのサポートが終了します。 たとえば、Go 1.5がリリースされている場合、それは現在のリリースであり、Go1.4以前はサポートされなくなります。 マイナーリビジョン(たとえば、Go 1.5.1、Go 1.5.2など)を発行することにより、必要に応じて現在のリリースの重大な問題を修正します。

Go1.5はGo1.4と下位互換性がありますが、いくつかの注意点があります。コードは文書化されていない動作(たとえば、sort.Sortによって選択された等しい要素の順序)に依存する必要はありません。コードはキー付き構造体リテラルを使用する必要があるため、そうではありません。新しい構造体フィールドが追加された場合は中断ます。

可能な限り、Go1.5.1はGo1.5と下位互換性がありますが、そのような警告はありません。Go1.5からGo 1.5.1に更新して、安全性が保証された操作であり、イベントではないことを目指しています。機能や動作を変更せずに」。

間違いを犯すことはソフトウェア作成の必然的な部分であり、変更を加えるたびにリリース前のテストでは検出されないバグが発生するリスクがあることを受け入れると、そのリスクを軽減するための最善の方法は許可しないことです。ポイントリリースの重要ではない変更。

マイクロベンチマークにのみ表示される0.5usの速度低下を修正することは、重要ではありません。

@tincoこの「いわゆるマイナーな」パフォーマンスの問題を考慮せず、それを無視するたびに、最終的には「遅くなる」ことになります。 IMOは誰もこれが起こることを望んでいません。

@ rashidul0405この問題はまだ開いています。 誰もそれを無視していません。 Go1.8またはGo1.8.1に急いで修正する価値がない理由を説明していました。

私は喜んで1%クラッシュするGoの代わりに1%遅いGoを取ります。

メーリングリストやツイッターなどで議論を続けましょう。

この問題に取り組んでいる場合にのみ、ここにコメントしてください。

誰もこれに取り組みたくないようですので、閉じます。 考えられるすべてのパフォーマンスの詳細を追跡することはできず、これは古くなっています。

このページは役に立ちましたか?
0 / 5 - 0 評価