Go: net / http: HTTP / 1-Server wurde in Go 1.8 um 0,5 μs langsamer

Erstellt am 6. Feb. 2017  ·  35Kommentare  ·  Quelle: golang/go

Bitte beantworten Sie diese Fragen, bevor Sie Ihr Problem einreichen. Vielen Dank!

Welche Version von Go verwenden Sie ( go version )?

Gehen Sie 1.7.5, 1.8 rc3 und git

Welche Betriebssystem- und Prozessorarchitektur verwenden Sie ( go env )?

Arch Linux 64bit

Was haben Sie gemacht?

go run https://gist.github.com/OneOfOne/4d7e13977886ddab825870bc3422a901
Wechseln Sie die Terminals und führen Sie wrk -c 20 -d 30s http://localhost:8081

Was hast du erwartet?

Gleicher Durchsatz wie 1,7 oder schneller.

Was hast du stattdessen gesehen?

Tipp td37b8ccf2

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

gehe 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

gehe 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

Hilfreichster Kommentar

Hallo @bradfitz

Nur eine kurze Frage von einem Außenstehenden. Bitte nehmen Sie Kontakt mit mir auf, wenn ich etwas Offensichtliches verpasst habe.
Warum sollte es zu spät sein, dieses Problem zu beheben? Ist das nicht der ganze Grund für Release-Kandidaten? Um endgültige Hauptprobleme zu finden, bevor die nächste Hauptversion veröffentlicht wird?

Wenn nicht, erziehe mich bitte.

Vielen Dank, dass Sie an diesem Projekt gearbeitet haben. Ich kenne viele Leute, die diese Sprache und die Gemeinschaft um sie herum lieben. 🎉

Alle 35 Kommentare

Für Go 1.8 ist es zu spät, aber wir können die Leistung von Go 1.9 untersuchen.

Möchte jemand den Unterschied untersuchen? Was sagt ein Go 1.7 vs Go 1.8 CPU-Profil?

Aktualisiertes Spiel mit pprof: https://play.golang.org/p/GZ4zQOg1Wf

Ich habe go tool pprof http://localhost:6060/debug/pprof/profile , die Begriffe gewechselt und wrk -c 20 -d 30s http://localhost:6060/ .

Tipp

