У меня есть два замечания по поводу ответа Zboson в:
1. Метод 1, конечно, правильно, но цикл редукция фактически выполняются серийно, из-за #pragma omp критический, который, конечно, необходим, поскольку частичные матрицы являются локальными для каждого потока, а corr требуемое сокращение должно выполняться нитью из-за матрицы.
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 <stdio.h>
#include <omp.h>
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 ",x.v[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++) r.v[i]=x.v[i]+y.v[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){
S.v[n] += A[m];
}
}
print_m10x1(S);
}
Это следует дословно сложный пример сокращения числа на стр 97 OpenMP 4.0 features.
Хотя параллельная версия корректно работает, то, вероятно, проблемы с производительностью, которые я не исследованные:
- add_m10x1 входов и выходов передаются по значению.
- Цикл в add_m10x1 запускается серийно.
Упомянутый «проблемы производительности» являются моего собственного изготовления, и это совершенно просто, чтобы не вводить их:
- Параметры для add_m10x1 должны быть переданы по ссылке (с помощью указателей в C, ссылки в C++)
- Вычисление в add_m10x1 должно выполняться на месте.
- add_m10x1 должно быть объявлено недействительным, а оператор возврата удален. Результат возвращается через первый параметр.
- Декларация о сокращении прагмы должна быть соответственно изменена, объединитель должен быть просто вызовом функции, а не назначением (спецификации v4.0 p181 строк 9,10).
- для цикла в add_m10x1 может быть распараллелены через OMP параллельно для прагме
- Параллельный раскрой должен быть включен (например, через 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}})
Сокращение на массивах в C/C++ теперь возможно с OpenMP 4.5. В принципе, вы должны указать разделы _array_ (см. Раздел 2.4, стр. 44 спецификации OpenMP 4.5). Ваша спецификация #pragma будет выглядеть так: '#pragma omp parallel для частного (n, m) сокращения (+: S [: 10])' Будьте осторожны, однако вы должны понимать, что каждая нить будет выделить собственную версию раздела массива; если вы сделаете это на больших массивах со многими потоками, вы сделаете так, что ваши воспоминания будут взорваны. –
@HugoRaguet, это удобно иметь синтаксис, чтобы сделать это сейчас, но это [не обязательно означает, что это сделано оптимально] (http://stackoverflow.com/a/35805567/2542702), т. Е. Все же может быть полезно сделать это вручную , –
Я хотел бы добавить, что наличие m дважды в одной строке omp невозможно. Он генерирует эту ошибку: появляется в блоках данных несколько раз во время компиляции. –