我是一个经验相对较丰富的OpenMP用户,但我遇到了一个令人困惑的问题,并希望有人能够帮助解决。问题在于,一个简单的哈希算法对于栈分配的数组表现良好,但对于堆上的数组表现不佳。
下面的例子使用i%M(i模M)来计数每个数组元素中第M个整数。为简单起见,假设N=1000000,M=10。如果N%M==0,则结果应该是bins[]的每个元素都等于N/M:
#pragma omp for
for (int i=0; i<N; i++)
bins[ i%M ]++;
每个线程都拥有私有的数组bins[](我在关键区域之后对所有线程的结果求和)。
当bins[]在堆栈上分配时,程序表现良好,性能与核心数成比例增长。
然而,如果bins[]在堆上(指向bins[]的指针在堆栈上),性能会急剧下降。 这是一个重大问题!
我想使用OpenMP将某些数据的binning(哈希)并行到堆数组中,但这会导致严重的性能问题。
这绝对不是诸如所有线程尝试写入同一内存区域之类的愚蠢问题。这是因为每个线程都有自己的bins[]数组,无论是堆还是栈上分配的bins,结果都是正确的,并且在单线程运行时性能没有区别。 我在不同的硬件(Intel Xeon和AMD Opteron),使用GCC和Intel C ++编译器复制了这个问题。 所有测试都在Linux(Ubuntu和RedHat)上进行。
好像没有理由使OpenMP的良好性能局限于栈数组。
有什么猜测吗? 也许线程对堆的访问经过某种共享网关? 我该如何解决这个问题?
以下是完整的程序示例:
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
int main(const int argc, const char* argv[])
{
const int N=1024*1024*1024;
const int M=4;
double t1, t2;
int checksum=0;
printf("OpenMP threads: %d\n", omp_get_max_threads());
//////////////////////////////////////////////////////////////////
// Case 1: stack-allocated array
t1=omp_get_wtime();
checksum=0;
#pragma omp parallel
{ // Each openmp thread should have a private copy of
// bins_thread_stack on the stack:
int bins_thread_stack[M];
for (int j=0; j<M; j++) bins_thread_stack[j]=0;
#pragma omp for
for (int i=0; i<N; i++)
{ // Accumulating every M-th number in respective array element
const int j=i%M;
bins_thread_stack[j]++;
}
#pragma omp critical
for (int j=0; j<M; j++) checksum+=bins_thread_stack[j];
}
t2=omp_get_wtime();
printf("Time with stack array: %12.3f sec, checksum=%d (must be %d).\n", t2-t1, checksum, N);
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// Case 2: heap-allocated array
t1=omp_get_wtime();
checksum=0;
#pragma omp parallel
{ // Each openmp thread should have a private copy of
// bins_thread_heap on the heap:
int* bins_thread_heap=(int*)malloc(sizeof(int)*M);
for (int j=0; j<M; j++) bins_thread_heap[j]=0;
#pragma omp for
for (int i=0; i<N; i++)
{ // Accumulating every M-th number in respective array element
const int j=i%M;
bins_thread_heap[j]++;
}
#pragma omp critical
for (int j=0; j<M; j++) checksum+=bins_thread_heap[j];
free(bins_thread_heap);
}
t2=omp_get_wtime();
printf("Time with heap array: %12.3f sec, checksum=%d (must be %d).\n", t2-t1, checksum, N);
//////////////////////////////////////////////////////////////////
return 0;
}
程序的示例输出如下:
对于OMP_NUM_THREADS=1
OpenMP threads: 1
Time with stack array: 2.973 sec, checksum=1073741824 (must be 1073741824).
Time with heap array: 3.091 sec, checksum=1073741824 (must be 1073741824).
对于OMP_NUM_THREADS=10
OpenMP threads: 10
Time with stack array: 0.329 sec, checksum=1073741824 (must be 1073741824).
Time with heap array: 2.150 sec, checksum=1073741824 (must be 1073741824).
我非常感激任何帮助!