Quali sono gli svantaggi di realizzare un'implementazione runtime JavaScript multi-thread? [chiuso]


51

Ho lavorato su un'implementazione runtime JavaScript multi-thread per la scorsa settimana. Ho una prova del concetto realizzata in C ++ usando JavaScriptCore e boost.

L'architettura è semplice: quando il runtime termina la valutazione dello script principale si avvia e si unisce a un pool di thread, che inizia a selezionare le attività da una coda di priorità condivisa, se due attività tentano di accedere contemporaneamente a una variabile viene contrassegnata come atomica e si contendono l'accesso .

Runtime node.js multithread funzionante

Il problema è che quando mostro questo progetto a un programmatore JavaScript ricevo un feedback estremamente negativo e non ho idea del perché. Anche in privato, tutti dicono che JavaScript deve essere a thread singolo, che le librerie esistenti dovrebbero essere riscritte e che i gremlin genereranno e mangeranno ogni essere vivente se continuo a lavorare su questo.

Inizialmente avevo anche un'implementazione coroutine nativa (usando contesti boost), ma dovevo abbandonarla (JavaScriptCore è pedante riguardo allo stack) e non volevo rischiare la loro ira, quindi ho deciso di non menzionarlo.

Cosa ne pensi? JavaScript è pensato per essere single thread e dovrebbe essere lasciato solo? Perché tutti sono contrari all'idea di un runtime JavaScript simultaneo?

Modifica: il progetto è ora su GitHub , sperimentalo tu stesso e fammi sapere cosa ne pensi.

Di seguito è riportato un quadro delle promesse in esecuzione su tutti i core della CPU in parallelo senza contese:

Correre promesse contemporaneamente.


7
Sembra una domanda molto supponente. Hai chiesto alle persone a cui apparentemente non è piaciuta la tua idea perché pensano che sarà fastidioso?
5gon12eder

26
L'aggiunta di thread a qualcosa che non è pensato per essere multithread è come convertire una strada a una corsia in una superstrada senza fornire il driver. Funzionerà abbastanza bene per la maggior parte del tempo, fino a quando le persone non inizieranno a bloccarsi casualmente. Con il multithreading, avrai o meno dei bug temporali che non puoi riprodurre o un comportamento irregolare per la maggior parte del tempo. Devi progettare con questo in mente. È necessaria la sincronizzazione dei thread. Il solo fatto di rendere atomiche le variabili non elimina le condizioni di gara.
mgw854,

2
Come pensi di gestire l'accesso multi-thread allo stato condiviso? "Contrassegnato come atomico e contendersi per l'accesso" non spiega come pensi che funzionerebbe davvero. Immagino che gli atteggiamenti negativi nei confronti dell'idea siano dovuti al fatto che le persone non hanno idea di come faresti davvero questo lavoro. Oppure, se stai caricando tutto lo sviluppatore come in Java o C ++ per usare mutex adeguati e simili, allora le persone probabilmente pensano perché vogliono quella complicazione e il rischio di programmazione in un ambiente che è libero da esso.
jfriend00

17
Perché il coordinamento automatico dello stato casuale tra i thread è considerato un problema quasi impossibile, quindi non si ha la credibilità di poter offrire tutto ciò che lo fa automaticamente. E, se hai intenzione di rimettere il carico sullo sviluppatore come fanno Java o C ++, la maggior parte dei programmatori node.js non vuole quel peso - a loro piace che node.js non debba occuparsene per la maggior parte. Se vuoi un orecchio più comprensivo, dovrai spiegare / mostrare come e cosa offriresti a questo proposito e perché sarebbe buono e utile.
jfriend00,

3
Per favore, continua il tuo lavoro. Considero le lingue senza multi-threading come lingue giocattolo. Penso che la maggior parte degli sviluppatori JavaScript lavori con il browser, che ha un modello a thread singolo.
Chloe,

Risposte:


70

1) Il multithreading è estremamente difficile, e sfortunatamente il modo in cui hai presentato questa idea finora implica che stai sottovalutando gravemente quanto sia dura.

Al momento, sembra che tu stia semplicemente "aggiungendo discussioni" al linguaggio e ti preoccupi di come renderlo corretto e performante in seguito. In particolare:

se due attività tentano di accedere contemporaneamente a una variabile, questa viene contrassegnata come atomica e si contendono l'accesso.
...
Sono d'accordo che le variabili atomiche non risolveranno tutto, ma lavorare su una soluzione per il problema di sincronizzazione è il mio prossimo obiettivo.

