Quels sont les inconvénients d'une implémentation d'exécution JavaScript multi-thread? [fermé]

51

Je travaille sur une implémentation d'exécution JavaScript multi-thread depuis une semaine. J'ai une preuve de concept faite en C ++ en utilisant JavaScriptCore et boost.

L’architecture est simple: lorsque le moteur d’exécution termine l’évaluation du script principal, il se lance et rejoint un pool de threads, qui commence à sélectionner des tâches dans une file d’attente de priorité partagée. .

Travail d'exécution multithread de node.js

Le problème est que lorsque je présente cette conception à un programmeur JavaScript, je reçois des commentaires extrêmement négatifs et je ne sais pas pourquoi. Même en privé, ils disent tous que JavaScript est censé être à thread unique, que les bibliothèques existantes devront être réécrites, et que les gremlins apparaîtront et mangeront tous les êtres vivants si je continue à travailler là-dessus.

J'avais à l'origine une implémentation coroutine native (utilisant des contextes boostés) également, mais je devais l'abandonner (JavaScriptCore est pédant à propos de la pile), et je ne voulais pas risquer leur colère, j'ai donc décidé de ne pas le mentionner.

Qu'est-ce que tu penses? JavaScript est-il censé être à thread unique et doit-il être laissé seul? Pourquoi tout le monde s'oppose-t-il à l'idée d'une exécution simultanée de JavaScript?

Edit: Le projet est maintenant sur GitHub , testez -le vous-même et dites-moi ce que vous pensez.

Vous trouverez ci-dessous une image des promesses exécutées sur tous les cœurs de la CPU en parallèle, sans contention:

Exécution simultanée de promesses.

voodooattack
la source
7
Cela semble être une question fortement d'opinion. Avez-vous demandé aux personnes qui apparemment n’avaient pas aimé votre idée pourquoi elles pensent que ce sera gênant?
5gon12eder
26
Ajouter des threads à quelque chose qui n'est pas destiné à être multithread, c'est comme convertir une route à une voie en une autoroute sans fournir la direction du conducteur. Cela fonctionnera assez bien la plupart du temps, jusqu'à ce que les gens commencent à se planter de manière aléatoire. Avec le multithreading, vous aurez soit des bugs de synchronisation subtiles que vous ne pouvez pas reproduire, soit un comportement erratique la plupart du temps. Vous devez concevoir avec cela à l'esprit. Vous avez besoin de la synchronisation des threads. Faire des variables atomiques n'élimine pas les conditions de concurrence.
mgw854
2
Comment envisagez-vous de gérer l'accès multi-thread à l'état partagé? "Marqué comme atomique et se disputer l'accès" n'explique pas comment vous pensez que cela fonctionnerait vraiment. J'imagine que les attitudes négatives vis-à-vis de l'idée sont dues au fait que les gens n'ont aucune idée de la façon dont vous feriez réellement ce travail. Ou, si vous imposez au développeur, comme en Java ou en C ++, tout le fardeau nécessaire pour utiliser les mutex appropriés, les utilisateurs se demanderont probablement pourquoi ils veulent cette complication et ce risque de programmation dans un environnement exempt de tout.
jfriend00
17
Parce que la coordination automatique des états aléatoires entre les threads est considérée comme un problème presque impossible, vous n'avez donc aucune crédibilité quant à la possibilité d'offrir tout ce qui le fait automatiquement. Et si vous voulez simplement que le développeur reprenne la charge, comme le fait Java ou C ++, la plupart des programmeurs de node.js ne veulent pas de cette charge - ils aiment que node.js ne soit pas obligé de gérer cela la plus grande partie. Si vous voulez une oreille plus sympathique, vous devrez expliquer / montrer comment et ce que vous offririez à cet égard et pourquoi cela serait utile et utile.
jfriend00
3
S'il vous plaît, continuez votre travail. Je considère les langues sans multi-threading comme des langues de jouet. Je pense que la plupart des développeurs JavaScript travaillent avec le navigateur, qui a un modèle mono-thread.
Chloé

