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

Me gustaría completar histogtwigs en paralelo usando OpenMP. He encontrado dos métodos diferentes para hacer esto con OpenMP en C / C ++.

El primer método proccess_data_v1 hist_private variable de histogtwig privado hist_private para cada subproceso, los completa en prallel y luego sum los histogtwigs privados en el histogtwig compartido hist en una sección critical .

El segundo método proccess_data_v2 una matriz compartida de histogtwigs con un tamaño de matriz igual al número de subprocesos, rellena esta matriz en paralelo y luego sum el histogtwig compartido hist en paralelo.

El segundo método me parece superior ya que evita una sección crítica y sum los histogtwigs en paralelo. Sin embargo, requiere conocer el número de subprocesos y llamar a 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 subprocesos y utilizando una matriz compartida con un tamaño igual al número de subprocesos?

 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 llamado process_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); } 

Puede asignar la matriz grande dentro de la región paralela, donde puede consultar sobre el número real de subprocesos utilizados:

 int *hista; #pragma omp parallel { const int nthreads = omp_get_num_threads(); const int ithread = omp_get_thread_num(); #pragma omp single hista = new int[nbins*nthreads]; ... } delete[] hista; 

Para un mejor rendimiento, le aconsejo que redondee el tamaño del fragmento de cada subproceso en hista a un múltiplo del tamaño de página de memoria del sistema, incluso si esto pudiera dejar huecos entre los diferentes histogtwigs parciales. De esta forma, evitará el uso compartido falso y el acceso a la memoria remota en los sistemas NUMA (pero no en la fase de reducción final).