Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.67/9: Рейтинг темы: голосов - 9, средняя оценка - 4.67
0 / 0 / 0
Регистрация: 19.05.2015
Сообщений: 2
1

Как добиться ускорения OpenMP C++

02.05.2016, 14:13. Просмотров 1736. Ответов 3
Метки нет (Все метки)


Доброго времени суток.
Я новичок в параллельном программировании. Передо мной поставили задачу расспараллелить с помощью OpenMP решение системы диффуров(в моем случае блочным методом), чтобы было ускорение хоть незначительное. Но у меня получается среднее время выполнение немного больше, в сравнении с последовательным.
Есть подозрения, что просто напросто время создания потоков и различных синхронизаций больше времени вычисления самой задачи. Например, в MPI это время существенно по понятным причинам.
Вопрос. Правильны ли мои догадки?
Спасибо
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
#include <math.h>
#include <ctime>
#include <Windows.h>
#include <omp.h>
using namespace std;
 
#define M_PI 3.14159265358979323846264
 
double* func(double, double*);
 
int main()
{ int k,nx;
  double err,norm_uk,norm_u;
  
  k = 4;
 
  double *b  = new double[k];
  double **a = new double*[k];
  for (int i=0; i<k; i++) a[i] = new double[k];
 
  // численные параметры метода
  switch (k){
      case 3: b[0] =  0.375000000988621;
              b[1] =  0.166666667112590;
              b[2] =  0.125000000367365;
 
a[0][0] = 0.791666664858906; a[0][1] =  -0.208333332171824; a[0][2] =    0.041666666386801;
a[1][0] = 0.666666665998845; a[1][1] =   0.166666667065973; a[1][2] =  -0.000000000094075;
a[2][0] = 0.374999999483541; a[2][1] =   0.375000000309568; a[2][2] =   0.124999999927026;
              break;
      case 4: b[0] = 0.348611113377678;
              b[1] = 0.161111112870624;
              b[2] = 0.112500001104145;
              b[3] = 0.077777778709850;
              
a[0][0] =  0.897222216827019; a[0][1] = -0.366666661429374; a[0][2] =  0.147222219680671; a[0][3] = -0.026388888390867;
a[1][0] =  0.688888884782252; a[1][1] =  0.133333337319852; a[1][2] =  0.022222220287626; a[1][3] = -0.005555555176466;
a[2][0] =  0.424999997597292; a[2][1] =  0.300000002262193; a[2][2] =  0.174999998913219; a[2][3] = -0.012499999788098;
a[3][0] =  0.355555553501783; a[3][1] =  0.133333335324043; a[3][2] =  0.355555554589430; a[3][3] =  0.077777777967116;
              break;
  }
 
  nx = 3;   // размерность вектора решения системы ОДУ
  double *tk = new double[k];
  double *y  = new double[nx];
  double *u0 = new double[nx];
  double *uk = new double[nx];
  double **u = new double*[k],
         **s = new double*[k],
         **F = new double*[k],
         **p = new double*[k];
  for (int i=0; i<k; i++){
      u[i] = new double[nx];
      s[i] = new double[nx];
      F[i] = new double[nx];
      p[i] = new double[nx];
  }
 
  double *F0,*fx;
 
  double  t = 0;            // текущее время
  double tN = 2;            // конечное время
  double  h = 0.1;          // шаг интегрирования
  double tay = h / k;       // шаг между узлами
 
  //Время
    double start_time, end_time;
    start_time = omp_get_wtime();
 
  for (int i=0; i<k; i++) tk[i] = (i+1) * tay;
 
  // начальные условия
  y[0] = 0.01745;  y[1] = 0.035; y[2] = 0.051;
 
 // int temp_tN=tN*10;
 
   omp_set_num_threads(3);
 
//#pragma omp parallel for //private(F0,fx,u0,u,uk,err,s,F,p)
  for (t=0; t<=tN; t+=h){ 
      // начальные значения вектора узлов блока
      #pragma omp parallel for
      for (int i=0; i<k; i++) u0[i] = y[i]; 
      
      F0 = func(t,u0);
#pragma omp parallel
      {
     #pragma omp for
      for (int i=0; i<k; i++)
          for (int j=0; j<nx; j++) 
              u[i][j] = u0[j] + tk[i]*F0[j];
 
      // итерационный процесс
     #pragma omp for
      for (int j=0; j<nx; j++) uk[j] = u[k-1][j];
      }
      err = 1; 
      while (err > 1e-12){
#pragma omp parallel 
          {
        #pragma omp for
          for (int i=0; i<k; i++)
              for (int j=0; j<nx; j++)
                  s[i][j] = u0[j] + b[i]*F0[j]*tk[i]; 
      
        #pragma omp for
          for (int i=0; i<k; i++){
              fx = func(t+tk[i],u[i]);
              for ( int j=0; j<nx; j++) F[i][j] = fx[j];
          }
 
        #pragma omp for
          for (int i=0; i<k; i++){
              for (int j=0; j<nx; j++){
                  p[i][j] = 0;
                  for (int q=0; q<k; q++) p[i][j] += a[i][q]*F[q][j];
              }
          }
         
        #pragma omp for
          for (int i=0; i<k; i++)
              for (int j=0; j<nx; j++)
                  u[i][j] = s[i][j] + tk[i]*p[i][j];
      }
          norm_uk = 0; norm_u = 0;
          
        #pragma omp parallel for reduction(+:norm_uk)
          for (int j=0; j<nx; j++){
              norm_uk += uk[j]*uk[j];
          }
         #pragma omp parallel for reduction(+:norm_u)
          for (int j=0; j<nx; j++){
              norm_u  += u[k-1][j]*u[k-1][j];
          }
         
          err = fabs(sqrt(norm_uk) - sqrt(norm_u));
         
         #pragma omp parallel for
          for (int j=0; j<nx; j++) y[j] = uk[j] = u[k-1][j]; 
 
          delete fx;    
      
      }
 
      cout << t+h << '\t';
      for (int j=0; j<nx; j++)
          cout << y[j] << '\t';
      cout << '\n';
      } 
      delete F0;
 
  //Время
    end_time = omp_get_wtime();
    cout<<"time = "<< end_time-start_time<<'\n'; 
    cout<<"clock = "<<clock()<<'\n';
 
    cin.get();
    cin.get();
   
  return 1;
}
 