Réponses:

70

1) Le multithreading est extrêmement difficile et, malheureusement, la façon dont vous avez présenté cette idée jusqu'à présent implique que vous sous-estimez sévèrement à quel point c'est difficile.

Pour le moment, on dirait que vous "ajoutez simplement des fils" au langage et que vous vous demandez comment le rendre correct et performant plus tard. En particulier:

si deux tâches tentent d'accéder simultanément à une variable, celle-ci est marquée atomique et se disputent l'accès.
...
Je conviens que les variables atomiques ne résoudront pas tout, mais mon prochain objectif est de rechercher une solution au problème de synchronisation.

Ajouter des discussions à Javascript sans "solution au problème de synchronisation" équivaudrait à ajouter des entiers à Javascript sans "solution au problème de l'addition". La nature même du problème est tellement fondamentale qu’il est en définitive inutile de discuter de la question de savoir si le multithreading vaut la peine d’être ajouté sans solution spécifique à l’esprit, peu importe à quel point nous le voudrions.

De plus, rendre toutes les variables atomiques est le genre de chose qui rendrait un programme multithread moins performant que son homologue monofil-lu, ce qui rend encore plus important de tester les performances sur des programmes plus réalistes et de voir si vous gagnez quelque chose ou non.

Je ne vois pas non plus clairement si vous essayez de masquer les discussions au programmeur node.js ou si vous prévoyez de les exposer à un moment donné, créant ainsi un nouveau dialecte Javascript pour la programmation multithread. Les deux options sont potentiellement intéressantes, mais il semblerait que vous n’ayez pas encore décidé laquelle vous visiez.

Donc, pour le moment, vous demandez aux programmeurs d’envisager de passer d’un environnement à un seul thread à un nouvel environnement multithread qui n’a pas de solution pour le problème de synchronisation ni aucune preuve qu’il améliore les performances dans le monde réel, et apparemment aucun plan pour résoudre ces problèmes.

C'est probablement pourquoi les gens ne vous prennent pas au sérieux.

2) La simplicité et la robustesse de la boucle d’événement unique constituent un avantage considérable .

Les programmeurs Javascript savent que le langage Javascript est "à l'abri" des conditions de concurrence et d'autres bugs extrêmement insidieux qui minent toute programmation véritablement multithread. Le fait qu’ils aient besoin d’arguments solides pour les convaincre d’abandonner cette sécurité ne les rend pas fermés d’esprit, ils les rendent responsables.

À moins que vous ne puissiez conserver cette sécurité, quiconque voudra passer à un nœud multithread sera probablement mieux de passer à un langage tel que Go, conçu dès le départ pour les applications multithread.

3) Javascript supporte déjà les "threads d'arrière-plan" (WebWorkers) et la programmation asynchrone sans exposer directement la gestion des threads au programmeur.

Ces fonctionnalités résolvent déjà de nombreux cas d'utilisation courants qui affectent les programmeurs Javascript dans le monde réel, sans pour autant renoncer à la sécurité de la boucle d'événement unique.

Avez-vous des cas d'utilisation spécifiques à l'esprit que ces fonctionnalités ne résolvent pas et pour lesquelles les programmeurs Javascript veulent une solution? Si tel est le cas, il serait judicieux de présenter votre fichier node.js multithread dans le contexte de ce cas d'utilisation spécifique.


Post-scriptum Qu'est-ce qui me convaincrait d'essayer de passer à une implémentation multithread de node.js?

Ecrivez un programme non-trivial dans Javascript / node.js qui, selon vous, bénéficierait d'un véritable multithreading. Effectuez des tests de performances sur cet exemple de programme dans un noeud normal et votre noeud multithread. Montrez-moi que votre version améliore considérablement les performances d'exécution, la réactivité et l'utilisation de plusieurs cœurs, sans introduire de bogues ni d'instabilité.

