Come faccio a sapere se un set di codice è un buon candidato per la parallelizzazione?

7

Come posso dire in un'applicazione se ci sono parti che trarranno beneficio dall'uso della libreria Task C # (credo che sia la libreria di elaborazione parallela).

Dato che sono state fatte altre ottimizzazioni, come faccio a sapere oltre a provarlo? In C #, posso utilizzare Visual Studio per questa analisi?

    
posta johnny 04.01.2018 - 19:07
fonte

3 risposte

3

Suggerirei che si tratti di eseguire pezzi di lavoro grandi indipendentemente, in gran parte, in qualsiasi ordine, senza produrre risultati diversi. (Salvo variazioni accettabili o buone .)

Tre candidati che cercherò:

  • Interventi dispendiosi in termini di tempo in cui l'ordine di elaborazione non cambierà il risultato.
  • Scopri che DoWork() deve accadere prima di aver bisogno dei risultati.
  • Scopri che DoWork() deve accadere e non hai affatto bisogno di risultati .

In tutti e tre i casi, assicurati che l'impatto sullo stato condiviso sia gestibile o inesistente.

E ricorda che questi sono solo candidati . È necessario valutare quanto lavoro deve essere fatto e quanto può essere impigliato (o non aggrovigliato) quello che funziona con tutti gli altri lavori che l'applicazione potrebbe fare. E, quindi, sarebbe ideale per confrontare entrambe le soluzioni.

Ad esempio ...

Un'iterazione dispendiosa in termini di tempo in cui l'ordine di elaborazione non ha importanza.

Hai una percentuale massiccia di% co_de per cui devi calcolare la media il più velocemente possibile . La costruzione di una media è principalmente aggiunta, che è associativa . Quindi, possiamo eseguire l'aggiunta in blocchi di attività:

avg(data[0..z]) == ( sum(data[0..n]) + sum(data[n..m]) .. sum(data[x..z]) ) / data.Count;

(In realtà può essere suddiviso in modo più sofisticato compatibile con cluster , ma è un esempio ...)

So che List<Int32> data deve accadere prima che tu abbia bisogno dei suoi risultati.

Stai rendendo un documento. Su quel documento, hai N componenti di vari tipi (immagini, blocchi di testo formattati, grafici, ecc.). Per semplificarlo, in questo scenario, ogni componente può renderizzare completamente in modo indipendente e restituire un componente grafico a LayoutEngine. E, LayoutEngine deve aspettare che ogni componente esegua il rendering e restituisca le sue dimensioni prima che possa posizionarle e darle da mangiare al DisplayEngine ... o qualsiasi altra cosa.

Non ho bisogno dei risultati di DoWork() affatto .

Alcuni tipi di registrazione.

Se stai registrando le interazioni delle applicazioni per supportare gli sforzi di marcatura, ad esempio, potresti voler incenerire e dimenticare quegli eventi di registrazione. Se la registrazione richiede molto tempo o fallisce, non si desidera interrompere l'operazione o le prestazioni dell'applicazione.

    
risposta data 04.01.2018 - 21:01
fonte
10

Sai che il codice è un buon candidato per la parallelizzazione quando può essere suddiviso in un insieme di compiti "discreti" (cioè indipendenti). Un compito discreto è uno che produce un risultato specifico e non ha effetti collaterali che competono con altri compiti, vale a dire un elemento di lavoro autonomo.

Considera questo algoritmo di quicksort ricorsivo parallelo :

private void QuicksortSequential<T>(T[] arr, int left, int right) 
where T : IComparable<T>
{
    if (right > left)
    {
        int pivot = Partition(arr, left, right);
        QuicksortSequential(arr, left, pivot - 1);
        QuicksortSequential(arr, pivot + 1, right);
    }
}

private void QuicksortParallelOptimised<T>(T[] arr, int left, int right) 
where T : IComparable<T>
{
    const int SEQUENTIAL_THRESHOLD = 2048;
    if (right > left)
    {
        if (right - left < SEQUENTIAL_THRESHOLD)
        {

            QuicksortSequential(arr, left, right);
        }
        else
        {
            int pivot = Partition(arr, left, right);
            Parallel.Do(
                () => QuicksortParallelOptimised(arr, left, pivot - 1),
                () => QuicksortParallelOptimised(arr, pivot + 1, right));
        }
    }
}

Notare Parallel.Do in basso. Questo è dove avviene la parallelizzazione; funziona perché nessuna di queste chiamate a QuicksortParallelOptimised interferirà l'una con l'altra.

Per parallelizzare il tuo codice, dovresti riscriverlo in modo che possa essere espresso come una serie di funzioni indipendenti che accettano ciascuno alcuni parametri e restituiscono un Task<T> . È quindi possibile eseguire tali funzioni in parallelo e combinare i risultati al termine.

Ulteriori letture
Procedura: scrivere un ciclo Simple Parallel.ForEach

    
risposta data 04.01.2018 - 19:26
fonte
0

un buon indicatore è: se esegui codice per fare qualcosa molto spesso (ad esempio per ogni file in una cartella) e queste esecuzioni particolari sono indipendenti l'una dall'altra. questa è una parte banale, il puro "strato di funzione".

la parte non banale è il "livello di progettazione": se vuoi parallelizzare qualcosa per accelerare, dovresti analizzare il tuo programma: quali parti del tuo programma impiegano più tempo? forse puoi trovare funzioni che saranno eseguite più spesso di quanto richiesto? quindi puoi riprogettare la tua domanda in merito. (progettare un programma con lo scopo di accelerare il tuo programma può forse causare meno leggibilità, qui dovresti riflettere sul valore della velocità e della leggibilità e, si spera, trovare un compromesso.)

    
risposta data 04.01.2018 - 19:36
fonte

Leggi altre domande sui tag