double* func(double t , double* x)
{ //***** Функция вычисления правой части дифференциального уравнения
  // x(1) - psi, x(2) - theta, x(3) - fi
  double *z = new double[3];
 
    #pragma omp parallel sections
    {       
        #pragma omp section
        {  
            z[0] = (0.1*sin(2*M_PI*2*t)*cos(x[2]) - 0.152*sin(2*M_PI*3*t)*sin(x[2])) / cos(x[1]);
        }
        #pragma omp section     
        { 
            z[1] = 0.1*sin(2*M_PI*2*t)*sin(x[2]) + 0.152*sin(2*M_PI*3*t)*cos(x[2]);
        }
        #pragma omp section 
        { 
            z[2] = 0.052*sin(2*M_PI*1*t) - (0.1*sin(2*M_PI*2*t)*cos(x[2]) - 0.152*sin(2*M_PI*3*t)*sin(x[2]))*tan(x[1]); 
        }
}
  return z;
}
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
02.05.2016, 14:13
Ответы с готовыми решениями:

OpenMP. Время выполнения программы больше чем без OpenMP
Сегодня первый раз сел за OpenMP. Читаю на сайте майкрософта как работает этот API. Так вот там...

OpenMP планировщик - добиться равномерного распределения задач
Есть следующий код: #include &lt;stdio.h&gt; #include &lt;unistd.h&gt; int cnttotal = 0; int cnt1 = 0,...

Как построить график скорости, ускорения, тангенциального ускорения по времени в двумерной плоскости?
В архиве расписана задача

Найти модуль скорости; модуль тангенциального ускорения; модуль нормального ускорения; модуль полного ускорения
1. Материальная точка движется по плоскости. Движение точки в векторном виде описывается уравнением...