Aggiungere thread a Javascript senza una "soluzione per il problema di sincronizzazione" sarebbe come aggiungere numeri interi a Javascript senza una "soluzione per il problema dell'aggiunta". È così fondamentale per la natura del problema che praticamente non ha nemmeno senso discutere se valga la pena aggiungere il multithreading senza una soluzione specifica in mente, non importa quanto potremmo volerlo.

Inoltre, rendere atomiche tutte le variabili è il genere di cose che probabilmente farà sì che un programma multithread abbia prestazioni peggiori rispetto alla sua controparte single-threaded, il che rende ancora più importante testare effettivamente le prestazioni su programmi più realistici e vedere se stai guadagnando qualcosa o meno.

Inoltre, non mi è chiaro se stai cercando di tenere nascosti i thread dal programmatore node.js o se hai intenzione di esporli ad un certo punto, creando effettivamente un nuovo dialetto di Javascript per la programmazione multithread. Entrambe le opzioni sono potenzialmente interessanti, ma sembra che tu non abbia ancora deciso quale stai puntando.

Quindi, al momento, stai chiedendo ai programmatori di prendere in considerazione il passaggio da un ambiente a thread singolo a un ambiente multithread nuovo di zecca che non ha soluzione per il problema di sincronizzazione e nessuna prova che migliora le prestazioni del mondo reale e apparentemente non ha alcun piano per risolvere tali problemi.

Questo è probabilmente il motivo per cui le persone non ti prendono sul serio.

2) La semplicità e la robustezza del singolo loop di eventi è un enorme vantaggio.

I programmatori Javascript sanno che il linguaggio Javascript è "sicuro" dalle condizioni di gara e da altri bug estremamente insidiosi che affliggono tutta la programmazione realmente multithread. Il fatto che abbiano bisogno di argomenti forti per convincerli a rinunciare al fatto che la sicurezza non li rende di mentalità chiusa, li rende responsabili.

A meno che tu non riesca in qualche modo a mantenere quella sicurezza, chiunque voglia passare a un nodo multithread.js probabilmente starebbe meglio passare a una lingua come Go progettata da zero per le applicazioni multithread.

3) Javascript supporta già i "thread in background" (WebWorkers) e la programmazione asincrona senza esporre direttamente la gestione dei thread al programmatore.

Queste funzionalità risolvono già molti dei casi d'uso comuni che interessano i programmatori Javascript nel mondo reale, senza rinunciare alla sicurezza del singolo loop di eventi.

Hai in mente casi d'uso specifici che queste funzionalità non risolvono e per cui i programmatori Javascript vogliono una soluzione? In tal caso, sarebbe una buona idea presentare il tuo node.js multithread nel contesto di quel caso d'uso specifico.


PS Cosa mi convincerebbe a provare a passare a un'implementazione node.js multithread?

Scrivi un programma non banale in Javascript / node.js che pensi possa trarre vantaggio da un vero multithreading. Eseguire test delle prestazioni su questo programma di esempio nel nodo normale e nel nodo multithread. Dimostrami che la tua versione migliora in modo significativo le prestazioni di runtime, la reattività e l'utilizzo di più core, senza introdurre alcun bug o instabilità.

Una volta che lo hai fatto, penso che vedrai le persone molto più interessate a questa idea.


1
1) Va bene, ammetto che sto rimandando il problema della sincronizzazione da un po 'di tempo. Quando dico che "due compiti contenderanno", questo non è il mio progetto, è principalmente un'osservazione: dl.dropboxusercontent.com/u/27714141/… - Non sono sicuro che tipo di stregoneria JavaScriptCore stia eseguendo qui, ma non dovrei ' questa stringa viene corrotta se non era intrinsecamente atomica? 2) Non sono assolutamente d'accordo. Questo è il motivo per cui JS è considerato un linguaggio giocattolo. 3) Le promesse di ES6 sarebbero molto più performanti se implementate usando uno scheduler di pool di thread.
voodooattack,

13
@voodooattack "Questo è il motivo per cui JS è considerato un linguaggio giocattolo." No, questo è il motivo per cui lo consideri un linguaggio giocattolo. Milioni di persone usano JS ogni giorno e ne sono perfettamente soddisfatti, carenze e tutto il resto. Assicurati di risolvere un problema che abbastanza altre persone hanno effettivamente, che non è meglio risolto semplicemente cambiando le lingue.
Chris Hayes,