(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

gehe 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

Lassen Sie mich wissen, ob ich etwas Bestimmtes tun soll.

Autsch, ich sehe dasselbe mit einer Verlangsamung meines Webserver-Benchmarks um 20% mit 1,8r3 und einer etwas größeren Binärgröße

Mir ist klar, dass es spät im Veröffentlichungszyklus ist, aber eine Regression von 20% auf http ist für viele Menschen eine enorme Regression.

Ich bin bereit, beim Debuggen zu helfen, wenn Sie mir sagen, was @bradfitz benötigt.

Ich bin bereit, beim Debuggen zu helfen, wenn Sie mir sagen, was @bradfitz benötigt.

Schritt 1 ist das Debuggen, warum es langsamer wurde. Wenn Sie Hinweise finden, lassen Sie es mich wissen.

@OneOfOne Wie funktioniert das für Anwendungen außerhalb der Welt? Sehen Sie dort irgendwelche Probleme? Vielleicht sind 20% für Hello World-Apps bis 1.9 akzeptabel?

@bradfitz Ich denke, die Funktion für Shutdown führt zu einer geringeren Leistung für die Art von "Hallo Welt" -Apps / -Tests.

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

Schau hier .

Bei 1,7 war es nur eine for Schleife.

Kann jemand meine Annahme bestätigen?

Vielen Dank,
Kataras

Ich kann die Ergebnisse des OP nicht reproduzieren. Ich bin auf einem Mac und verwende leicht ältere Versionen von 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

Es gibt einen kleinen Unterschied, aber er ist kleiner als 20%, hier fast vernachlässigbar.

@ Kataras , hast du Beweise für diese Theorie?

Sie können es selbst bestätigen: Löschen Sie diese Zeilen und messen Sie erneut.

Ich wäre jedoch überrascht.

@kataras Das sieht so aus, als ob es wahrscheinlich so ist. Zusätzlich zur Auswahl haben Sie einen Mutex-Erwerb und eine Freischaltung, die in einem Aufschub durchgeführt wird (von dem ich weiß, dass er kürzlich beschleunigt wurde, aber immer noch etwas langsamer als eine direkte Freischaltung ist).

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

Da die Akzeptanzschleife ein so heißer Pfad ist, kann es sinnvoll sein, die Abschaltauswahl in eine separate Goroutine zu verschieben und mit sync / atomar das Herunterfahren in der Akzeptanzschleife zu signalisieren.

BEARBEITEN:
Ich denke nicht, dass es nur das ist, nur versuchtes Trinkgeld ohne die Auswahl alle zusammen und es fügt ungefähr 7us (5% ~) hinzu.

Ich verstehe, dass dies für 1.8 zu spät ist, aber warum sollte dies nicht in einer zukünftigen Version 1.8.1 behoben werden?

Hat noch jemand diese Ergebnisse reproduzieren können?

-11,2% auf Debian 8, amd64 (1.7.5 und rc3)

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 Die Implementierung ist korrekt, was bedeutet, dass der Autor vorsichtig war. Es ist nicht so

Das heißt, ich habe mich sowieso geirrt, ich habe gerade noch einmal nachgesehen und festgestellt, dass die Auswahl nur dann erfolgt, wenn beim Akzeptieren ein Fehler auftritt. Seltsam ist, wie es mein Programm konsequent verlangsamt, wenn ich es aus dem Akzeptieren entferne. Vielleicht disqualifiziert es diesen Block von einigen Optimierungsdurchläufen oder Inlining? Es kann irgendwo ein Problem geben, das eine Verlangsamung verursacht, aber es ist woanders und erfordert ein anderes Setup als meins.

@bradfitz Dies halbiert kurz und scheint zu begehen, dass faf882d1d427e8c8a9a1be00d8ddcab81d1e848e zumindest einen Teil davon verursacht. Ich sehe einen Performance-Hit von ungefähr 10% bei dieser Benchmark mit diesem Commit im Vergleich zu dem Commit davor.

@codido , danke für die Halbierung! Ja, und das ist das Commit, das ich auch vermuten würde. Gut, Bestätigung zu sehen. Das war eine der größten architektonischen Änderungen im Netz / http.Server seit geraumer Zeit.

Nach dieser Änderung habe ich kein Benchmarking (oder Optimierungen) mehr durchgeführt.

Wir können es für Go 1.9 untersuchen.

Hallo @bradfitz

Nur eine kurze Frage von einem Außenstehenden. Bitte nehmen Sie Kontakt mit mir auf, wenn ich etwas Offensichtliches verpasst habe.
Warum sollte es zu spät sein, dieses Problem zu beheben? Ist das nicht der ganze Grund für Release-Kandidaten? Um endgültige Hauptprobleme zu finden, bevor die nächste Hauptversion veröffentlicht wird?

Wenn nicht, erziehe mich bitte.

Vielen Dank, dass Sie an diesem Projekt gearbeitet haben. Ich kenne viele Leute, die diese Sprache und die Gemeinschaft um sie herum lieben. 🎉

Warum reagieren die Leute und stimmen ab, als ob dies ein großes Problem ist? Diese Änderung scheint einen Worst-Case-Performance-Effekt von ~ 40us pro Anforderung zu haben. Das klingt für mich sehr leise. Gibt es ein reales Szenario, in dem dies von Bedeutung wäre?

(bearbeiten: Ich habe mich geirrt, es ist ~ 0,5us pro Anfrage, also noch niedriger)

Ich denke, es muss möglicherweise mehr Benchmarks geben, um die Auswirkungen auf andere Dinge als die Hallo-Welt zu sehen. Dinge wie Hallo Welt üben einen hohen Benchmark-Druck auf die Interna aus und können als solche die Auswirkungen von Verlangsamungen verstärken. In einer realen Anwendung wird mehr Code für das Laden von Booten ausgeführt, wodurch der Effekt solcher Dinge theoretisch pro Anforderung weitaus geringer wird - dh er wird pro Anfrage weniger als%, was bedeutet, dass der Effekt verringert wird.
Nur meine 2 Cent.
(Ich werde mir das später genauer ansehen, wenn ich Zeit habe.)

@reimertz Ich erinnere mich, dass ich hier über den Veröffentlichungszyklus https://github.com/golang/go/wiki/Go-Release-Cycle

Sobald ein Release-Kandidat ausgestellt wurde, sollten nur Dokumentationsänderungen und Änderungen zur Behebung kritischer Fehler vorgenommen werden. Im Allgemeinen ist die Leiste für Fehlerkorrekturen zu diesem Zeitpunkt sogar etwas höher als die Leiste für Fehlerkorrekturen in einer Nebenversion. Wir ziehen es möglicherweise vor, eine Version mit einem bekannten, aber sehr seltenen Absturz herauszugeben, als eine Version mit einem neuen, aber nicht produktionstesteten Fix herauszugeben.

Eines der Kriterien für die Ausgabe eines Release-Kandidaten ist, dass Google diese Version des Codes standardmäßig für neue Produktions-Builds verwendet: Wenn wir bei Google nicht bereit sind, ihn für die Produktion auszuführen, sollten wir andere nicht dazu auffordern.

@kmlx danke für den Link! Ich war nur hier: https://golang.org/doc/contribute.html und suchte nach 'release' und konnte nichts finden. Mein Fehler.

Auch wow! Wenn Google diese Version auf seinen Produktionsservern ausführt und mit einer Auswirkung auf die Leistung im schlimmsten Fall von ~ 40 us pro Anfrage (unter Angabe von @tinco )

Aus diesem Grund bitten wir die Leute, Beta-Versionen zu testen. Wenn sie glauben, ein Problem gefunden zu haben, können sie sich frühzeitig beschweren.

go1.8beta1 wurde am 1. Dezember 2016 vor mehr als 2 Monaten veröffentlicht:

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

Zitat der Ankündigung:

Es ist wichtig, dass wir Fehler finden, bevor wir einen Release-Kandidaten herausgeben.
Der Release Candidate ist für die erste Januarwoche geplant.
Ihre Hilfe beim Testen dieser Beta ist von unschätzbarem Wert.

Entschuldigung, ich habe die Zahlen falsch angeschaut. Nach dem Test von go tip in 30 Sekunden 5110713 Anfragen gestellt, das sind 5,87 us pro Anfrage. Go 1.7.5 hat 5631803 Anfragen in 30 Sekunden ausgeführt, 5.33us pro Anfrage. Wenn Sie diese also miteinander vergleichen, bedeutet dies einen Leistungsabfall von 11%. Wenn Sie es jedoch aus einer absoluten Perspektive betrachten, ist dies ein Leistungseinbruch von nur einer halben Mikrosekunde pro Anforderung. Ich kann mir nicht einmal einen HTTP-Dienst vorstellen, bei dem dies relevant wäre.

@tinco Ich stimme zu, es ist eine sehr kleine Regression, wenn man sie relativiert. Es ist jedoch immer noch sehr wichtig herauszufinden, warum es zurückgegangen ist. Es sei denn, Sie möchten eine Situation wie: # 6853 und Go 1.9 kommen mit einem weiteren Rückgang von 11%.

Davon abgesehen bin ich mir nicht sicher, warum dies mit einer Patch-Version (im Vergleich zu einer Nebenversion) nicht behoben werden konnte.

@tinco , wie viele Kerne hat dieser Computer? Multiplizieren Sie 0,5us mit der Anzahl der Kerne.

Am Mittwoch, 8. Februar 2017, um 16:13 Uhr, Sokolov Yura [email protected]
schrieb:

Wie viele Kerne hat dieser Computer? Multiplizieren Sie 0,5us mit der Anzahl der Kerne.

Warum hat die Anzahl der Kerne damit zu tun?
Jede Anforderung wird nur von einem einzelnen Kern verarbeitet.

@minux Die 5 Millionen Anfragen werden in 30 Sekunden von N Kernen bearbeitet. Die pro Anforderung für jeden Kern aufgewendete Echtzeit beträgt also 30 / 5M * N. N ist wahrscheinlich eher klein, weniger als 10 wahrscheinlich, daher ist es nicht wirklich relevant.

Kleinere Releases sind für kritische, unvermeidbare Fehler vorgesehen. Ihr Programm hat eine Wahrscheinlichkeit von 1%, dass es nach einem Tag der Ausführung zufällig abstürzt. Dies ist die Art von Fehler, die wir in einer Punktveröffentlichung beheben, mit der einfachsten, sichersten und trivialsten Korrektur, die möglich ist. Kleinere Releases dienen nicht dazu, "Ihr Programm läuft 0,5us langsamer, um einen Vorgang auszuführen, der wahrscheinlich ohnehin viel länger dauert".

Ich denke, Sie beziehen sich auf Patch-Releases ( 1.8.x ) vs Minor ( 1.x.0 ).
IMO ging es bei Patch-Releases darum, Regressionen zu beheben, ohne die Funktionen oder das Verhalten zu ändern. Während dieses nicht wie ein großes aussieht, sehe ich keinen Grund, es nicht in einem 1.8.patch zu reparieren.

Im Go-Projekt bezeichnen wir 1.x als Hauptversion und 1.xy als Nebenversion (oder manchmal als Punktversion): Go 1.8 ist eine Hauptversion; Go 1.8.2 ist eine Neben- oder Punktversion. Es macht für uns keinen Sinn, Go 1.8 als Nebenversion zu bezeichnen.

Die Freigaberichtlinie für das Go-Projekt ist unter https://golang.org/doc/devel/release.html#policy dokumentiert:

Jede wichtige Go-Version veraltet und beendet die Unterstützung für die vorherige. Wenn beispielsweise Go 1.5 veröffentlicht wurde, handelt es sich um die aktuelle Version, und Go 1.4 und frühere Versionen werden nicht mehr unterstützt. Wir beheben kritische Probleme in der aktuellen Version nach Bedarf, indem wir kleinere Revisionen herausgeben (z. B. Go 1.5.1, Go 1.5.2 usw.).

Go 1.5 ist mit einigen Einschränkungen abwärtskompatibel mit Go 1.4: Ihr Code muss nicht von undokumentiertem Verhalten abhängen (z. B. der Reihenfolge gleicher Elemente, die durch sort.Sort ausgewählt werden). Ihr Code muss verschlüsselte Strukturliterale verwenden, damit dies nicht der Fall ist brechen, wenn neue Strukturfelder hinzugefügt werden, und so weiter .

Go 1.5.1 ist so weit wie möglich abwärtskompatibel mit Go 1.5 ohne solche Einschränkungen: Wir möchten von Go 1.5 auf Go 1.5.1 aktualisieren, um einen garantiert sicheren Betrieb zu gewährleisten, ein Nicht-Ereignis, wie Sie sagen. " ohne Funktionen oder Verhalten zu ändern ".

Das Akzeptieren, dass Fehler ein unvermeidlicher Bestandteil des Schreibens von Software sind und dass jedes Mal, wenn Sie eine Änderung vornehmen, das Risiko besteht, dass ein Fehler auftritt, der nicht durch Tests vor der Veröffentlichung erkannt wird. Der beste Weg, dieses Risiko zu verringern, besteht darin, ihn nicht zuzulassen unkritische Änderungen in Punktfreigaben.

Das Beheben einer Verlangsamung um 0,5 us, die nur in Mikrobenchmarks angezeigt wird, ist eine unkritische Änderung.

@tinco Wenn wir dieses "sogenannte kleinere" Leistungsproblem nicht berücksichtigen und jedes Mal, wenn wir es ignorieren, werden wir letztendlich "langsamer". IMO will niemand, dass das passiert.

@ rashidul0405 Dieses Problem ist noch offen; niemand ignoriert es. Ich habe erklärt, warum es keine schnelle Korrektur in Go 1.8 oder Go 1.8.1 verdient.

Ich würde gerne einen 1% langsameren Go anstelle eines 1% Crashier Go nehmen.

Lassen Sie uns die Diskussion über Mailinglisten und Twitter usw. fortsetzen.

Bitte kommentieren Sie hier nur, wenn Sie an diesem Problem arbeiten.

Da anscheinend niemand daran arbeiten möchte, werde ich es schließen. Wir können nicht jedes mögliche Leistungsdetail aufspüren, und dieses wird alt.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen