Je crains de faire une erreur, si c'est le cas, effacez-moi et je m'excuse. En particulier, je ne vois pas comment je crée les petites annotations soignées que certains ont créées. Cependant, j'ai de nombreuses préoccupations/observations à faire sur ce fil.
1) L'élément commenté dans le pseudo-code d'une des réponses populaires
result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );
est essentiellement bidon. Si le fil est en train de calculer, alors il ne se tourne pas les pouces, il fait le travail nécessaire. Si, d'autre part, il attend simplement la fin d'une entrée/sortie, alors il est pas en utilisant le temps CPU, l'objectif de l'infrastructure de contrôle des threads dans le noyau est que le CPU trouve quelque chose d'utile à faire. La seule façon de "se tourner les pouces" comme suggéré ici serait de créer une boucle d'interrogation, et personne qui a codé un vrai serveur web n'est assez inepte pour le faire.
2) "Les threads sont difficiles", n'a de sens que dans le contexte du partage des données. Si vous avez des threads essentiellement indépendants, comme c'est le cas lorsque vous traitez des requêtes web indépendantes, alors le threading est trivialement simple, il suffit de coder le flux linéaire de la façon de traiter une tâche, et de rester assis en sachant qu'il traitera plusieurs requêtes, et que chacune sera effectivement indépendante. Personnellement, je dirais que pour la plupart des programmeurs, l'apprentissage du mécanisme de fermeture/callback est plus complexe que le simple codage de la version top-to-bottom du thread. (Mais oui, si vous devez communiquer entre les threads, la vie devient très vite difficile, mais je ne suis pas convaincu que le mécanisme de closure/callback change vraiment cela, il limite juste vos options, parce que cette approche est toujours réalisable avec les threads. De toute façon, c'est une toute autre discussion qui n'est pas vraiment pertinente ici).
3) Jusqu'à présent, personne n'a présenté de véritable preuve de la raison pour laquelle un type particulier de changement de contexte prendrait plus ou moins de temps qu'un autre type. Mon expérience dans la création de noyaux multi-tâches (à petite échelle pour des contrôleurs embarqués, rien d'aussi sophistiqué qu'un "vrai" système d'exploitation) suggère que ce ne serait pas le cas.
4) Toutes les illustrations que j'ai vues jusqu'à présent et qui prétendent montrer à quel point Node est plus rapide que les autres serveurs web sont horriblement défectueuses, cependant, elles sont défectueuses d'une manière qui illustre indirectement un avantage que j'accepterais définitivement pour Node (et ce n'est en aucun cas insignifiant). Node ne semble pas avoir besoin (ni même permettre, en fait) de s'accorder. Si vous avez un modèle threadé, vous devez créer suffisamment de threads pour gérer la charge attendue. Si vous le faites mal, vous obtiendrez des performances médiocres. S'il y a trop peu de threads, le processeur est inactif, mais incapable d'accepter d'autres demandes. Si vous créez trop de threads, vous gaspillerez la mémoire du noyau et, dans le cas d'un environnement Java, vous gaspillerez également la mémoire du tas principal. Or, pour Java, gaspiller du heap est le premier, le meilleur moyen de foutre en l'air les performances du système, parce qu'un garbage collector efficace (actuellement, cela pourrait changer avec G1, mais il semble que le jury n'ait pas encore tranché sur ce point au début de 2013 au moins) dépend de la disponibilité de beaucoup de heap. Donc, voilà le problème, réglez-le avec trop peu de threads, vous avez des processeurs inactifs et un faible débit, réglez-le avec trop de threads, et il s'enlise d'autres façons.
5) Il y a une autre façon dont j'accepte la logique de l'affirmation que l'approche de Node "est plus rapide par conception", et c'est la suivante. La plupart des modèles de threads utilisent un modèle de changement de contexte en tranches de temps, superposé au modèle préemptif plus approprié (alerte au jugement de valeur :) et plus efficace (pas un jugement de valeur). Cela se produit pour deux raisons, premièrement, la plupart des programmeurs ne semblent pas comprendre la préemption prioritaire, et deuxièmement, si vous apprenez le threading dans un environnement Windows, le timeslicing est là que vous le vouliez ou non (bien sûr, cela renforce le premier point ; notamment, les premières versions de Java utilisaient la préemption prioritaire sur les implémentations Solaris, et le timeslicing sous Windows. Comme la plupart des programmeurs ne comprenaient pas et se plaignaient que "le threading ne fonctionne pas sous Solaris", ils ont changé le modèle pour utiliser le timeslice partout). Quoi qu'il en soit, l'essentiel est que le timeslicing crée des commutations de contexte supplémentaires (et potentiellement inutiles). Chaque changement de contexte prend du temps au CPU, et ce temps est effectivement retiré du travail qui peut être fait sur le vrai travail à faire. Cependant, le temps investi dans le changement de contexte à cause du timeslicing ne devrait pas dépasser un très faible pourcentage du temps global, à moins que quelque chose d'extraordinaire ne se produise, et il n'y a aucune raison de s'attendre à ce que ce soit le cas dans un simple serveur web). Donc, oui, les commutations de contexte excessives impliquées dans le timeslicing sont inefficaces (et elles ne se produisent pas dans le cas d'un serveur web simple). Noyau ) mais la différence sera de quelques pourcentages de débit, pas le genre de facteurs entiers qui sont impliqués dans les revendications de performance qui sont souvent impliquées pour Node.
Quoi qu'il en soit, je vous prie de m'excuser si tout cela est long et décousu, mais j'ai vraiment l'impression que jusqu'à présent, la discussion n'a rien prouvé, et je serais heureux d'entendre quelqu'un qui se trouve dans l'une ou l'autre de ces situations :
a) une véritable explication de la raison pour laquelle Node devrait être meilleur (au-delà des deux scénarios que j'ai décrits ci-dessus, dont le premier (mauvais réglage) est, je crois, la véritable explication de tous les tests que j'ai vus jusqu'à présent. ([edit], en fait, plus j'y pense, plus je me demande si la mémoire utilisée par un grand nombre de piles pourrait être significative ici. La taille des piles par défaut pour les threads modernes a tendance à être assez énorme, mais la mémoire allouée par un système d'événements basé sur les fermetures serait seulement ce qui est nécessaire).
b) un vrai benchmark qui donne une chance équitable au serveur threadé de son choix. Au moins de cette façon, je devrais arrêter de croire que les revendications sont essentiellement fausses ;> ([edit] c'est probablement un peu plus fort que ce que j'avais l'intention de dire, mais j'ai le sentiment que les explications données pour les avantages en termes de performances sont au mieux incomplètes, et que les benchmarks présentés ne sont pas raisonnables).
A la vôtre, Toby