Я довольно опытный пользователь OpenMP, но я только что столкнулся с загадочной проблемой, и я надеюсь, что кто-то здесь может помочь.Проблема в том, что простой алгоритм хеширования хорошо работает для массивов, выделенных стеком, но плохо для массивов в куче.
В приведенном ниже примере i i M (i модуль M) подсчитывается для каждого M-го целого числа в соответствующихэлемент массива.Для простоты представим N = 1000000, M = 10.Если N% M == 0, то результатом должно быть то, что каждый элемент bin [] равен N / M:
#pragma omp for
for (int i=0; i<N; i++)
bins[ i%M ]++;
Массив bin [] является частным для каждого потока (я суммирую результатывсе темы в критическом разделе впоследствии).
Когда бины [] размещены в стеке, программа работает отлично, с масштабированием производительности пропорционально количеству ядер.
Однако, если бины [] находятся в куче (указатель наbin [] в стеке), производительность резко падает.И это серьезная проблема!
Я хочу распараллелить объединение (хэширование) определенных данных в массивы кучи с помощью OpenMP, и это является серьезным ударом по производительности.
Это определенно не так глупо, каквсе потоки пытаются записать в одну область памяти.Это связано с тем, что каждый поток имеет свой собственный массив bin [], результаты корректны для двоичных объектов, выделенных как для кучи, так и для стека, и нет различий в производительности для однопоточных запусков.Я воспроизвел проблему на другом оборудовании (Intel Xeon и AMD Opteron) с компиляторами GCC и Intel C ++.Все тесты проводились в Linux (Ubuntu и RedHat).
Кажется, нет причин, по которым хорошая производительность OpenMP должна ограничиваться стековыми массивами.
Есть предположения?Может быть, доступ потоков к куче проходит через какой-то общий шлюз в Linux?Как мне это исправить?
Полная программа, с которой можно поиграться, приведена ниже:
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
int main(const int argc, const char* argv[])
{
const int N=10241024;
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;
}
Ниже приведены примеры выходных данных программы:
*1024* для 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).
Буду очень признателен за любую помощь!