Форум программистов, компьютерный форум CyberForum.ru

C++

Войти
Регистрация
Восстановить пароль
 
Annie Moro
0 / 0 / 0
Регистрация: 19.05.2015
Сообщений: 2
#1

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

02.05.2016, 14:13. Просмотров 321. Ответов 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;
}
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
02.05.2016, 14:13     Как добиться ускорения OpenMP C++
Посмотрите здесь:
C++ OpenMP. Время выполнения программы больше чем без OpenMP
Как завершить цикл в OpenMP C++
C++ Linux Как скомпилировать программу с технологиями openmp?
Как выполнить распараллеливание с помощью OpenMP C++
C++ Как добиться 40 миллисекунд в консоле?
Как контролировать число создаваемых потоков? OpenMP C++
Как в среде Dev-C++ сделать доступными технологии OpenMP? C++
Visual C++ Как ускорить эту часть кода с использованием OpenMp?
C++ Не могу разобраться с OpenMP! Как правильно распараллелить формулу?
Программа на цикл for (как добиться вывода на экран?) C++
C++ Как добиться возможности компилить с поддержкой нового стандарта в Code Blocks (MinGW)?
Visual C++ OpenMP

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

Или воспользуйтесь поиском по форуму:
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Petrolion
24 / 24 / 7
Регистрация: 02.02.2016
Сообщений: 124
03.05.2016, 20:02     Как добиться ускорения OpenMP C++ #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-х элементов.... в трехпоточной секции... да еще последовательно два таких цикла...
Судите сами.
Annie Moro
0 / 0 / 0
Регистрация: 19.05.2015
Сообщений: 2
04.05.2016, 20:30  [ТС]     Как добиться ускорения OpenMP C++ #3
MS VS , OpenMP подключила в свойствах проекта, ядер 8. Распараллеливает все как надо.
Спасибо большое за развернутый ответ с объяснениями. Все ошибки и неточности по коду поняла, буду пытаться оптимизировать. В будущем эта программа будет на 12 уравнений,но преподаватель вынудил сначала ускорить вычисление трех, хоть я и пыталась объяснить, что это не очень разумно.
Еще раз благодарю за ответ.
Petrolion
24 / 24 / 7
Регистрация: 02.02.2016
Сообщений: 124
04.05.2016, 20:41     Как добиться ускорения OpenMP C++ #4
У вас там есть немного, что еще распараллелить.
Попробуйте на листке бумаги разложить весь алгоритм и вы найдете что можно в пучки повязать

ЗЫ. Забыл добавить. в 80 строке вашего кода вы правильно отказались от //#pragma omp parallel for
Параллельная секция работает только для for (int i=0; ... целых, с известным количеством итераций.
Посмотрите, нет ли варианте этот момент "сэмулировать" для компилятора.
Yandex
Объявления
04.05.2016, 20:41     Как добиться ускорения OpenMP C++
Ответ Создать тему
Опции темы

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