Veuillez répondre à ces questions avant de soumettre votre problème. Merci!
go version
)?Allez 1.7.5, 1.8 rc3 et git
go env
)?Arch Linux 64 bits
go run
https://gist.github.com/OneOfOne/4d7e13977886ddab825870bc3422a901
changer de terminal et exécuter wrk -c 20 -d 30s http://localhost:8081
Même débit que 1.7 ou plus rapide.
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
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
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
Trop tard pour Go 1.8, mais nous pouvons examiner les performances pendant Go 1.9.
Quelqu'un veut-il enquêter sur la différence? Que dit un profil de processeur Go 1.7 vs Go 1.8?
Jeu mis à jour avec pprof: https://play.golang.org/p/GZ4zQOg1Wf
J'ai couru go tool pprof http://localhost:6060/debug/pprof/profile
, changé les termes et exécuté 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
(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
Faites-moi savoir si vous voulez que je fasse quelque chose de spécifique.
Aïe, je vois la même chose avec un ralentissement d'environ 20% sur mon benchmark de serveur Web avec 1.8r3, et une taille binaire légèrement plus grande aussi
Je me rends compte qu'il est tard dans le cycle de publication, mais une régression de 20% sur http est une énorme régression pour beaucoup de gens.
Je suis prêt à aider au débogage si vous me dites ce qui est nécessaire @bradfitz.
Je suis prêt à aider au débogage si vous me dites ce qui est nécessaire @bradfitz.
L'étape 1 consiste à déboguer la raison pour laquelle il est devenu plus lent. Si vous trouvez des indices, faites-le moi savoir.
@OneOfOne comment cela fonctionne-t-il pour les applications autres que Hello World? Y voyez-vous des problèmes? Peut-être que 20% pour les applications Hello World sont acceptables jusqu'à la 1.9?
@bradfitz Je pense que la fonctionnalité pour Shutdown
entraîne une baisse des performances pour le type d'applications / tests «bonjour le monde».
select {
case <-srv.getDoneChan():
return ErrServerClosed
//....
Regardez ici .
À 1,7, c'était juste une boucle for
.
Quelqu'un peut-il confirmer mon hypothèse?
Merci,
kataras
Je ne suis pas en mesure de reproduire les résultats du PO. Je suis sur un Mac et j'utilise des versions plus anciennes de 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
Il y a une légère différence mais elle est inférieure à 20%, presque négligeable ici.
@kataras , avez-vous des preuves en faveur de cette théorie?
Vous pouvez le confirmer vous-même: supprimez ces lignes et mesurez à nouveau.
Je serais surpris, cependant.
@kataras On dirait que c'est probablement ça. En plus de la sélection, vous avez une acquisition mutex et un déverrouillage qui se fait dans un différé (qui, je sais, a récemment été accéléré, mais qui sont toujours un peu plus lents qu'un déverrouillage direct.
func (s *Server) getDoneChan() <-chan struct{} {
s.mu.Lock()
defer s.mu.Unlock()
return s.getDoneChanLocked()
}
Puisque la boucle d'acceptation est un chemin si chaud, il peut être intéressant de déplacer la sélection d'arrêt dans un goroutine séparé et d'utiliser sync / atomic pour signaler l'arrêt dans la boucle d'acceptation.
ÉDITER:
Je ne pense pas que ce soit juste ça, j'ai juste essayé la pointe sans sélectionner tous ensemble et cela ajoute environ 7us (5% ~).
Je comprends que c'est trop tard pour la 1.8, mais pourquoi ne pas peut-être résoudre ce problème dans une future version 1.8.1?
Quelqu'un d'autre a-t-il même pu reproduire ces résultats?
-11,2% sur Debian 8, amd64 (1.7.5 et 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 L'implémentation est correcte, ce qui signifie que l'auteur a été prudent ... ce n'est pas si grave que des bugs se produisent dans les logiciels.
Cela dit, j'avais tort de toute façon, j'ai regardé à nouveau tout à l'heure et j'ai réalisé que la sélection ne se produit que lorsqu'une erreur se produit lors de l'acceptation. Ce qui est étrange, c'est comment cela a ralenti mon programme de manière constante lorsque je le supprime de l'acceptation. Peut-être que cela disqualifie ce bloc de certaines passes d'optimisation ou de l'intégration? Il peut y avoir un problème quelque part provoquant un ralentissement, mais il est ailleurs et nécessite une configuration différente de la mienne.
@bradfitz a brièvement divisé cela en deux et semble commettre faf882d1d427e8c8a9a1be00d8ddcab81d1e848e en est à l'origine au moins en partie. Je constate une baisse de performance d'environ 10% sur ce benchmark avec ce commit par rapport au commit qui le précède.
@codido , merci pour la bissectrice! Oui, et c'est le commit que je soupçonnerais aussi. Bon à voir la confirmation. Ce fut l'un des plus gros changements architecturaux du net / http.Server depuis un certain temps.
Je n'ai jamais fait de benchmarking (ou d'optimisation) après ce changement.
Nous pouvons l'examiner pour Go 1.9.
Salut @bradfitz
Juste une petite question d'un étranger, alors s'il vous plaît soyez patient si j'ai raté quelque chose d'évident.
Pourquoi serait-il trop tard pour résoudre ce problème? N'est-ce pas là toute la raison de la libération des candidats? Pour trouver les derniers problèmes majeurs avant de publier la prochaine version majeure?
Sinon, renseignez-moi.
Aussi, merci d'avoir travaillé sur ce projet, je connais beaucoup de gens qui aiment cette langue et la communauté qui l'entoure. 🎉
Pourquoi les gens répondent-ils et votent-ils comme s'il s'agissait d'un problème majeur? Ce changement semble avoir un impact sur les performances dans le pire des cas d'environ 40us par demande. Cela me semble très bas, y a-t-il un scénario dans le monde réel où cela aurait de l'importance?
(modifier: je me suis trompé, c'est ~ 0,5us par demande, donc encore plus bas)
Je pense qu'il faudra peut-être plus de repères à ce sujet pour voir l'effet sur des choses autres que bonjour le monde. Des choses comme hello world exercent une forte pression de référence sur les composants internes et peuvent en tant que telles amplifier les effets des ralentissements. Dans une application du monde réel, il y a une charge de bateau plus de code qui s'exécute, ce qui en théorie rend l'effet de choses comme celle-ci beaucoup plus petit par requête - c'est-à-dire qu'il devient moins d'un% par requête, ce qui signifie que l'effet est réduit.
Juste mes 2 cents.
(Je vais regarder précisément cela plus tard si j'ai le temps)
@reimertz Je me souviens avoir lu sur le cycle de publication ici: https://github.com/golang/go/wiki/Go-Release-Cycle
Une fois qu'une version candidate est publiée, seuls les changements de documentation et les changements pour résoudre les bogues critiques doivent être effectués. En général, la barre pour les corrections de bogues à ce stade est même légèrement plus élevée que la barre pour les corrections de bogues dans une version mineure. Nous préférons peut-être publier une version avec un crash connu mais très rare plutôt qu'une version avec un nouveau correctif mais non testé en production.
L'un des critères pour émettre une version candidate est que Google utilise cette version du code pour les nouvelles versions de production par défaut: si chez Google, nous ne sommes pas disposés à l'exécuter pour une utilisation en production, nous ne devrions pas demander aux autres de le faire.
@kmlx merci pour le lien! Je suis seulement allé ici: https://golang.org/doc/contribute.html et j'ai recherché 'release' et je n'ai rien trouvé. Ma faute.
Aussi, wow! Si Google exécute cette version sur ses serveurs de production et accepte un impact sur les performances dans le pire des cas d'environ 40us par requête (en citant @tinco ), je suppose que le reste du monde le peut aussi. 🙂
C'est pourquoi nous demandons aux gens de tester les versions bêta. Ainsi, s'ils croient avoir trouvé un problème, ils peuvent se plaindre tôt.
go1.8beta1 est sorti le 1 décembre 2016 , il y a plus de 2 mois:
https://groups.google.com/d/topic/golang-nuts/QYuo0fai6YE/discussion
Citant l'annonce:
Il est important de trouver des bogues avant de publier une version candidate.
La version candidate est prévue pour la première semaine de janvier.
Votre aide pour tester cette version bêta est inestimable.
Désolé, j'ai mal regardé les chiffres. Donc, d' après le test de go tip
fait 5110713 requêtes en 30 secondes, soit 5,87us par requête. Go 1.7.5 a fait 5631803 requêtes en 30 secondes, 5,33us par requête. Donc, lorsque vous les comparez les uns aux autres, c'est comme une baisse de performance de 11%. Mais si vous regardez les choses d'un point de vue absolu, cela représente une performance de seulement une demi-microseconde par requête. Je ne peux même pas imaginer un service HTTP où cela serait pertinent.
@tinco Je suis d'accord, c'est une régression très mineure quand on la met en perspective. Cependant, il est toujours très important de comprendre pourquoi il a régressé. À moins que vous ne souhaitiez une situation comme: # 6853 et Go 1.9 se présente avec une autre diminution de 11%.
Cela étant dit, je ne sais pas pourquoi cela n'a pas pu être corrigé avec une version de correctif (par rapport à une version mineure).
@tinco , combien de cœurs cet ordinateur a-t-il? multipliez 0,5us par le nombre de cœurs.
Le mercredi 8 février 2017 à 16:13, Sokolov Yura [email protected]
a écrit:
combien de cœurs cet ordinateur a-t-il? multipliez 0,5us par le nombre de cœurs.
Pourquoi le nombre de cœurs a-t-il à voir avec cela?
chaque requête n'est traitée que par un seul cœur.
@minux les 5 millions de requêtes sont traitées en 30 secondes par N cœurs. Ainsi, le temps réel passé par requête sur chaque cœur est de 30 / 5M * N.N est probablement assez petit, moins de 10 probablement donc ce n'est pas vraiment pertinent.
Les versions mineures concernent des bogues critiques et inévitables. Votre programme a 1% de chances de planter au hasard après une journée d'exécution, c'est le genre de bogue que nous corrigeons dans une version ponctuelle, avec le correctif le plus simple, le plus sûr et le plus trivial possible. Les versions mineures ne sont pas destinées à corriger "votre programme s'exécute 0,5us plus lentement pour exécuter une opération qui prend probablement beaucoup plus de temps de toute façon".
Je suppose que vous faites référence aux versions de correctifs ( 1.8.x
) vs mineures ( 1.x.0
).
OMI, le but des versions de correctifs était d'aborder les régressions sans modifier les fonctionnalités ni aucun comportement. Alors que celui-ci ne ressemble pas à un gros, je ne vois aucune raison de ne pas le réparer dans un 1.8.patch
.
Dans le projet Go, nous nous référons à 1.x comme une version majeure et 1.xy comme une version mineure (ou parfois ponctuelle): Go 1.8 est une version majeure; Go 1.8.2 est une version mineure ou ponctuelle. Cela n'a aucun sens pour nous d'appeler Go 1.8 une version mineure.
La politique de publication du projet Go est documentée à l' adresse https://golang.org/doc/devel/release.html#policy :
Chaque version majeure de Go devient obsolète et met fin à la prise en charge de la précédente. Par exemple, si Go 1.5 a été publié, il s'agit de la version actuelle et Go 1.4 et les versions antérieures ne sont plus prises en charge. Nous corrigeons les problèmes critiques dans la version actuelle selon les besoins en publiant des révisions mineures (par exemple, Go 1.5.1, Go 1.5.2, etc.).
Go 1.5 est rétrocompatible avec Go 1.4 avec quelques mises en garde: votre code ne doit pas dépendre d'un comportement non documenté (par exemple, l'ordre des éléments égaux choisis par sort.Sort), votre code doit utiliser des littéraux de struct clé, donc il ne le fait pas break si de nouveaux champs struct sont ajoutés, et ainsi de suite .
Dans toute la mesure du possible, Go 1.5.1 est rétrocompatible avec Go 1.5 sans aucune mise en garde: nous visons la mise à jour de Go 1.5 vers Go 1.5.1 pour être une opération de sécurité garantie, un non-événement, comme vous dites " sans modifier les fonctionnalités ni aucun comportement ».
Accepter que faire des erreurs est une partie inévitable de l'écriture d'un logiciel et que chaque fois que vous apportez un changement, il y a un risque qu'il introduise un bogue qui n'est pas détecté par les tests avant la sortie, la meilleure façon que nous connaissons de réduire ce risque est de refuser changements non critiques dans les versions ponctuelles.
La correction d'un ralentissement de 0,5us qui n'apparaît que dans les microbenchmarks est un changement non critique.
@tinco si nous ne considérons pas ce problème de performance "soi-disant mineur" et chaque fois que nous l'ignorons, nous obtiendrons finalement "un coup plus lent". OMI, personne ne veut que cela se produise.
@ rashidul0405 Ce problème est toujours ouvert; personne ne l'ignore. J'expliquais pourquoi cela ne méritait pas une solution précipitée dans Go 1.8 ou Go 1.8.1.
Je prendrais volontiers un Go 1% plus lent au lieu d'un Go 1% plus crash.
Continuons la discussion sur les listes de diffusion et Twitter, etc.
Veuillez ne commenter ici que si vous travaillez sur ce problème.
Puisque personne ne semble vouloir travailler là-dessus, je vais le fermer. Nous ne pouvons pas rechercher tous les détails de performance possibles, et celui-ci vieillit.
Commentaire le plus utile
Salut @bradfitz
Juste une petite question d'un étranger, alors s'il vous plaît soyez patient si j'ai raté quelque chose d'évident.
Pourquoi serait-il trop tard pour résoudre ce problème? N'est-ce pas là toute la raison de la libération des candidats? Pour trouver les derniers problèmes majeurs avant de publier la prochaine version majeure?
Sinon, renseignez-moi.
Aussi, merci d'avoir travaillé sur ce projet, je connais beaucoup de gens qui aiment cette langue et la communauté qui l'entoure. 🎉