Quando avresti bisogno di "centinaia di migliaia" di thread?

31

Erlang, Go e Rust dichiarano in un modo o nell'altro di supportare la programmazione concorrente con "thread" / coroutine poco costosi. Le domande frequenti contengono:

It is practical to create hundreds of thousands of goroutines in the same address space.

Il tutorial Rust dice:

Because tasks are significantly cheaper to create than traditional threads, Rust can create hundreds of thousands of concurrent tasks on a typical 32-bit system.

La documentazione di Erlang dice:

The default initial heap size of 233 words is quite conservative in order to support Erlang systems with hundreds of thousands or even millions of processes.

La mia domanda: quale tipo di applicazione richiede così tanti thread di esecuzione simultanei? Solo i server Web più attivi ricevono migliaia di visitatori simultanei. Le applicazioni di tipo Boss-worker / job-dispatching che ho scritto hanno colpito rendimenti decrescenti quando il numero di thread / processi è molto maggiore del numero di core fisici. Suppongo che potrebbe avere senso per le applicazioni numeriche, ma in realtà molte persone delegano il parallelismo a librerie di terze parti scritte in Fortran / C / C ++, non in questi linguaggi di nuova generazione.

    
posta user39019 10.02.2013 - 04:33
fonte

8 risposte

19

un caso d'uso - websockets:
poiché i websocket sono longevi rispetto alle semplici richieste, su un server molto occupato si accumulano molte web socket nel tempo. microtreade ti danno una buona modellazione concettuale e anche un'implementazione relativamente facile.

più in generale, i casi in cui numerose unità più o meno autonome attendono che si verifichino determinati eventi dovrebbero essere buoni casi d'uso.

    
risposta data 10.02.2013 - 06:53
fonte
15

Potrebbe aiutare a pensare a quello che Erlang era stato originariamente progettato per fare, ovvero gestire le telecomunicazioni. Attività come routing, commutazione, raccolta / aggregazione di sensori, ecc.

Portalo nel mondo del web - considera un sistema come Twitter . Il sistema probabilmente non userebbe microtread nella generazione di pagine web, ma potrebbe usarle nella sua raccolta / caching / distribuzione di tweet.

Questo articolo potrebbe essere di ulteriore aiuto.

    
risposta data 10.02.2013 - 05:35
fonte
11

In una lingua in cui non è consentito modificare le variabili, il semplice atto di mantenere lo stato richiede un contesto di esecuzione separato (che la maggior parte delle persone chiamerebbe un thread e Erlang chiama un processo). Fondamentalmente, tutto è un lavoratore.

Considera questa funzione di Erlang, che mantiene un contatore:

counter(Value) ->
    receive                               % Sit idle until a message is received
        increment -> counter(Value + 1);  % Restart with incremented value
        decrement -> counter(Value - 1);  % Restart with decremented value
        speak     ->
            io:fwrite("~B~n", [Value]),
            counter(Value);               % Restart with unaltered value
        _         -> counter(Value)       % Anything else?  Do nothing.
    end.

In un linguaggio OO convenzionale come C ++ o Java, si otterrebbe ciò con una classe con un membro della classe privata, metodi pubblici per ottenere o modificare il suo stato e un oggetto istanziato per ogni contatore. Erlang sostituisce la nozione dell'oggetto istanziato con un processo, la nozione di metodi con i messaggi e il mantenimento dello stato con chiamate tail che riavviare la funzione con qualsiasi valore componga il nuovo stato. Il vantaggio nascosto in questo modello - e la maggior parte della ragion d'essere di Erlang - è che la lingua serializza automaticamente l'accesso al valore del contatore attraverso l'uso di una coda di messaggi, rendendo molto facile il codice concorrente implementare con un alto grado di sicurezza.

Probabilmente sei abituato all'idea che gli switch di contesto siano costosi, il che è ancora vero dal punto di vista del sistema operativo host. Il runtime di Erlang è a sua volta un piccolo sistema operativo sintonizzato in modo che il passaggio tra i propri processi sia rapido ed efficiente, il tutto mantenendo al minimo il numero di switch di contesto che il sistema operativo esegue. Per questo motivo, avere migliaia di processi non è un problema ed è incoraggiato.

    
risposta data 25.02.2013 - 15:22
fonte
4

My question: what sort of application requires so many concurrent threads of execution?

1) Il fatto che una lingua "scala" significa che ci sono meno possibilità che tu debba abbandonare quella lingua quando le cose diventano più complesse lungo la strada. (Questo è chiamato il concetto di "Whole Product".) Molte persone stanno abbandonando Apache per Nginx proprio per questo motivo. Se sei vicino al "limite rigido" imposto dall'overhead del thread, ti spaventerai e inizierai a pensare ai modi per superarlo. I siti Web non possono mai prevedere la quantità di traffico che riceveranno, quindi spendere un po 'di tempo per rendere le cose scalabili è ragionevole.