Une fois que vous avez fait cela, je pense que vous verrez des gens beaucoup plus intéressés par cette idée.

Ixrec
la source
1
1) D'accord, je reconnais que cela fait un moment que je remets à plus tard le problème de la synchronisation. Quand je dis que «deux tâches vont se disputer», ce n'est pas ma conception, c'est principalement une observation: dl.dropboxusercontent.com/u/27714141/… - Je ne suis pas sûr du genre de sorcellerie que JavaScriptCore effectue ici, mais ne devrait pas. cette chaîne est-elle corrompue si elle n’était pas intrinsèquement atomique? 2) Je suis fortement en désaccord. C'est la raison pour laquelle JS est considéré comme un langage jouet. 3) Les promesses ES6 seraient beaucoup plus performantes si elles étaient implémentées à l'aide d'un planificateur de pool de threads.
voodooattack
13
@voodooattack "C'est la raison pour laquelle JS est considéré comme un langage de jouet." Non, c'est la raison pour laquelle vous le considérez comme un langage de jouets. Des millions de personnes utilisent JS chaque jour et en sont parfaitement satisfaites, ainsi que leurs lacunes. Assurez-vous de résoudre un problème que suffisamment de personnes ont réellement, ce qui ne serait pas mieux résolu en changeant simplement de langue.
Chris Hayes
@ChrisHayes La question qui se pose est la suivante: pourquoi accepter ses lacunes lorsque je peux les corriger? La concurrence en tant que fonctionnalité améliorerait-elle JavaScript?
voodooattack
1
@voodooattack C'est la question. La simultanéité en tant que fonctionnalité améliorerait-elle le Javascript? Si vous pouvez obtenir une réponse de la communauté à ce qui n'est pas "non", alors peut-être que vous êtes sur quelque chose. Cependant, il semble que la boucle d'événement et la délégation native du nœud aux threads de travail pour bloquer les événements suffisent pour la plupart des tâches que les utilisateurs doivent effectuer. Je pense que si les gens ont vraiment besoin de Javascript fileté, ils utiliseront des travailleurs Javascript. Si vous pouvez trouver un moyen de faire en sorte que les utilisateurs travaillent avec des fonctions de première classe au lieu de fichiers JS, cependant ... alors vous pourriez vraiment être sur quelque chose.
lunchmeat317
7
@voodooattack Les personnes qui considèrent JavaScript comme un langage jouet ne savent pas de quoi elles parlent. Est-ce la langue de prédilection pour tout? Bien sûr que non, mais le rejeter comme tel est certainement une erreur. Si vous souhaitez dissiper la notion selon laquelle JS est un langage jouet, créez-y une application de production non triviale ou pointez-la vers une application existante. Le simple ajout de la concurrence ne changera pas l'esprit de ces personnes, de toute façon.
jpmc26
16

Devinez juste ici pour démontrer un problème dans votre approche. Je ne peux pas le tester contre la mise en œuvre réelle car il n'y a pas de lien nulle part ...

Je dirais que c'est parce que les invariants ne sont pas toujours exprimés par la valeur d'une variable et que "une variable" n'est pas suffisant pour être la portée d'un verrou dans le cas général. Par exemple, imaginons que nous ayons un invariant a+b = 0(le solde d’une banque avec deux comptes). Les deux fonctions ci-dessous garantissent que l'invariant est toujours maintenu à la fin de chaque fonction (l'unité d'exécution dans JS à un seul thread).

function withdraw(v) {
  a -= v;
  b += v;
}
function deposit(v) {
  b -= v;
  a += v;
}

Maintenant, dans votre monde multithread, ce qui se passe lorsque deux threads exécutent withdrawet depositen même temps? Merci, Murphy ...

