Rellene los histogramas (reducción de matriz) en paralelo con OpenMP sin usar una sección crítica

Me gustaría llenar histogramas en paralelo utilizando OpenMP. He encontrado dos métodos diferentes para hacer esto con OpenMP en C / C ++.

El primer metodoproccess_data_v1 hace una variable de histograma privadahist_private para cada hilo, los llena en paralelo, y luego suma los histogramas privados en el histograma compartidohist en uncritical sección.

El segundo metodoproccess_data_v2 hace que una matriz compartida de histogramas con un tamaño de matriz igual al número de subprocesos, complete esta matriz en paralelo y luego sume el histograma compartidohist en paralelo.

El segundo método me parece superior, ya que evita una sección crítica y suma los histogramas en paralelo. Sin embargo, se requiere saber el número de hilos y llamar.omp_get_thread_num(). Generalmente trato de evitar esto. ¿Hay una mejor manera de hacer el segundo método sin hacer referencia a los números de hilos y usar una matriz compartida con un tamaño igual al número de hilos?

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;
}

Editar: Basado en una sugerencia de @HristoIliev, he creado un método mejorado llamadoprocess_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);
}

Respuestas a la pregunta(1)

Su respuesta a la pregunta