Уменьшение массива в OpenMP

Я пытаюсь распараллелить следующую программу, но не знаю, как уменьшить массив. Я знаю, что это невозможно сделать, но есть ли альтернатива? Благодарю. (Я добавил сокращение на m, что неправильно, но хотел бы получить совет о том, как это сделать.)

#include  #include  #include  #include  using namespace std; int main () { int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13}; int S [10]; time_t start_time = time(NULL); #pragma omp parallel for private(m) reduction(+:m) for (int n=0 ; n<10 ; ++n ){ for (int m=0; m<=n; ++m){ S[n] += A[m]; } } time_t end_time = time(NULL); cout << end_time-start_time; return 0; } 

Да, с помощью OpenMP можно сделать сокращение массива. В Фортране это даже построило для этого. В C / C ++ вы должны сделать это сами. Вот два способа сделать это.

Первый метод делает частную версию S для каждого streamа, заполняет их параллельно, а затем объединяет их в S в критическом разделе (см. Код ниже). Второй метод делает массив с размерами 10 * nthreads. Заполняет этот массив параллельно, а затем объединяет его в S без использования критического раздела. Второй метод намного сложнее и может иметь проблемы с кешем, особенно в многопроцессорных системах, если вы не будете осторожны. Для получения дополнительной информации см. Гистограммы заполнения (уменьшение массива) параллельно с OpenMP без использования критического раздела

Первый метод

 int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13}; int S [10] = {0}; #pragma omp parallel { int S_private[10] = {0}; #pragma omp for for (int n=0 ; n<10 ; ++n ) { for (int m=0; m<=n; ++m){ S_private[n] += A[m]; } } #pragma omp critical { for(int n=0; n<10; ++n) { S[n] += S_private[n]; } } } 

Второй метод

 int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13}; int S [10] = {0}; int *S_private; #pragma omp parallel { const int nthreads = omp_get_num_threads(); const int ithread = omp_get_thread_num(); #pragma omp single { S_private = new int[10*nthreads]; for(int i=0; i<(10*nthreads); i++) S_private[i] = 0; } #pragma omp for for (int n=0 ; n<10 ; ++n ) { for (int m=0; m<=n; ++m){ S_private[ithread*10+n] += A[m]; } } #pragma omp for for(int i=0; i<10; i++) { for(int t=0; t 

У меня есть два замечания относительно ответа Збосона:
1. Метод 1, безусловно, правильный, но цикл восстановления фактически запускается серийно из-за критического #pragma omp, который, конечно, необходим, так как частичные матрицы являются локальными для каждого streamа, и соответствующее сокращение должно выполняться streamом из-за matrix.
2. Способ 2: Цикл инициализации может перемещаться за пределы отдельной секции и, следовательно, стать параллелизуемым.

Следующая программа реализует редукцию массива с использованием объекта сокращения, установленного пользователем с помощью OpenMP v4.0 :

 /* Compile with: gcc -Wall -fopenmp -o ar ar.c Run with: OMP_DISPLAY_ENV=TRUE OMP_NUM_THREADS=10 OMP_NESTED=TRUE ./ar */ #include  #include  struct m10x1 {int v[10];}; int A [] = {84, 30, 95, 94, 36, 73, 52, 23, 2, 13}; struct m10x1 S = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; int n,m=0; void print_m10x1(struct m10x1 x){ int i; for(i=0;i<10;i++) printf("%d ",xv[i]); printf("\n"); } struct m10x1 add_m10x1(struct m10x1 x,struct m10x1 y){ struct m10x1 r ={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; int i; for (i=0;i<10;i++) rv[i]=xv[i]+yv[i]; return r; } #pragma omp declare reduction(m10x1Add: struct m10x1: \ omp_out=add_m10x1(omp_out, omp_in)) initializer( \ omp_priv={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} ) int main () { #pragma omp parallel for reduction(m10x1Add: S) for ( n=0 ; n<10 ; ++n ) { for (m=0; m<=n; ++m){ Sv[n] += A[m]; } } print_m10x1(S); } 

Это следует дословно на примере сокращения числа номеров на стр. 97 функций OpenMP 4.0 .

Хотя параллельная версия работает правильно, возможно, проблемы с производительностью, которые я не исследовал:

  1. Входы и выходы add_m10x1 передаются по значению.
  2. Цикл в add_m10x1 запускается серийно.

Сами «проблемы с производительностью» являются моими собственными делами, и совершенно просто не вводить их:

  1. Параметры add_m10x1 должны передаваться по ссылке (через указатели в C, ссылки на C ++)
  2. Вычисление в add_m10x1 должно выполняться на месте.
  3. add_m10x1 должен быть объявлен void и оператор return удален. Результат возвращается через первый параметр.
  4. Декларация о сокращении прагмы должна быть соответственно изменена, объединитель должен быть просто вызовом функции, а не назначением (спецификации v4.0 p181 строки 9,10).
  5. Цикл for в add_m10x1 может быть распараллелен через omp-параллель для прагмы
  6. Параллельная вложенность должна быть включена (например, через OMP_NESTED = TRUE)

Измененная часть кода:

 void add_m10x1(struct m10x1 * x,struct m10x1 * y){ int i; #pragma omp parallel for for (i=0;i<10;i++) x->v[i] += y->v[i]; } #pragma omp declare reduction(m10x1Add: struct m10x1: \ add_m10x1(&omp_out, &omp_in)) initializer( \ omp_priv={{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} ) 

Если перевод кода в Fortran, который может использовать массивы в операциях сокращения OpenMP, не привлекает вас, вы можете использовать кучу временных переменных. Например

 int S0, S1, S2, ..., S9; ... #pragma omp parallel for private(...) shared(S0, S1, S2, ..., S9) \ reduction(+:S0, S1, S2, ..., S9) for ... 

Это оставляет вам непревзойденную перспективу написать какой-либо оператор if или case чтобы определить, какой из временных файлов должен быть обновлен. Если ваш код является просто примером, который вы хотите использовать для обучения, продолжайте.

Но если ваше намерение действительно писать параллельную префиксную рутину, тогда выполните поиск. Это хорошее место для старта.

  • Ожидание параллельных сценариев партии
  • Разрыв параллельный.
  • Почему я должен отдавать предпочтение одиночному «ожидание Task.WhenAll» в течение нескольких ожиданий?
  • mpi: блокировка против неблокирующего
  • В чем разница между асинхронным программированием и многоstreamовой обработкой?
  • Разложение Холецкого с помощью OpenMP
  • Параллель.For (): обновить переменную вне цикла
  • Является ли это ошибкой в ​​Files.lines (), или я что-то не понимаю о параллельных streamах?
  • Как настроить тонкую пул streamов для фьючерсов?
  • Какова цель инструкции «ПАУЗА» в x86?
  • Параллельные streamи, коллекторы и безопасность streamов
  • Давайте будем гением компьютера.