@ChrisHayes La domanda è: perché tollerare i suoi difetti quando posso risolverli? La concorrenza come funzionalità migliorerebbe JavaScript?
voodooattack,

1
@voodooattack Questa è la domanda. La concorrenza come funzionalità migliorerebbe Javascript? Se riesci a ottenere una risposta della community a ciò che non è "no", allora forse sei su qualcosa. Tuttavia, sembra che il ciclo degli eventi e la delega nativa di Node ai thread di lavoro per bloccare gli eventi siano sufficienti per la maggior parte di ciò che le persone devono fare. Penso che se le persone hanno davvero bisogno di Javascript threaded, useranno i lavoratori Javascript. Se riesci a trovare un modo per far lavorare i lavoratori con funzioni di prima classe anziché con i file JS, tuttavia .... allora potresti davvero essere coinvolto in qualcosa.
lunchmeat317

7
@voodooattack Le persone che chiamano JavaScript un linguaggio giocattolo non sanno di cosa stanno parlando. È la lingua di riferimento per tutto? Certo che no, ma respingerlo in quel modo è certamente un errore. Se vuoi dissipare l'idea che JS sia un linguaggio giocattolo, crea un'applicazione di produzione non banale o punta a uno esistente. Il solo fatto di aggiungere la concorrenza non cambierà comunque le idee di quelle persone.
jpmc26,

16

Indovina qui per dimostrare un problema nel tuo approccio. Non riesco a testarlo con la vera implementazione in quanto non esiste alcun collegamento da nessuna parte ...

Direi che è perché gli invarianti non sono sempre espressi dal valore di una variabile e 'una variabile' non è sufficiente per essere l'ambito di un blocco nel caso generale. Ad esempio, immagina di avere un invariante a+b = 0(il saldo di una banca con due conti). Le due funzioni seguenti assicurano che l'invariante sia sempre tenuto alla fine di ogni funzione (l'unità di esecuzione in JS a thread singolo).

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

Ora, nel tuo mondo multithread, cosa succede quando due thread vengono eseguiti withdrawe depositallo stesso tempo? Grazie Murphy ...

(Potresti avere un codice che tratta + = e - = specialmente, ma questo non è di aiuto. Ad un certo punto, avrai uno stato locale in una funzione e senza alcun modo per "bloccare" due variabili contemporaneamente il tuo invariante essere violato).


EDIT: se il tuo codice è semanticamente equivalente al codice Go su https://gist.github.com/thriqon/f94c10a45b7e0bf656781b0f4a07292a , il mio commento è accurato ;-)


Questo commento è irrilevante, ma i filosofi sono in grado di bloccare più oggetti, ma muoiono di fame perché li bloccano in un ordine incoerente.
Dietrich Epp,

3
@voodooattack "Ho appena testato ..." Non hai mai riscontrato un ordine di esecuzione incoerente con la pianificazione dei thread? Varia da una corsa all'altra, da una macchina all'altra. Sedersi ed eseguire un singolo test (o anche un centinaio di test!) Senza identificare il meccanismo per la pianificazione delle operazioni è inutile.
jpmc26,

4
@voodooattack Il problema è che semplicemente testare la concorrenza non è utile. Devi essere in grado di dimostrare che l'invariante manterrà, o il meccanismo non potrà mai essere attendibile nella produzione.
sapi,

1
Ma non hai fornito all'utente alcuno strumento per "usare il sistema in modo responsabile" perché non c'è assolutamente alcun meccanismo di blocco. Rendere tutto atomico dà l'illusione della sicurezza del thread (e si prende il colpo di prestazione di tutto ciò che è atomico, anche quando non è necessario l'accesso atomico), ma in realtà non risolve la maggior parte dei problemi di concorrenza come quello che dà Thriqon qui. Per un altro esempio, prova a scorrere su un array su un thread mentre un altro thread aggiunge o rimuove elementi dall'array. Del resto, cosa ti fa pensare che l'implementazione del motore di Array sia persino thread-safe?
Zach Lipton,

