10

È il seguente corretto?Quando utilizzare il pattern di disturbo e quando lo storage locale viene rubato dal lavoro?

  • Il disruptor pattern ha prestazioni migliori parallelo e scalabilità se ogni voce deve essere lavorato in diversi modi (io operazioni o annotazioni), dal momento che può essere parallelizzata utilizzando più consumatori senza contesa.
  • contrario, work stealing (cioè la memorizzazione voci localmente e rubare voci da altri thread) ha prestazioni migliori parallelo e scalabilità se ogni voce deve essere elaborato in un unico modo unico, poiché la distribuzione disgiuntamente le voci su più thread nel disgregatore il modello causa la contesa.

(ed è il modello distruttore ancora così molto più veloce di altri lockless multi-produttore code multi-consumo (ad esempio from boost) quando più produttori (cioè CAS operations) sono coinvolti?)


mio situazione in dettaglio:

L'elaborazione di una voce può produrre diverse nuove voci, che devono essere elaborate alla fine. Le prestazioni hanno la massima priorità, le voci elaborate nell'ordine FIFO hanno la seconda priorità.

Nell'attuale implementazione, ogni thread utilizza un FIFO locale, dove aggiunge le sue nuove voci. I thread inattivi rubano il lavoro dalla FIFO locale di altri thread. Le dipendenze tra l'elaborazione del thread vengono risolte utilizzando una tabella hash lockless, mechanically sympathetic (CAS in scrittura, con granularità del bucket). Ciò si traduce in una contesa piuttosto bassa ma l'ordine FIFO a volte è rotto.

L'utilizzo del pattern di disturbo garantisce l'ordine FIFO. Ma non distribuire le voci sui thread causa una contesa molto più alta (ad esempio CAS su un cursore di lettura) rispetto a FIFO locali con il furto del lavoro (la velocità effettiva di ogni thread è circa la stessa)?


riferimenti che ho trovato

I test di prestazioni in the standard technical paper on the disruptor (capitolo 5 + 6) non coprono distribuzione del lavoro disgiunto.

https://groups.google.com/forum/?fromgroups=#!topic/lmax-disruptor/tt3wQthBYd0 è l'unico riferimento che ho trovato su disruptor + work stealing. Dichiara che una coda per thread è notevolmente più lenta se c'è uno stato condiviso, ma non entra nei dettagli o spiega perché. Dubito che questa frase si applica alla mia situazione con:

  • stato condiviso risolto con una tabella hash senza chiave;
  • distribuire disgiuntamente voci tra i consumatori;
  • tranne per il furto del lavoro, ogni thread legge e scrive solo nella coda locale.

risposta

12

Update - Bottom line in anticipo per le prestazioni max: è necessario scrivere sia nella sintassi idiomatica per disgregatore e il lavoro rubare, e quindi punto di riferimento.

Per me, penso che la distinzione si concentri principalmente sulla suddivisione tra focus di messaggio vs task, e quindi nel modo in cui si vuole pensare al problema. Cerca di risolvere il tuo problema, e se è focalizzato sull'attività, Disruptor è una buona idea. Se il problema è focalizzato sul messaggio, potresti essere più adatto a un'altra tecnica come il furto del lavoro.

  • Usa lavoro rubare quando l'implementazione è messaggio focalizzato. Ogni thread può raccogliere un messaggio ed eseguirlo fino al completamento. Per un server HTTP di esempio: a ogni richiesta http in ingresso viene assegnato un thread. Quel filo è focalizzata sulla gestione della partenza richiesta alla fine - registrato la richiesta, controllando i controlli di sicurezza, facendo ricerca vhost, file di recupero, l'invio di risposta, e chiudendo la connessione

  • Usa perturbatore quando l'implementazione è compito focalizzata . Ogni thread può lavorare su una particolare fase del processo. Esempio alternativo: per un task focus, l'elaborazione sarà suddivisa in fasi, quindi si avrà un thread che esegue la registrazione, un thread per i controlli di sicurezza, un thread per la ricerca di vhost, ecc; ogni thread focalizzato sul proprio compito e passa la richiesta al thread successivo nella pipeline. Le fasi possono essere parallele ma la struttura generale è un thread focalizzato su un compito specifico e passa il messaggio tra i thread.

Naturalmente, è possibile modificare l'implementazione per adattarsi meglio a ciascun approccio.

Nel tuo caso, strutturerei il problema in modo diverso se volessi usare Disruptor. In generale si elimina lo stato condiviso perché un singolo thread possiede lo stato e passa tutte le attività attraverso quel thread di lavoro: cerca SEDA per molti diagrammi come questo. Questo può avere molti vantaggi, ma, ancora una volta, dipende davvero dalla tua implementazione.

Alcuni più verbosità:

  • Disruptor - molto utile quando è richiesta rigoroso ordinamento delle fasi, vantaggi aggiuntivi quando tutte le attività sono di lunghezza costante es: nessun blocco sul sistema esterno, e molto simile quantità di elaborazione per compito. In questo scenario, è possibile assumere che tutti i thread funzionino in modo uniforme attraverso il sistema e quindi organizzare N thread per elaborare ogni N messaggi. Mi piace pensare a Disruptor come un modo efficace per implementare sistemi simili a SEDA in cui le fasi del processo di thread. Ovviamente è possibile avere un'applicazione con un singolo stadio e più unità parallele che eseguono lo stesso lavoro in ogni fase, ma questo non è proprio il punto a mio avviso. Ciò eviterebbe totalmente il costo dello stato condiviso.
  • Rubare lavoro: utilizzare questa opzione quando le attività hanno una durata variabile e l'ordine di elaborazione dei messaggi non è importante, in quanto ciò consente thread liberi e che hanno già consumato i propri messaggi per continuare il progresso da un'altra coda di attività. In questo modo se ad esempio hai 10 thread e 1 è bloccato su IO, il resto completerà comunque la loro elaborazione.
+0

Grazie per la risposta. Fare una distinzione basata su alcuni suoni di paradigma è abbastanza buona, sfortunatamente non so quale sia la differenza tra focus del messaggio e focus del task. – DaveFar

+0

Ho progettato una soluzione al mio problema con gli interruttori: un utente annota le voci in (più) disruptor con i loro valori hash, un altro utente le inserisce nella tabella hash locale e annota le voci con un flag sia che siano state in la tabella hash già. Questa soluzione potrebbe essere più veloce, ma non scala in modo lineare nel numero di thread, come fa la tabella hash lockless.Un ulteriore distruttore, implementando la FIFO per tutti gli elementi appena creati, ha più produttori, quindi non sono sicuro che un disruptor migliori questa parte dell'implementazione. – DaveFar

+0

Btw, i miei compiti sono sicuramente di varia durata, ma l'ordine di elaborazione dei messaggi è un po 'importante ... – DaveFar

Problemi correlati