【发布时间】:2013-05-23 06:12:22
【问题描述】:
我想使用 OpenMP 并行填充直方图。我想出了两种不同的方法来使用 C/C++ 中的 OpenMP 执行此操作。
第一种方法proccess_data_v1 为每个线程创建一个私有直方图变量hist_private,将它们并行填充,然后将私有直方图相加到critical 部分中的共享直方图hist 中。
第二种方法proccess_data_v2 制作一个数组大小等于线程数的共享直方图数组,并行填充这个数组,然后并行求和共享直方图hist。
第二种方法似乎优于我,因为它避免了关键部分并并行汇总直方图。但是,它需要知道线程数并调用omp_get_thread_num()。我通常会尽量避免这种情况。有没有更好的方法来执行第二种方法而不引用线程号并使用大小等于线程数的共享数组?
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;
}
编辑:
根据@HristoIliev 的建议,我创建了一个改进的方法,称为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);
}
【问题讨论】:
-
您能解释一下为什么要使用嵌套并行区域吗? (我指的是您的 process_data_v1 方法)。也许我不理解某些东西,但是根据您的代码,在我看来,您要求的是 Nthreads**2。也就是说,您要求的资源比可用的资源多。那是对的吗?换句话说,你能解释一下外部平行区域的行为吗?谢谢...
-
嗨@user2088790,
proccess_data_v1不是最快的吗?因为我们不需要共享内存。我尝试了版本 2 和 3,它们比 v1 慢。有什么建议吗?