2
@voodooattack Quindi se gli utenti possono usare i tuoi thread solo per funzioni senza dati condivisi o effetti collaterali (e sarebbe meglio non usarli per nient'altro, perché come abbiamo visto qui, non c'è modo di garantire la sicurezza dei thread), allora quale valore stai fornendo su Web Workers (o una delle tante librerie che forniscono un'API più utilizzabile su Web Workers)? E i Web Worker sono molto più sicuri, perché rendono impossibile per l'utente utilizzare il sistema "irresponsabilmente".
Zach Lipton,

15

Circa un decennio fa Brendan Eich (l'inventore di JavaScript) ha scritto un saggio chiamato Threads Suck , che è sicuramente uno dei pochi documenti canonici della mitologia del design di JavaScript.

Se è corretto è un'altra domanda, ma penso che abbia avuto una grande influenza sul modo in cui la comunità JavaScript pensa alla concorrenza.


31
L'ultima persona da cui vorrei ricevere consigli è Brendan Eich. Tutta la sua carriera si basa sulla creazione di JavaScript, il che è così grave che abbiamo un mucchio di strumenti creati per cercare di aggirare la sua innata cazzata. Pensa a quante ore degli sviluppatori sono state sprecate nel mondo a causa sua.
Phil Wright,

12
Mentre capisco perché dovresti scartare la sua opinione in generale, e persino il tuo disprezzo per JavaScript, non capisco come la tua prospettiva consentirebbe di ignorare la sua esperienza nel dominio.
Billy Cravens,

4
@BillyCravens haha, anche il libro canonico su Javascript: "Javascript le parti buone " di Crockford lascia il gioco nel titolo. Tratta gli atteggiamenti di Eich allo stesso modo, basta attenersi alle cose che dice che sono buone :-)
gbjbaanb

13
@PhilWright: La sua prima implementazione di Javascript è stata una variante Lisp. Il che per me lo guadagna enorme rispetto. La decisione del suo capo di costringerlo a sostituire la sintassi di Lisp con una sintassi di tipo C non è colpa sua. Javascript al suo interno è ancora un runtime Lisp.
slebetman,

7
@PhilWright: Non dovresti incolpare Eich per la schifezza della lingua. Sono bug nelle prime implementazioni e la necessità di retrocompatibilità per un linguaggio web che ha impedito a JS di maturare. I concetti chiave formano ancora un linguaggio meraviglioso.
Bergi,

8

L'accesso atomico non si traduce in un comportamento thread-safe.

Un esempio è quando una struttura di dati globale deve essere non valida durante un aggiornamento, ad esempio la ripetizione di un hashmap (ad esempio quando si aggiunge una proprietà a un oggetto) o l'ordinamento di un array globale. Durante quel periodo non puoi consentire a nessun altro thread di accedere alla variabile. Ciò significa sostanzialmente che è necessario rilevare l'intero ciclo di lettura-aggiornamento-scrittura e bloccarlo. Se l'aggiornamento non è banale, finirà per arrestare il territorio problematico.

Javascript è stato single threading e sandbox dall'inizio e tutto il codice è stato scritto tenendo conto di questi presupposti.

Questo ha un grande vantaggio per quanto riguarda i contesti isolati e consentire l'esecuzione di 2 contesti separati in thread diversi. Voglio anche dire che le persone che scrivono javascript non devono sapere come gestire le condizioni di gara e vari altri pitfal multi-thread.


Questo è il motivo per cui penso che l'API dello scheduler dovrebbe essere più avanzata e utilizzata internamente per promesse e funzioni che non hanno effetti collaterali.
voodooattack,

6

Il tuo approccio migliorerà significativamente le prestazioni?

Dubbioso. Hai davvero bisogno di dimostrarlo.

Il tuo approccio renderà più semplice / veloce la scrittura del codice?

Sicuramente no, il codice multithread è molte volte più difficile da ottenere rispetto al codice a thread singolo.

Il tuo approccio sarà più solido?

Deadlock, condizioni di gara ecc. Sono un incubo da risolvere.


2
Prova a creare un raytracer usando node.js, ora riprova con i thread. Il multiprocesso non è tutto.
voodooattack,

8
@voodooattack, nessuno nella loro mente corretta scriverebbe un ray-tracer usando javascript, con o senza thread, perché è un algoritmo relativamente semplice, ma ad alta intensità di calcolo che è meglio scritto in un linguaggio completamente compilato, preferibilmente uno con supporto SIMD . Per il tipo di problemi per i quali JavaScript viene utilizzato, il multi-processo è più che sufficiente.
Jan Hudec,

@JanHudec: Heh, JS otterrà anche il supporto SIMD :-) hacks.mozilla.org/2014/10/introducing-simd-js
Bergi

2
@voodooattack Se non ne sei a conoscenza, dai un'occhiata a SharedArrayBuffers . JavaScript sta ottenendo più costrutti di concorrenza, ma vengono aggiunti con estrema cautela, affrontando specifici punti deboli, cercando di ridurre al minimo le decisioni di progettazione sbagliate con cui dovremo convivere per anni.
REINSTATE MONICA -Jeremy Banks

