2013-03-04 11 views
10

Ho un programma C++ che potrebbe essere parallelizzato. Sto usando Visual Studio 2010, compilation a 32 bit.I task paralleli ottengono prestazioni migliori con boost :: thread rispetto a ppl o OpenMP

In breve la struttura del programma è il seguente

#define num_iterations 64 //some number 

struct result 
{ 
    //some stuff 
} 

result best_result=initial_bad_result; 

for(i=0; i<many_times; i++) 
{ 
    result *results[num_iterations]; 


    for(j=0; j<num_iterations; j++) 
    { 
     some_computations(results+j); 
    } 

    // update best_result; 
} 

Poiché ciascuna some_computations() è indipendente (alcune variabili globali leggere, ma nessuna variabile globale modificate) I parallelizzata interno for -loop.

Il mio primo tentativo è stato con boost :: filo,

thread_group group; 
for(j=0; j<num_iterations; j++) 
{ 
    group.create_thread(boost::bind(&some_computation, this, result+j)); 
} 
group.join_all(); 

I risultati sono stati buoni, ma ho deciso di provare di più.

Ho provato il biblioteca OpenMP

#pragma omp parallel for 
for(j=0; j<num_iterations; j++) 
{ 
    some_computations(results+j); 
} 

I risultati sono stati peggiori di quelli s' il boost::thread.

Poi ho provato il ppl biblioteca e utilizzato parallel_for():

Concurrency::parallel_for(0,num_iterations, [=](int j) { 
    some_computations(results+j); 
}) 

I risultati sono stati i peggiori.

Ho trovato questo comportamento abbastanza sorprendente. Poiché OpenMP e ppl sono progettati per la parallelizzazione, mi sarei aspettato risultati migliori rispetto a boost::thread. Ho sbagliato?

Perché boost::thread mi dà risultati migliori?

+2

Potrebbe per favore quantificare "migliore", ad es. fornire tempi di esecuzione rispetto al numero di thread? Con 'boost :: thread' stai creando 64 thread. OpenPM utilizza un gruppo di thread di lavoro il cui numero predefinito è il numero di CPU virtuali. PPL utilizza anche un pool di thread e ha un overhead ancora maggiore rispetto a OpenMP poiché implementa anche il bilanciamento del lavoro. –

+0

Ho usato lo stesso numero (32 o 64) per ogni tentativo, forse come hai fatto notare, con OpenMP e ppl ho potuto ottenere risultati migliori impostando il numero di thread uguale al numero di core. Ci proverò. – 888

+1

È quasi impossibile rispondere alla domanda così com'è. Cosa sta facendo 'some_computations'? C'è una possibile contesa da qualche parte (che potrebbe colpire le diverse librerie in modo diverso, ad esempio se openmp ha effettivamente un overhead più basso, ma si hanno molte scritture sulla cache condivisa, la frenesia di invalidazione della cache risultante potrebbe renderla più lenta)? Quanto tempo ci vuole per percorrere il blocco parallelizzato per ogni variante – Grizzly

risposta

9

OpenMP o PPL non sono pessimisti. Fanno solo quello che viene detto, tuttavia ci sono alcune cose che dovresti prendere in considerazione quando cerchi di paralizzare i loop.

Senza vedere come hai implementato queste cose, è difficile dire quale possa essere la vera causa.

Inoltre, se le operazioni in ogni iterazione dipendono da qualsiasi altra iterazione nello stesso ciclo, questo creerà contesa, che rallenterà le cose. Non hai ancora mostrato cosa fa la tua funzione some_operation, quindi è difficile dire se ci sono delle dipendenze dei dati.

Un ciclo che può essere realmente parallelizzato deve essere in grado di eseguire ciascuna iterazione in modo totalmente indipendente da tutte le altre iterazioni, senza accedere alla memoria condivisa in nessuna delle iterazioni. Quindi preferibilmente, dovresti scrivere cose su variabili locali e poi copiare alla fine.

Non tutti i cicli possono essere parallelizzati, è molto dipendente dal tipo di lavoro svolto.

Ad esempio, qualcosa che è buono per la parallelizzazione è lavoro svolto su ciascun pixel di un buffer dello schermo. Ogni pixel è totalmente indipendente da tutti gli altri pixel e, pertanto, un thread può richiedere un'iterazione di un ciclo e svolgere il lavoro senza dover essere trattenuto in attesa di memoria condivisa o dipendenze dei dati all'interno del ciclo tra le iterazioni.

Inoltre, se si dispone di un array contiguo, questa matrice potrebbe essere parzialmente in una riga della cache e se si modifica l'elemento 5 nel thread A e quindi si modifica l'elemento 6 nel thread B, è possibile ottenere contesa della cache, che anche rallentare le cose, poiché queste si trovano nella stessa riga della cache. Un fenomeno noto come condivisione false.

Ci sono molti aspetti da considerare quando si esegue la parallelizzazione del ciclo.

+0

la funzione 'some_operation' prende un offset in un array e l'array è condiviso tra più thread. Non so che PPL o OpenMP possano fornire garanzie che non state scrivendo su quell'array, o che qualsiasi altra cosa stia scrivendo su quell'array. Quindi la mia risposta non cambia. –

+1

Il tuo primo paragrafo non è vero. Né OpenMP né PPL si preoccupano di ciò che si fa alle variabili condivise e non c'è niente di pessimista o di ottimista nel modo in cui funzionano. Entrambi sono concetti di programmazione imperativi, il che significa che il compilatore rende parallelo il codice se detto così, piuttosto che trattare le espressioni come suggerimenti. Il corretto trattamento delle variabili condivise è lasciato al solo programmatore. –

2

In breve, openMP si basa principalmente su memoria condivisa, con costi aggiuntivi di gestione del tasking e gestione della memoria. ppl è progettato per gestire schemi generici di strutture e algoritmi di dati comuni, comporta costi aggiuntivi di complessità. Entrambi hanno un costo aggiuntivo per la CPU, ma la semplice caduta dei thread boost non avviene (i thread boost sono semplicemente semplici wrapping API). Ecco perché entrambi sono più lenti della tua versione boost. E, poiché il calcolo esemplificato è indipendente l'uno dall'altro, senza sincronizzazione, openMP dovrebbe essere vicino alla versione boost.

Si verifica in scenari semplici, ma, per scenari complicati, con layout e algoritmi di dati complicati, dovrebbe dipendere dal contesto.

+2

OpenMP non è progettato per il passaggio di messaggi, MPI è quello che supera i massegi. – Moss

+1

@Moss, grazie, ho confuso OpenMP e MPI. OpenMP è basato sulla memoria condivisa. –