Qualcuno può suggerire scenari tipici in cui la classe Partitioner
introdotta in .NET 4.0 può/deve essere utilizzata?Quando utilizzare la classe Partitioner?
risposta
La classe Partitioner
è usato per fare le esecuzioni parallele più grosso. Se hai un sacco di compiti molto piccoli da eseguire in parallelo, il sovraccarico di invocare delegati per ciascuno può essere proibitivo. Utilizzando Partitioner
, è possibile riorganizzare il carico di lavoro in blocchi e fare in modo che ogni chiamata parallela funzioni su un set leggermente più grande. La classe astrae questa funzione ed è in grado di partizionare in base alle condizioni effettive del set di dati e dei core disponibili.
Esempio: immagina di voler eseguire un semplice calcolo come questo in parallelo.
Parallel.ForEach(Input, (value, loopState, index) => { Result[index] = value*Math.PI; });
Questo invocherà il delegato per ogni voce in Input. Fare così aggiungerebbe un po 'di spese generali a ciascuno. Utilizzando Partitioner
possiamo fare qualcosa di simile
Parallel.ForEach(Partitioner.Create(0, Input.Length), range => {
for (var index = range.Item1; index < range.Item2; index++) {
Result[index] = Input[index]*Math.PI;
}
});
Questo ridurrà il numero di invoca come ogni invoke lavorerà su un insieme più ampio. Nella mia esperienza, ciò può aumentare significativamente le prestazioni quando si parallelizzano operazioni molto semplici.
Per parallelizzare un'operazione su un'origine dati, uno dei passaggi essenziali consiste nel suddividere l'origine in più sezioni a cui è possibile accedere contemporaneamente da più thread. PLINQ e Task Parallel Library (TPL) forniscono partizionatori predefiniti che funzionano in modo trasparente quando si scrive una query parallela o un ciclo ForEach. Per scenari più avanzati, è possibile collegare il proprio partizionatore.
Per saperne di più here:
mi piacerebbe aggiungere a questo: In generale * voi * non lo uso. PLINQ lo fa, e probabilmente farai franca con i partizionatori di default. –
La partizione di intervallo, come suggerito da Brian Rasmussen, è un tipo di partizionamento che deve essere utilizzato quando il lavoro è impegnativo per la CPU, tende ad essere piccolo (rispetto a una chiamata al metodo virtuale), molti elementi devono essere elaborati ed è per lo più costante quando si tratta di run time per elemento.
L'altro tipo di partizione che deve essere considerato è il partizionamento del blocco. Questo tipo di partizionamento è anche noto come algoritmo di bilanciamento del carico perché un thread di lavoro raramente rimane inattivo mentre c'è più lavoro da fare, cosa che non è il caso di una partizione di intervallo.
Una partizione di blocco deve essere utilizzata quando il lavoro presenta alcuni stati di attesa, tende a richiedere più elaborazione per elemento o ogni elemento può avere tempi di elaborazione del lavoro significativamente differenti.
Un esempio di questo potrebbe essere la lettura in memoria e l'elaborazione di 100 file con dimensioni molto diverse. Un file 1K verrà elaborato in un tempo molto inferiore rispetto a un file 1mb. Se per questo viene utilizzata una partizione di intervallo, alcuni thread potrebbero rimanere inattivi per un po 'di tempo poiché si verificavano file più piccoli.
A differenza di una partizione di intervallo, non è possibile specificare il numero di elementi da elaborare da ciascuna attività, a meno che non si scriva il proprio partizionatore personalizzato. Un altro svantaggio nell'usare una partizione di blocchi è che potrebbe esserci qualche contesa quando torna a prendere un altro blocco dal momento che un blocco esclusivo viene utilizzato in quel punto. Quindi, chiaramente una partizione di blocchi non dovrebbe essere usata per una piccola quantità di lavoro intensivo della CPU.
Il partizionatore del blocco predefinito inizia con una dimensione del blocco di 1 elemento per blocco. Dopo ogni thread elabora tre blocchi di 1 elemento, la dimensione del blocco viene incrementata a 2 elementi per blocco.Dopo che tre blocchi di 2 elementi sono stati elaborati da ciascun thread, la dimensione del blocco viene incrementata nuovamente a 3 elementi per blocco e così via. Almeno questo è il modo in cui funziona secondo Dixin Yan, (vedi la sezione di partizionamento di Chunk) che lavora per Microsoft.
A proposito, il simpatico strumento di visualizzazione nel suo blog sembra essere il Concurrency Visualizer profile tool. Lo docs for this tool afferma che può essere utilizzato per individuare colli di bottiglia delle prestazioni, sottoutilizzo della CPU, conflitto di thread, migrazione di thread tra core, ritardi di sincronizzazione, attività di DirectX, aree di I/O sovrapposte e altre informazioni. Fornisce visualizzazioni di dati grafici, tabulari e testuali che mostrano le relazioni tra i thread in un'app e il sistema nel suo complesso.
Altre risorse:
MSDN: Custom Partitioners for PLINQ and TPL
Part 5: Parallel Programming - Optimizing PLINQ di Joseph Albahari
- 1. Quando utilizzare e non utilizzare la classe Application di Android?
- 2. Quando utilizzare un modulo e quando utilizzare una classe
- 3. php: quando utilizzare la classe astratta e di interfaccia?
- 4. Come utilizzare la classe CHCSVParser
- 5. Quando utilizzare le categorie e quando utilizzare la sottoclasse?
- 6. Quando utilizzare Pipe vs Quando utilizzare la memoria condivisa
- 7. quando utilizzare la funzione htmlspecialchars()?
- 8. Quando utilizzare la coda sull'arrayist
- 9. Utilizzare la classe NSManagedObject senza initWithEntity :?
- 10. Java: quando utilizzare la parola chiave "this"
- 11. grado di utilizzare un tipo di intersezione quando la classe nozionale richiede la modifica accesso
- 12. Utilizzare la classe CSS in HighCharts
- 13. Come utilizzare la classe DTMFRecognitionEngine in Microsoft.Speech
- 14. come utilizzare l'interfaccia con la classe singleton
- 15. Utilizzare la classe estesa al posto della classe base
- 16. Come utilizzare la classe personalizzata con la compilazione dinamica
- 17. Quando utilizzare JMS e quando utilizzare REST?
- 18. Quando JVM carica la classe di annotazione
- 19. Quando utilizzare 'java.util.Objects. *'?
- 20. Quando utilizzare la lingua di elaborazione?
- 21. Quando utilizzare la barra iniziale in gitignore
- 22. Quando utilizzare la funzione membro statico?
- 23. Quando è appropriato utilizzare la cultura invariabile?
- 24. Quando dovremmo usare la classe e quando non dovremmo
- 25. Quando è necessario utilizzare System.AppContext?
- 26. Quando utilizzare parser-generator, quando è sufficiente la regex?
- 27. Quando utilizzare `method_missing`
- 28. Quando utilizzare, non utilizzare, OneToOne e ManyToOne
- 29. Quando utilizzare EntityManager.clear()?
- 30. Perché utilizzare SyncLocks in .NET per operazioni semplici quando è disponibile la classe Interlocked?
Il rangeSize predefinito è uno per Partitioner.Create(). Pertanto, la partizione è la stessa per entrambi gli esempi di codice. A meno di Partitioner.Create (0, Input.Length, i); dove i> 1, avrà ancora lo stesso numero di thread. – Pingpong