8
votes

Le fil du fil par demande peut-il être plus rapide que les E / S non bloquantes?

Je me souviens d'il y a 2 ou 3 ans en train de lire quelques articles où les gens ont affirmé que les bibliothèques de filetage modernes devaient si bien que les serveurs thread-per-requis ne soient pas seulement plus faciles à écrire que les serveurs non bloquants, mais ils seraient plus rapide aussi. Je crois que cela a été démontré à Java avec une JVM qui a cartographié les fils Java à Pthreads (c'est-à-dire que la surcharge de Java Nio était supérieure à la tête de commutation de contexte).

Mais maintenant je vois que tous les serveurs "tranchants" utilisent des bibliothèques asynchrones (Java Nio, Epoll, même nœud.js). Cela signifie-t-il que ASYNC a gagné?


0 commentaires

4 Réponses :


6
votes

Il est plus rapide tant qu'il y a suffisamment de mémoire.

Lorsqu'il y a trop de connexions, la plupart d'entre eux sont inactifs, Nio peut enregistrer des threads, ce qui permet de sauvegarder la mémoire et du système peut gérer beaucoup plus d'utilisateurs que le modèle de thread-per-connexion.

La CPU n'est pas un facteur direct ici. Avec Nio, vous devez effectivement mettre en œuvre un modèle de filetage vous-même, ce qui est peu susceptible d'être plus rapide que les threads de JVM.

Dans l'un ou l'autre choix, la mémoire est le goulot d'étranglement ultime. Lorsque la charge augmente et les approches de mémoire utilisées Max, GC sera très occupé et le système démontre souvent le symptôme de 100% de processeur.


1 commentaires

Ça a du sens. Une recherche Google rapide montre que la pile par défaut par hotspot par fil sur des machines de 64 bits est de 1 Mo (je sais que ce n'est pas un tas, mais cela réduirait la quantité de mémoire restante pour le tas). Cela seul signifie que le thread-per-demande n'est pas adapté à C10K, au moins en Java. Mais si la mémoire est la contrainte, cela pourrait être approprié dans les centaines de connexions simultanées.



7
votes

pas à mon avis. Si les deux modèles sont bien implémentés (il s'agit d'une grande exigence), je pense que le concept de Nio devrait prévaloir.

au cœur d'un ordinateur sont des noyaux. Peu importe ce que vous faites, vous ne pouvez pas parlementer votre application plus que vous n'avez de nœuds. I.e. Si vous avez une machine à 4 noyau, vous ne pouvez faire que 4 choses à la fois (je brille sur certains détails ici, mais cela suffit à cet argument).

Développer sur cette pensée, si vous avez déjà plus de threads que les noyaux, vous avez des gaspilles. Ce déchet prend deux formes. La première est la surcharge des fils supplémentaires eux-mêmes. Deuxièmement, le temps passé à changer de threads. Les deux sont probablement mineurs, mais ils sont là.

Idéalement, vous avez un seul fil par noyau et chacun de ces threads fonctionne à une vitesse de traitement de 100% sur leur noyau. La commutation de tâche ne se produirait pas dans l'idéal. Bien sûr, il y a le système d'exploitation, mais si vous prenez une machine à 16 noyau et laissez 2-3 threads pour le système d'exploitation, les 13-14 restants vont vers votre application. Ces threads peuvent changer ce qu'ils font dans votre application, comme lorsqu'ils sont bloqués par les exigences de l'IO, mais n'ont pas à payer ce coût au niveau du système d'exploitation. Ecrivez-le directement dans votre application.

Un excellent exemple de cette mise à l'échelle est vu dans Seda http: // www. eecs.harvard.edu/~mdw/proj/seda/ . Il a montré une grande meilleure mise à l'échelle sous charge qu'un modèle standard de thread-per-demandes.

Mon expérience personnelle est avec Netty. J'ai eu une application simple. Je l'ai bien impliqué dans Tomcat et Netty. Ensuite, je l'ai testé avec 100 demandes simultanées (plus de 800, je crois). Finalement, Tomcat a ralenti à un crawl et a exposé un comportement extrêmement éclaté / laggy. ATTENDU QUE la mise en œuvre de Netty a simplement augmenté le temps de réponse, mais s'est poursuivi avec un débit extrêmement global.

Veuillez noter que ces charnières sur la mise en œuvre solide. Nio s'améliore toujours avec le temps. Nous apprenons à régler nos serveurs OSES pour mieux fonctionner, ainsi que sur la manière de mettre en œuvre la JVMS afin de mieux exploiter la fonctionnalité du système d'exploitation. Je ne pense pas qu'un gagnant puisse être déclaré encore, mais je crois que Nio sera le vainqueur éventuel, et cela va déjà bien.


2 commentaires

C'est un test réel très intéressant. Pour un certain nombre d'applications, 800 demandes simultanées ne sont vraiment pas beaucoup. Je me demande si le décalage à Tomcat était dû au passage au contexte (ou à GC, comme suggère @ hirreputable), ce qui signifierait que le modèle thread-per-requier était en faute plutôt que la mise en œuvre de Tomcat.


Pour être juste, il s'agissait d'un seul serveur qui a été testé, pas un cluster. Quant à 800 demandes. Dans mon expérience, 800 simultanés sont assez élevés. Il est important de distinguer les demandes simultanées des connexions. Si vous obtenez des temps de réponse dans la plage de 100 ms à cette charge, vous prenez en charge des demandes de 8 km par seconde. Ce n'est pas mauvais pour une seule boîte. Échelle de l'échelle pendant toute une journée et vous gérez 700 millions de demandes avec un seul serveur. Peu de nombreuses applications n'ont pas besoin de beaucoup.



4
votes

Il y a quelque temps, j'ai trouvé présentation plutôt intéressante fournir une idée de la perspicacité "Pourquoi l'ancien fil-plat -Client modèle est meilleur ". Il y a même des mesures. Cependant, je pense toujours à cela. À mon avis, la meilleure réponse à cette question est "cela dépend" car la plupart des décisions d'ingénierie (sinon toutes) sont des échanges.


1 commentaires

Je pense que cela a peut-être été la présentation que je me souviens. Ces observations (Nio peuvent être lentes) combinées à la nouvelle appréciation de la simultanéité basée sur l'acteur à Erlang et Scala m'a fait penser que le monde se dirigeait vers des modèles synchrones. Mais en dehors de ces langues (et Scala qui trichent de Scala), je pense que je vais rester synchrone comme par défaut jusqu'à ce que je frappe des problèmes du monde réel comme ceux rencontrés par @RFeak.



1
votes

Comme cette présentation dit - Il y a une vitesse et il y a une évolutivité.

Un scénario où le thread-per-requérant sera presque certainement plus rapide que toute solution ASYNC est lorsque vous avez un nombre relativement petit de clients (par exemple <100), mais chaque client est très élevé. par exemple. Une application en temps réel où plus de 100 clients envoient / générant 500 messages une seconde chacune. Le modèle de thread-par demande sera certainement plus efficace que toute solution de boucle d'événement ASYNC. ASYNC échoue mieux lorsqu'il existe de nombreuses demandes / clients car il ne gaspille pas de cycles en attente de nombreuses connexions clientes, mais lorsque vous avez peu de clients de volume élevé avec peu d'attente, c'est moins efficace.

D'après ce que j'ai vu, les auteurs du nœud et Netty reconnaissent tous deux que ces cadres sont destinés à aborder principalement des volumes élevés / de nombreuses situations d'évolutivité des connexions plutôt que d'être plus rapides pour un plus petit nombre de clients à volume élevé.


0 commentaires