Füllen Sie Histogramme (Array-Reduzierung) parallel mit OpenMP, ohne einen kritischen Abschnitt zu verwenden

Ich möchte Histogramme parallel mit OpenMP füllen. Ich habe mir zwei verschiedene Methoden ausgedacht, um dies mit OpenMP in C / C ++ zu tun.

Die erste Methodeproccess_data_v1 Erstellt eine private Histogrammvariablehist_private Füllt sie für jeden Thread parallel aus und summiert dann die privaten Histogramme in das gemeinsame Histogrammhist in einemcritical Sektion.

Die zweite Methodeproccess_data_v2 erstellt ein gemeinsames Array von Histogrammen mit einer Arraygröße, die der Anzahl der Threads entspricht, füllt dieses Array parallel und summiert dann das gemeinsame Histogrammhist parallel zu.

Die zweite Methode erscheint mir überlegen, da sie einen kritischen Abschnitt vermeidet und die Histogramme parallel summiert. Es erfordert jedoch die Kenntnis der Anzahl der Threads und des Aufrufsomp_get_thread_num(). Ich versuche im Allgemeinen, dies zu vermeiden. Gibt es eine bessere Möglichkeit, die zweite Methode durchzuführen, ohne die Thread-Nummern zu referenzieren und ein gemeinsames Array mit einer Größe zu verwenden, die der Anzahl der Threads entspricht?

void proccess_data_v1(float *data, int *hist, const int n, const int nbins, float max) {
    #pragma omp parallel 
    {
        int *hist_private = new int[nbins];
        for(int i=0; i<nbins; i++) hist_private[i] = 0;
        #pragma omp for nowait
        for(int i=0; i<n; i++) {
            float x = reconstruct_data(data[i]);
            fill_hist(hist_private, nbins, max, x);
        }
        #pragma omp critical 
        {
            for(int i=0; i<nbins; i++) {
                hist[i] += hist_private[i];
            }
        }
        delete[] hist_private;
    }
}

void proccess_data_v2(float *data, int *hist, const int n, const int nbins, float max) {
    const int nthreads = 8;
    omp_set_num_threads(nthreads);
    int *hista = new int[nbins*nthreads];

    #pragma omp parallel 
    {
        const int ithread = omp_get_thread_num();
        for(int i=0; i<nbins; i++) hista[nbins*ithread+i] = 0;
        #pragma omp for
        for(int i=0; i<n; i++) {
            float x = reconstruct_data(data[i]);
            fill_hist(&hista[nbins*ithread], nbins, max, x);
        }

        #pragma omp for
        for(int i=0; i<nbins; i++) {
            for(int t=0; t<nthreads; t++) {
                hist[i] += hista[nbins*t + i];
            }
        }

    }
    delete[] hista;
}

Bearbeiten: Basierend auf einem Vorschlag von @HristoIliev habe ich eine verbesserte Methode namens erstelltprocess_data_v3

#define ROUND_DOWN(x, s) ((x) & ~((s)-1))
void proccess_data_v2(float *data, int *hist, const int n, const int nbins, float max) {
    int* hista;
    #pragma omp parallel 
    {
        const int nthreads = omp_get_num_threads();
        const int ithread = omp_get_thread_num();

        int lda = ROUND_DOWN(nbins+1023, 1024);  //1024 ints = 4096 bytes -> round to a multiple of page size
        #pragma omp single
        hista = (int*)_mm_malloc(lda*sizeof(int)*nthreads, 4096);  //align memory to page size

        for(int i=0; i<nbins; i++) hista[lda*ithread+i] = 0;
        #pragma omp for
        for(int i=0; i<n; i++) {
            float x = reconstruct_data(data[i]);
            fill_hist(&hista[lda*ithread], nbins, max, x);
        }

        #pragma omp for
        for(int i=0; i<nbins; i++) {
            for(int t=0; t<nthreads; t++) {
                hist[i] += hista[lda*t + i];
            }
        }

    }
    _mm_free(hista);
}

Antworten auf die Frage(1)

Ihre Antwort auf die Frage