2

L'implementazione non riguarda solo l'introduzione della concorrenza, ma piuttosto l'introduzione di un modo specifico per implementare la concorrenza, ovvero la concorrenza per stato mutabile condiviso. Nel corso della storia le persone hanno usato questo tipo di concorrenza e questo ha portato a molti tipi di problemi. Ovviamente puoi creare semplici programmi che funzionano perfettamente con l'utilizzo della concorrenza condivisa dello stato mutabile, ma il vero test di qualsiasi meccanismo non è quello che può fare, ma può ridimensionarsi man mano che il programma diventa complesso e sempre più funzionalità vengono aggiunte al programma. Ricorda che il software non è una cosa statica che costruisci una volta e che ne hai fatto, ma continua a evolversi nel tempo e se qualsiasi meccanismo o concetto può "

Puoi esaminare altri modelli di concorrenza (ad es. Il passaggio di messaggi) per aiutarti a capire che tipo di vantaggi offrono questi modelli.


1
Penso che le promesse di ES6 trarrebbero beneficio dal modello della mia implementazione, poiché non contendono l'accesso.
voodooattack,

Dai un'occhiata alle specifiche di WebWorkers. Ci sono pacchetti npm che forniscono la loro implementazione ma puoi implementarli come il nucleo del tuo motore piuttosto che come pacchetto
Ankur

JavaScriptCore (l'implementazione webkit di JS che sto usando) li implementa già, è solo un flag di compilazione.
voodooattack,

Ok. I WebWorker sono in concorrenza con il passaggio dei messaggi. Puoi provare il tuo esempio di raytracer con loro e confrontarlo con l'approccio dello stato mutabile.
Ankur,

4
Il passaggio dei messaggi è sempre implementato sopra lo stato mutabile, il che significa che sarebbe più lento. Non riesco a vedere il tuo punto qui. : - /
voodooattack

1

Questo è necessario. La mancanza di un meccanismo di concorrenza di basso livello nel nodo js limita le sue applicazioni in campi come la matematica e la bioinformatica, ecc ... Inoltre, la concorrenza con i thread non è necessariamente in conflitto con il modello di concordanza predefinito utilizzato nel nodo. Esistono semantiche ben note per il threading in un ambiente con un ciclo di eventi principale, come i framework dell'interfaccia utente (e nodejs), e tuttavia sono decisamente eccessivamente complesse per la maggior parte delle situazioni che hanno ancora usi validi.

Certo, la tua app web media non richiederà thread, ma prova a fare qualcosa di un po 'meno convenzionale e la mancanza di una primitiva di concorrenza di basso livello ti sposterà rapidamente a qualcos'altro che la offre.


4
Ma "matematica e bioinformatica" sarebbero meglio scritti in C #, JAVA o C ++
Ian

In realtà Python e Perl sono le lingue dominanti in quelle aree.
dryajov,

1
In realtà, il motivo principale per cui lo sto implementando è a causa delle applicazioni di apprendimento automatico. Quindi hai ragione.
voodooattack,

@dryajov Sii felice di non sapere quanto sia grande FORTRAN in alcune aree della scienza computazionale ...
cmaster

@dryajov Perché sono più accessibili agli umani che forse non sono programmatori a tempo pieno, non perché sono intrinsecamente migliori nel sequenziamento del genoma - abbiamo linguaggi appositamente studiati come R e per questo abbiamo compilato linguaggi scientifici come Fortran.
gatto

0

Credo davvero che sia perché è un'idea diversa e potente. Stai andando contro i sistemi di credenze. Le cose diventano accettate o popolari attraverso gli effetti della rete non sulla base del merito. Inoltre, nessuno vuole adattarsi a un nuovo stack. Le persone rifiutano automaticamente cose troppo diverse.

Se riesci a trovare un modo per trasformarlo in un normale modulo npm che sembra improbabile, allora potresti convincere alcune persone a usarlo.


Vuoi dire che i programmatori JS sono bloccati dal fornitore su npm?
voodooattack

3
La domanda riguarda un nuovo sistema di runtime e non alcuni moduli npm e anche la concorrenza mutevole dello stato condiviso è davvero una vecchia idea.
Ankur,

1
@voodooattack Voglio dire che sono bloccati al cervello all'approccio che è già popolare e sarà quasi impossibile superare il pregiudizio dello status quo.
Jason Livesay,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.