2015-02-01 4 views
6

Ho provato a scrivere questo codiceUtilizzando openMP per ottenere l'indice di elemento minimo parallelamente

float* theArray; // the array to find the minimum value 
int index, i; 
float thisValue, min; 

index = 0; 
min = theArray[0]; 
#pragma omp parallel for reduction(min:min_dist) 
for (i=1; i<size; i++) { 
    thisValue = theArray[i]; 
    if (thisValue < min) 

    { /* find the min and its array index */ 

     min = thisValue; 

     index = i; 
    } 
} 
return(index); 

Tuttavia questo non è l'output risposte corrette. Sembra che il minimo sia OK ma l'indice corretto è stato distrutto dai thread.

Ho anche provato alcuni modi forniti su Internet e qui (usando parallelo per il ciclo esterno e utilizzo critico per il confronto finale) ma questo causa una caduta di velocità piuttosto che un aumento della velocità.

Cosa devo fare per rendere corretto sia il valore minimo sia il suo indice? Grazie!

+0

non è male per condividere variabili (QuestoValore, min) tra le discussioni senza un mutex? –

+0

Sembra che questa riduzione abbia fatto molte cose, quindi non sono così chiaro se è condiviso o meno. Ho provato una versione senza riduzione, i thread funzionano liberamente, la parte assegnata è critica e la velocità di esecuzione si riduce effettivamente. – xxbidiao

risposta

2

Poiché non si sta solo cercando di trovare il valore minimo (reduction(min:___)) ma si mantiene anche l'indice, è necessario rendere il controllo critico. Ciò può rallentare significativamente il ciclo (come riportato). In generale, assicurati che ci sia abbastanza lavoro in modo da non incontrare sovraccarichi come nella domanda this. Un'alternativa sarebbe avere ogni thread che trova il minimo e il suo indice e salvarli in una variabile univoca e fare in modo che il thread master esegua un controllo finale su quelli come nel seguente programma.

#include <iostream> 
#include <vector> 
#include <ctime> 
#include <random> 
#include <omp.h> 

using std::cout; 
using std::vector; 

void initializeVector(vector<double>& v) 
{ 
    std::mt19937 generator(time(NULL)); 
    std::uniform_real_distribution<double> dis(0.0, 1.0); 
    v.resize(100000000); 
    for(int i = 0; i < v.size(); i++) 
    { 
     v[i] = dis(generator); 
    } 
} 

int main() 
{ 
    vector<double> vec; 
    initializeVector(vec); 

    float minVal = vec[0]; 
    int minInd = 0; 

    int startTime = clock(); 

    for(int i = 1; i < vec.size(); i++) 
    { 
     if(vec[i] < minVal) 
     { 
      minVal = vec[i]; 
      minInd = i; 
     } 

    } 

    int elapsedTime1 = clock() - startTime; 

    // Change the number of threads accordingly 
    vector<float> threadRes(4, std::numeric_limits<float>::max()); 
    vector<int> threadInd(4); 

    startTime = clock(); 
#pragma omp parallel for 
    for(int i = 0; i < vec.size(); i++) 
    { 
     { 
      if(vec[i] < threadRes[omp_get_thread_num()]) 
      { 
       threadRes[omp_get_thread_num()] = vec[i]; 
       threadInd[omp_get_thread_num()] = i; 
      } 
     } 

    } 

    float minVal2 = threadRes[0]; 
    int minInd2 = threadInd[0]; 

    for(int i = 1; i < threadRes.size(); i++) 
    { 
     if(threadRes[i] < minVal2) 
     { 
      minVal2 = threadRes[i]; 
      minInd2 = threadInd[i]; 
     } 
    } 

    int elapsedTime2 = clock() - startTime; 

    cout << "Min " << minVal << " at " << minInd << " took " << elapsedTime1 << std::endl; 
    cout << "Min " << minVal2 << " at " << minInd2 << " took " << elapsedTime2 << std::endl; 
} 

prega di notare che con le ottimizzazioni su e niente altro da fare nel ciclo, la versione di serie sembra rimanere re. Con le ottimizzazioni disattivate, OMP guadagna il sopravvento.

P.S. hai scritto reduction(min:min_dist) e hai continuato a usare min anziché min_dist.

+0

false sharing e 'clock' –

+0

@Zboson Ammetto il falso punto di condivisione, ma' clock' è abbastanza buono in quanto la dimensione del vettore è abbastanza grande. Se fa molta differenza, lo cambierò in 'chrono :: high_resolution_clock'. –

+0