(Vous pourriez avoir un code qui traite spécialement les + = et - =, mais ce n'est pas une aide. À un moment donné, vous aurez un état local dans une fonction, et avec aucun moyen de "verrouiller" deux variables en même temps, votre volonté invariante être violé.)


ÉDITER: si votre code est sémantiquement équivalent au code Go à l’ adresse https://gist.github.com/thriqon/f94c10a45b7e0bf656781b0f4a07292a , mon commentaire est exact ;-)

thriqon
la source
Ce commentaire est sans importance, mais les philosophes sont capables de verrouiller plusieurs objets, mais ils meurent de faim parce qu'ils les verrouillent dans un ordre incohérent.
Dietrich Epp
3
@voodooattack "Je viens de tester ..." N'avez-vous jamais rencontré un ordre d'exécution incohérent avec la planification des threads? Cela varie d'un cycle à l'autre, d'une machine à l'autre. S'asseoir et exécuter un seul test (voire une centaine de tests!) Sans identifier le mécanisme de planification des opérations est inutile.
jpmc26
4
@voodooattack Le problème est que tester simplement la simultanéité n'est pas utile. Vous devez être en mesure de prouver que l'invariant tiendra le coup ou que le mécanisme ne peut jamais être approuvé en production.
sapi
1
Mais vous n'avez donné à l'utilisateur aucun outil pour "utiliser le système de manière responsable", car il n'y a absolument aucun mécanisme de verrouillage. Rendre tout atomique donne une illusion de sécurité des threads (et vous prenez la performance de tout ce qui est atomique, même lorsque vous n’avez pas besoin d’un accès atomique), mais cela ne résout pas réellement la plupart des problèmes de concurrence tels que celui donné par thriqon ici. Pour un autre exemple, essayez de parcourir un tableau sur un thread alors qu'un autre thread ajoute ou supprime des éléments du tableau. D'ailleurs, qu'est-ce qui vous fait penser que l'implémentation de Array dans le moteur est même thread-safe?
Zach Lipton
2
@voodooattack Donc, si les utilisateurs ne peuvent utiliser vos threads que pour des fonctions sans données partagées ni effets secondaires (et qu'ils ne feraient pas mieux de les utiliser pour autre chose, car comme nous l'avons vu ici, il n'y a aucun moyen de garantir la sécurité des threads), alors quelle valeur offrez-vous sur Web Workers (ou sur l’une des nombreuses bibliothèques offrant une API plus utilisable autour de Web Workers)? Et les travailleurs Web sont beaucoup plus en sécurité, car ils empêchent l'utilisateur d'utiliser le système de manière "irresponsable".
Zach Lipton
15

Il y a une dizaine d'années, Brendan Eich (l'inventeur de JavaScript) a écrit un essai intitulé Threads Suck , qui est certainement l'un des rares documents canoniques de la mythologie du design de JavaScript.

Une autre question est de savoir si elle est correcte, mais je pense que cela a eu une grande influence sur la façon dont la communauté JavaScript pense à la simultanéité.

Erik Pukinskis
la source
31
La dernière personne que je prendrais conseil est Brendan Eich. Toute sa carrière est basée sur la création de JavaScript, ce qui est tellement grave que nous avons un tas d’outils créés pour tenter de surmonter sa foutaise innée. Pensez au nombre d’heures de développeur perdues dans le monde à cause de lui.
Phil Wright
12
Bien que je comprenne pourquoi vous écarteriez son opinion en général et même votre dédain pour JavaScript, je ne comprends pas comment votre point de vue permettrait de ne pas tenir compte de son expertise dans le domaine.
Billy Cravens
4
@BillyCravens haha, même le livre canonique sur Javascript: "Javascript the good parts " de Crockford explique le jeu dans le titre. Traitez les attitudes d'Eich de la même manière, tenez-vous-en à ce qu'il dit être bon :-)
gbjbaanb
13
@PhilWright: Sa première implémentation de Javascript était une variante de Lisp. Ce qui pour moi lui vaut un énorme respect. La décision de son patron de le forcer à remplacer la syntaxe Lisp par une syntaxe C-like n'est pas de sa faute. Le noyau de Javascript est toujours une exécution Lisp.
Slebetman
7
@PhilWright: Vous ne devriez pas blâmer Eich pour la folie de la langue. Il s'agissait de bogues dans les premières implémentations et du besoin de compatibilité ascendante pour un langage Web empêchant JS de mûrir. Les concepts de base forment encore un langage merveilleux.
Bergi
8