3
24 / 24 / 8
Регистрация: 02.02.2016
Сообщений: 135
03.05.2016, 20:02 2
Хотел уточнить, в какой среде используете OMP?
MS VS нужно в свойствах проекта еще разрешить, иначе балласт.
Сколько ядер доступно в системе? Если ядер меньше 3 то omp_set_num_threads(3); бесполезен.
Не обязательно заходить в параллельную секцию, чтобы выполнить параллельный for. Вход-выход в секцию занимает время. Уж если зашли, так не выходите.
Но в вашем случае потери должны быть минимальны. И для вашего же случая k настолько мало, что накладные расходы по созданию и завершению процесса могут превысить полезный эффект. (Шла бы речь о k = 100; или более...). Так что ваши догадки правильные.
Попробуйте изменить алгоритм загрузив параллельные потоки, при минимальном их создании/удалении.
Например оптимизировать с 83 по 97 строки кода:
C++
1
2
3
4
5
6
7
8
9
10
11
12
F0 = func(t,y);
#pragma omp for
     for (int i=0; i<k; i++)
          for (int j=0; j<nx; j++)
          {
                 u[i][j] = y[j] + tk[i]*F0[j];
                 if (i == k-1)
                 {
                        uk[j] = u[k-1][j];
                        u0[j] = y[j];
                 }
          }
И т.д.
(Кстати, имхо, в 84 строке у вас ошибка. Почему не рушатся данные или не ругается ваш компилятор...)
А ваша функция быстрее будет работать с другой оптимизацией:
C++
1
2
3
4
5
6
7
8
9
10
11
double* func(double t , double* x)
{ //***** Функция вычисления правой части дифференциального уравнения
  // x(1) - psi, x(2) - theta, x(3) - fi
  double *z = new double[3];
  double si22 = 0.1*sin(2*M_PI*2*t);
  double si23 = 0.152*sin(2*M_PI*3*t);
  z[0] = (si22*cos(x[2]) - si23*sin(x[2])) / cos(x[1]);
  z[1] = si22*sin(x[2]) + si23*cos(x[2]);
  z[2] = 0.052*sin(2*M_PI*1*t) - (si22*cos(x[2]) - si23*sin(x[2]))*tan(x[1]); 
  return z;
}
Добавлено через 28 минут
Кажется здесь вы неэффективно используете reduction.
Цитата Сообщение от Annie Moro Посмотреть сообщение
#pragma omp parallel for reduction(+:norm_uk)
for (int j=0; j<nx; j++){
norm_uk += uk[j]*uk[j];
}
#pragma omp parallel for reduction(+:norm_u)
for (int j=0; j<nx; j++){
norm_u *+= u[k-1][j]*u[k-1][j];
}
Т.к. временные издержки ее создания превышают даже издержки создания параллельной секции. А для цикла всего из 3-х элементов.... в трехпоточной секции... да еще последовательно два таких цикла...
Судите сами.
1
0 / 0 / 0
Регистрация: 19.05.2015
Сообщений: 2
04.05.2016, 20:30  [ТС] 3
MS VS , OpenMP подключила в свойствах проекта, ядер 8. Распараллеливает все как надо.
Спасибо большое за развернутый ответ с объяснениями. Все ошибки и неточности по коду поняла, буду пытаться оптимизировать. В будущем эта программа будет на 12 уравнений,но преподаватель вынудил сначала ускорить вычисление трех, хоть я и пыталась объяснить, что это не очень разумно.
Еще раз благодарю за ответ.
0
24 / 24 / 8
Регистрация: 02.02.2016
Сообщений: 135
04.05.2016, 20:41 4
У вас там есть немного, что еще распараллелить.
Попробуйте на листке бумаги разложить весь алгоритм и вы найдете что можно в пучки повязать

ЗЫ. Забыл добавить. в 80 строке вашего кода вы правильно отказались от //#pragma omp parallel for
Параллельная секция работает только для for (int i=0; ... целых, с известным количеством итераций.
Посмотрите, нет ли варианте этот момент "сэмулировать" для компилятора.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
04.05.2016, 20:41

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь или здесь.

Найти ускорения грузов и угловые ускорения блоков
Добрый день, господа. Прошу помочь в решении данной задачи, интересуют два пункта: 6 и 8, остальные...

Как изменяется вектор ускорения?
Вектор скорости движущейся частицы изменяется по закону:...

Как подключить openMP?
#include &lt;stdio.h&gt; #include &lt;time.h&gt; #include &lt;conio.h&gt; #include &lt;locale.h&gt; #include &lt;stdlib.h&gt;...

Как добиться ТИЦ10? (:
Доброго времени суток. Народ, нужно догнать сайт до 10 ТИЦ без вложений. Кто что может...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.