[Non utilizzare 'clock()' per la sincronizzazione del codice multi-threading tranne con MSVC] (https://stackoverflow.com/questions/10874214/measure-execution-time-in-c-openmp-code). Usa 'omp_get_wtime()' poiché è affidabile con MSVC, GCC e ICC. –

12

Non so di un elegante voglio fare una riduzione minima e salvare un indice. Lo faccio trovando il minimo locale e l'indice per ogni thread, quindi il minimo globale e l'indice in una sezione critica.

index = 0; 
min = theArray[0]; 
#pragma omp parallel 
{ 
    int index_local = index; 
    float min_local = min; 
    #pragma omp for nowait 
    for (i = 1; i < size; i++) {   
     if (theArray[i] < min_local) { 
      min_local = theArray[i]; 
      index_local = i; 
     } 
    } 
    #pragma omp critical 
    { 
     if (min_local < min) { 
      min = min_local; 
      index = index_local; 
     } 
    } 
} 

Con OpenMP 4.0 è possibile utilizzare riduzioni definite dall'utente. Una riduzione minimo definito dall'utente può essere definita così

struct Compare { float val; sizt_t index; };  
#pragma omp declare reduction(minimum : struct Compare : omp_out = omp_in.val < omp_out.val ? omp_in : omp_out) 

Poi la riduzione può essere fatto come quello

struct Compare min; 
min.val = theArray[0]; 
min.index = 0; 
#pragma omp parallel for reduction(minimum:min) 
for(int i = 1; i<size; i++) { 
    if(theArray[i]<min.val) { 
     min.val = a[i]; 
     min.index = i; 
    } 
} 

che funziona per C e C++. Le riduzioni definite dall'utente hanno altri vantaggi oltre al codice semplificato. Esistono più algoritmi per fare riduzioni. Ad esempio la fusione può essere eseguita in O(number of threads) o O(Log(number of threads). La prima soluzione che ho dato lo fa in O(number of threads), tuttavia utilizzando riduzioni definite dall'utente, OpenMP sceglie l'algoritmo.

+0

Non so perché, ma sto ottenendo risultati errati con questo codice. –

+0

Capito. 'theArray [i]

+0

@AviGinsburg, grazie per aver trovato e risolto il mio bug! –

1

idea di base

Ciò può essere realizzato senza alcuna critical o atomic sezioni parellelization-rottura creando un custom reduction. Fondamentalmente, definisci un oggetto che memorizza sia l'indice che il valore, quindi crea una funzione che ordina due di questi oggetti solo dal valore, non dall'indice.

dettagli

Un oggetto per memorizzare un indice e valore di insieme:

typedef std::pair<unsigned int, float> IndexValuePair; 

È possibile accedere all'indice accedendo alla proprietà first e il valore accedendo alla proprietà second, vale a dire,

IndexValuePair obj(0, 2.345); 
unsigned int ix = obj.first; // 0 
float val = obj.second; // 2.345 

Definire una funzione per ordinare due IndexValuePair oggetti:

IndexValuePair myMin(IndexValuePair a, IndexValuePair b){ 
    return a.second < b.second ? a : b; 
} 

Poi, costruire una riduzione personalizzato seguendo le linee guida della OpenMP documentation:

#pragma omp declare reduction \ 
(minPair:IndexValuePair:omp_out=myMin(omp_out, omp_in)) \ 
initializer(omp_priv = IndexValuePair(0, 1000)) 

In questo caso, ho scelto per inizializzare l'indice a 0 e il valore a 1000 Il valore deve essere inizializzato su un numero maggiore del valore massimo che si prevede di ordinare.

funzionale Esempio

Infine, combinare tutti questi pezzi con il parallelo per ciclo!

// Compile with g++ -std=c++11 -fopenmp demo.cpp 
#include <iostream> 
#include <utility> 
#include <vector> 

typedef std::pair<unsigned int, float> IndexValuePair; 

IndexValuePair myMin(IndexValuePair a, IndexValuePair b){ 
    return a.second < b.second ? a : b; 
} 

int main(){ 

    std::vector<float> vals {10, 4, 6, 2, 8, 0, -1, 2, 3, 4, 4, 8}; 
    unsigned int i; 

    IndexValuePair minValueIndex(0, 1000); 

    #pragma omp declare reduction \ 
     (minPair:IndexValuePair:omp_out=myMin(omp_out, omp_in)) \ 
     initializer(omp_priv = IndexValuePair(0, 1000)) 

    #pragma omp parallel for reduction(minPair:minValueIndex) 
    for(i = 0; i < vals.size(); i++){ 

     if(vals[i] < minValueIndex.second){ 
      minValueIndex.first = i; 
      minValueIndex.second = vals[i]; 
     } 
    } 

    std::cout << "minimum value = " << minValueIndex.second << std::endl; // Should be -1 
    std::cout << "index = " << minValueIndex.first << std::endl; // Should be 6 


    return EXIT_SUCCESS; 

}