L'accès atomique ne se traduit pas par un comportement thread-safe.

Un exemple est le cas lorsqu'une structure de données globale doit être invalide lors d'une mise à jour, telle que le rehachage d'une table de hachage (lors de l'ajout d'une propriété à un objet, par exemple) ou le tri d'un tableau global. Pendant ce temps, vous ne pouvez autoriser aucun autre thread à accéder à la variable. Cela signifie essentiellement que vous devez détecter tous les cycles lecture-mise à jour-écriture et les verrouiller. Si la mise à jour est non triviale, cela finira par arrêter le territoire du problème.

Le Javascript a été unifié et a été sandboxé depuis le début et tout le code est écrit en tenant compte de ces hypothèses.

Cela présente un grand avantage en ce qui concerne les contextes isolés et permet à deux contextes distincts de s'exécuter dans des threads différents. Cela signifie également que les personnes qui écrivent en javascript n'ont pas besoin de savoir comment faire face aux conditions de course et à d'autres pitfals multi-thread.

monstre à cliquet
la source
C'est pourquoi je pense que l'API du planificateur devrait être plus avancée et être utilisée en interne pour les promesses et les fonctions sans effets secondaires.
voodooattack
6

Votre approche va-t-elle améliorer considérablement les performances?

Douteux. Vous devez vraiment le prouver.

Votre approche va-t-elle faciliter l'écriture de code?

Certainement pas, le code multithread est beaucoup plus difficile à obtenir que le code à un seul thread.

Votre approche sera-t-elle plus robuste?

Les impasses, les conditions de course, etc. sont un cauchemar à réparer.

Phil Wright
la source
2
Essayez de construire un rayon de recherche en utilisant node.js, essayez maintenant à nouveau avec des threads. Le multi-processus n'est pas tout.
voodooattack
8
@voodooattack, personne de sensé n'écrirait un traceur en javascript, avec ou sans thread, car il s'agit d'un algorithme relativement simple, mais gourmand en calculs, qui est mieux écrit dans un langage entièrement compilé, de préférence avec le support SIMD . Pour le type de problèmes pour lesquels javascript est utilisé, le multi-processus est largement suffisant.
Jan Hudec
@JanHudec: Hé, JS va également avoir le support de SIMD :-) hacks.mozilla.org/2014/10/introducing-simd-js
Bergi
2
@voodooattack Si vous ne les connaissez pas, jetez un coup d'œil à SharedArrayBuffers . JavaScript reçoit de plus en plus de concepts de concurrence, mais ils sont ajoutés avec une extrême prudence, en abordant des problèmes particuliers, en essayant de minimiser les mauvaises décisions de conception auxquelles nous devrons faire face pendant des années.
REINSTATE MONICA - Jeremy Banks
2

Votre implémentation ne consiste pas seulement à introduire la simultanéité, mais plutôt à introduire un moyen spécifique pour implémenter la simultanéité, c’est-à-dire la simultanéité par état mutable partagé. Au cours de l'histoire, les gens ont eu recours à ce type de concurrence, ce qui a entraîné de nombreux problèmes. Bien sûr, vous pouvez créer des programmes simples qui fonctionnent parfaitement avec l’utilisation de la simultanéité d’états mutables partagés, mais le véritable test de tout mécanisme n’est pas ce qu’il peut faire, mais il peut évoluer à mesure que le programme se complexifie et que de plus en plus de fonctionnalités sont ajoutées au programme. Rappelez-vous que les logiciels ne sont pas des objets statiques que vous construisez une fois et que vous en avez fini, mais qu’ils évoluent avec le temps et si un mécanisme ou un concept le permet.