2) Una goroutine per richiesta solo all'inizio. Ci sono molte ragioni per usare internamente le goroutine.

  • Considera un'app Web con 100 richieste simultanee, ma ogni richiesta genera centinaia di richieste di back-end. L'esempio ovvio è un aggregatore di motori di ricerca. Ma qualsiasi app potrebbe creare goroutine per ogni "area" sullo schermo, quindi generarle in modo indipendente anziché sequenziale. Ad esempio, ogni pagina su Amazon.com è composta da oltre 150 richieste di back-end, assemblate solo per te. Non ti accorgi perché sono in parallelo, non sequenziali, e ogni "area" è il proprio servizio web.
  • Considerare qualsiasi app in cui affidabilità e latenza sono fondamentali. Probabilmente vorrai che ogni richiesta in arrivo spari alcune richieste di back-end e restituisca per prima cosa i dati tornano prima .
  • Considera qualsiasi "aggiunta di clienti" nella tua app. Invece di dire "per ogni elemento, ottieni dati", puoi far girare un mucchio di goroutine. Se hai una serie di DB slave da interrogare, diventerai magicamente N volte più veloce. Se non lo fai, non sarà più lento.

hit diminishing returns when the number of threads/processes is much greater than the number of physical cores

Le prestazioni non sono l'unica ragione per suddividere un programma in CSP . Può effettivamente rendere il programma più facile da capire, e alcuni problemi possono essere risolti con molto meno codice.

Come nelle diapositive collegate sopra, la concorrenza nel codice è un modo per organizzare il problema. Non avere goroutine è come non avere una struttura dati di Map / Dictonary / Hash nella tua lingua. Puoi farcela senza di essa. Ma una volta che lo hai, inizi a usarlo ovunque, e semplifica davvero il tuo programma.

In passato, questo significava "roll your own" programmazione multithread. Ma questo era complesso e pericoloso - non ci sono ancora molti strumenti per assicurarti di non creare gare. E come si impedisce a un futuro manutentore di commettere un errore? Se guardi programmi grandi / complessi, vedrai che spendono un sacco di risorse in quella direzione.

Poiché la concorrenza non è una parte di prima classe nella maggior parte delle lingue, i programmatori di oggi non sanno perché sarebbe utile per loro. Questo diventerà più evidente solo quando tutti i telefoni e gli orologi da polso puntano verso 1000 core. Naviga con uno strumento per rilevatori di corsa integrato.

    
risposta data 27.12.2013 - 19:47
fonte
2

Per Erlang è comune avere un processo per connessione o altro compito. Ad esempio, un server audio in streaming potrebbe avere 1 processo per utente connesso.

Erlang VM è ottimizzato per gestire migliaia o persino centinaia di migliaia di processi rendendo molto convenienti gli switch di contesto.

    
risposta data 10.02.2013 - 08:53
fonte
1

Convenienza. Quando ho iniziato a fare la programmazione multi-thread, stavo facendo un sacco di simulazione e sviluppo del gioco sul lato per divertimento. Ho trovato di grande praticità la semplice rotazione di un thread per ogni singolo oggetto e lasciare che faccia la sua cosa piuttosto che elaborarla in loop. Se il tuo codice non è disturbato da un comportamento non deterministico e non hai collisioni, può semplificare la codifica. Con la potenza a nostra disposizione ora, se dovessi tornare a farlo, posso facilmente immaginare di far girare un paio di migliaia di thread grazie alla potenza di elaborazione e alla memoria sufficienti per gestire tanti oggetti discreti!

    
risposta data 25.02.2013 - 16:37
fonte
1

Un semplice esempio di Erlang, che è stato progettato per la comunicazione: il trasferimento di pacchetti di rete. Quando fai una richiesta http, potresti avere migliaia di pacchetti TCP / IP. Aggiungi a questo che tutti si connettono allo stesso tempo e hai il tuo caso d'uso.

Considera molte applicazioni utilizzate internamente da qualsiasi grande azienda per gestire i loro ordini o qualsiasi altra cosa possa aver bisogno. I server Web non sono l'unica cosa che richiede thread.

    
risposta data 27.12.2013 - 21:07
fonte
-2

Mi vengono in mente alcuni compiti di rendering. Se stai facendo una lunga catena di operazioni su ogni pixel di un'immagine, e se queste operazioni sono parallelizzabili, anche un'immagine relativamente piccola di 1024x768 è proprio nella parentesi "a centinaia di migliaia".

    
risposta data 10.02.2013 - 15:26
fonte

Leggi altre domande sui tag