Preencha histogramas (redução de matriz) em paralelo com o OpenMP sem usar uma seção crítica

Eu gostaria de preencher histogramas em paralelo usando o OpenMP. Eu criei dois methods diferentes de fazer isso com o OpenMP em C / C ++.

O primeiro método proccess_data_v1 hist_private variável de histograma privada hist_private para cada thread, preenche-os em paralelo e, em seguida, sum os histogramas privados no histograma compartilhado do histograma em uma seção critical .

O segundo método proccess_data_v2 faz uma matriz compartilhada de histogramas com tamanho de matriz igual ao número de threads, preenche essa matriz em paralelo e, em seguida, sum o hist histograma compartilhado em paralelo.

O segundo método parece superior a mim, pois evita uma seção crítica e sum os histogramas em paralelo. No entanto, é necessário conhecer o número de threads e chamar omp_get_thread_num() . Eu geralmente tento evitar isso. Existe uma maneira melhor de fazer o segundo método sem referenciar os números de encadeamento e usar uma matriz compartilhada com tamanho igual ao número de encadeamentos?

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

Edit: Baseado em uma sugestão de @HristoIliev eu criei um método melhorado chamado 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); } 

    Você poderia alocar a grande matriz dentro da região paralela, onde você pode consultar sobre o número real de encadeamentos sendo usados:

     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 um melhor desempenho, eu aconselho que você arredonde o tamanho do trecho de cada thread em hista para um múltiplo do tamanho de página de memory do sistema, mesmo que isso possa potencialmente deixar buracos entre os diferentes histogramas parciais. Desta forma, você evitará o compartilhamento falso e o access remoto à memory nos sistemas NUMA (mas não na fase final de redução).