Vous pouvez examiner d’autres modèles de concurrence (par exemple, la transmission de messages) pour vous aider à déterminer les avantages qu’ils procurent.

Ankur
la source
1
Je pense que les promesses ES6 bénéficieraient du modèle de mon implémentation, car elles ne se disputent pas l'accès.
voodooattack
Consultez la spécification WebWorkers. Il y a des paquets npm qui fournissent leur implémentation, mais vous pouvez les implémenter comme le noyau de votre moteur plutôt que comme un paquet
Ankur
JavaScriptCore (l'implémentation webkit de JS que j'utilise) les implémente déjà, c'est juste un drapeau de compilation.
voodooattack
D'accord. WebWorkers sont simultanés avec la transmission de messages. Vous pouvez essayer votre exemple de raytracer avec eux et le comparer à l'approche de l'état mutable.
Ankur
4
Le passage de message est toujours implémenté au-dessus de l'état mutable, ce qui signifie qu'il serait plus lent. Je ne vois pas ce que vous dites ici. : - /
voodooattack
1

C'est nécessaire. L'absence d'un mécanisme d'accès simultané de bas niveau dans le noeud js limite ses applications dans des domaines tels que les mathématiques et la bioinformatique, etc. En outre, une concurrence d'accès avec des threads n'entre pas nécessairement en conflit avec le modèle de concurence par défaut utilisé dans node. Il existe une sémantique bien connue pour les threads dans un environnement avec une boucle d'événement principale, telle que les frameworks ui (et nodejs), et même s'ils sont trop complexes pour la plupart des situations, ils ont toujours des utilisations valables.

Bien sûr, votre application Web moyenne ne nécessitera pas de threads, mais essayez de faire quelque chose de moins conventionnel, et l'absence d'une primitive de concurrence à bas niveau sonore vous éloignera rapidement de quelque chose qui la propose.

Dryajov
la source
4
Mais "mathématiques et bioinformatique" serait mieux écrit en C #, JAVA ou C ++
Ian
En réalité, Python et Perl sont les langues dominantes dans ces régions.
dryajov
1
En fait, la principale raison pour laquelle je l'implémente est due aux applications d'apprentissage automatique. Donc, vous avez un point.
voodooattack
@dryajov Soyez heureux de ne pas connaître l'ampleur de Fortran dans certains domaines de la science informatique ...
cmaster
@dryajov Parce qu'ils sont plus accessibles aux humains qui ne sont peut-être pas des programmeurs à temps plein, non pas parce qu'ils sont intrinsèquement meilleurs en séquençage du génome - nous avons des langages spécialement conçus comme R et des langages scientifiques compilés comme Fortran.
chat
0

Je crois vraiment que c'est parce que c'est une idée différente et puissante. Vous allez contre les systèmes de croyance. Les choses deviennent acceptées ou populaires via le réseau affecte non pas sur la base du mérite. De plus, personne ne veut s’adapter à une nouvelle pile. Les gens rejettent automatiquement les choses trop différentes.

Si vous pouvez trouver un moyen de le transformer en un module npm normal qui semble peu probable, certaines personnes l'utiliseront peut-être.

Jason Livesay
la source
Voulez-vous dire que les programmeurs JS sont liés au fournisseur par npm?
voodooattack
3
La question concerne un nouveau système d’exécution et non pas un module npm et la simultanéité mutable d’un état partagé est une vieille idée.
Ankur
1
@voodooattack Je veux dire, ils sont résolus à adopter l'approche qui est déjà populaire et il sera presque impossible de surmonter le biais du statu quo.
Jason Livesay