Kselax.ru

Hacker Kselax — the best hacker in the world

Menu
  • Блог
  • Контакты
  • wp plugin генератор
  • Русский
    • Русский
    • English
Menu

Программирование для нескольких ядер PPL

Posted on 16 августа, 20143 апреля, 2015 by admin

Схема параллельних потоковЗдорова Господа!

В этой статье мы разберем, что такое параллельное программирование и как может программа параллельно выполняться. Я давно хотел эту тему поднять, но все никак она мне не попадалась в книгах, а так разбирать не охота было, а сейчас дошел я до этой темы и так мы будем рассматривать библиотеку PPL.

Содержание:

1. PPL (Parallel Patterns Library)
2. Алгоритмы параллельной обработки
3. Алгоритм parallel_for
4. Алгоритм parallel_for_each
5. Использование алгоритма parallel_invoke
6. Шаблон класса combinable
7. Объект класса task_group
8. Объект structured_task_group
9. Отображение набора Мандельброта
10. Рисование набора Мандельброта с помощью алгоритма parallel_invoke.
11. Три решенные задачи по PPL .


PPL (Parallel Patterns Library)  —  это так называемая библиотека шаблонов для параллельных вычислений.  Определена в файле ppl.h. Инструменты разработчика находятся в пространстве имен Concurrency.

Библиотека PPL определяет три вида средств параллельной обработки

  • Шаблоны для алгоритмов параллельных операций.
  • Шаблон класса для обслуживания совместно используемых ресурсов
  • Шаблоны класса для управления и группировки параллельных задач.

Алгоритмы параллельной обработки

Библиотека PPL предоставляет три алгоритма инициализации параллельной обработки на несколько ядрах.

  • Алгоритм parallel for — эквивалент цикла for, выполняющий итерации цикла параллельно.
  • Алгоритм parallel_for_each параллельно выполняет повторяющиеся операции с контейнером STL.
  • Алгоритм parallel_invoke параллельно выполняет набор из двух или нескольких независимых задач.

Рассмотрим как используется каждый алгоритм.

Алгоритм parallel_for

 Разсмотрим пример

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using std::cout;
using std::endl;
#include "ppl.h"
 
int main()
{
//возвести индексы элементов массива в квадрат используя ppl
const int count(100000);
int mass[count];
Concurrency::parallel_for(0,count,
[&mass](int n)->void//лямбда выражение
{
mass[n]=n*n;
});
 
for(int i=0;i<10;i++)
cout <<"i= "<<i<<" mass["<<i<<"]= "<<mass[i]<<endl;
 
return 0;
}

Вывод:

Вывод программыВ коде выше мы рассчитываем используя PPL кубы от 0 до 100000 элементов, я выводить все элементы стал, а только 10, чтобы показать, что программа корректно работает, вычисление в программе идет параллельно.

И еще в функцию parralel_for можно добавлять шаг, тоесть избирать элементы для которых будет рассчитываться куб, например для каждого третьего элемента.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using std::cout;
using std::endl;
#include "ppl.h"
 
int main()
{
//возвести индексы элементов массива в квадрат используя ppl
const int count(100000);
int mass[count]={0};
Concurrency::parallel_for(0,count,3,
[&mass](int n)->void//лямбда выражение
{
mass[n]=n*n;
});
 
for(int i=0;i<10;i++)
cout <<"i= "<<i<<" mass["<<i<<"]= "<<mass[i]<<endl;
 
return 0;
}

вывод будет уже другой чуток:

ppl_enter_three

Вывод уже другой как видно из рисунка выше. Мы просто добавили еще один параметр шаг и вызвали перегруженную функцию с шагом.

Рассмотрим следующий алгоритм parallel_for_each

Алгоритм parallel_for_each

Алгоритм parallel_for_each подобен алгоритму parallel_for, но отличия в том, что данная функция работает с контейнерами STL и принимает в качестве параметров итераторы. От простой примерчик использования parallel_for_each.

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
#include <iostream>
using std::cout;
using std::endl;
#include <array>
using std::array;
#include <string>
using std::string;
#include <algorithm>
using std::for_each;
#include "ppl.h"//подключаем библиотеку шаблонов
 
int main()
{
array<string, 5>mass={"hellow world", "ebta mazafaka",
"beny benasy", "supper pupper", "leddy gaga"};
cout <<"before:"<<endl;
for_each(mass.begin(),mass.end(),
[](string s)
{
cout <<"s= "<<s<<endl;
});
cout <<endl;
 
Concurrency::parallel_for_each(mass.begin(),mass.end(),
[](string& s)->void
{
size_t pos=s.find(' ');//находим позицию пробела
string s1=s.substr(0,pos);
string s2=s.substr(pos+1,s.length()-1);
s=s2+" "+s1;
});
 
for_each(mass.begin(),mass.end(),
[](string s)->void
{
cout <<"s= "<<s<<endl;
});
 
return 0;
}

вывод:

вывод из программыКак видим примерчик меняет местами слова в словосочетаниях, все это он делает параллельно, используя параллельные вычисления.

И еще рассмотрим примерчик, как из paralle_for можно вызвать исключение и обработать

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
#include <iostream>
using std::cout;
using std::endl;
#include <array>
using std::array;
#include <string>
using std::string;
#include <algorithm>
using std::for_each;
#include "ppl.h"//подключаем библиотеку шаблонов
 
int findIndex(array<string,5> mass,string name);
 
int main()
{
array<string, 5>mass={"hellow world", "ebta mazafaka",
"beny benasy", "supper pupper", "leddy gaga"};
cout <<"before:"<<endl;
for_each(mass.begin(),mass.end(),
[](string s)
{
cout <<"s= "<<s<<endl;
});
cout <<endl;
 
Concurrency::parallel_for_each(mass.begin(),mass.end(),
[](string& s)->void
{
size_t pos=s.find(' ');//находим позицию пробела
string s1=s.substr(0,pos);
string s2=s.substr(pos+1,s.length()-1);
s=s2+" "+s1;
});
 
cout <<"after:"<<endl;
for_each(mass.begin(),mass.end(),
[](string s)->void
{
cout <<"s= "<<s<<endl;
});
 
string name="gaga";
int a=findIndex(mass,name);
if(a==-1)
cout <<"not found name"<<endl;
else
cout <<"mass["<<a<<"]= "<<mass[a]<<endl;
 
return 0;
}
 
int findIndex(array<string,5> mass,string name)
{
int begin(0);
int end=mass.size();
 
try
{
Concurrency::parallel_for(begin,end,
[=](int n)->void
{
int pos=mass[n].find(' ');
string s1=mass[n].substr(0,pos);
if(name==s1)//если найдено первое слово, то возвращаем индекс
throw n;
});
}
catch(int& index)
{
cout <<"index= "<<index<<endl;
return index;
}
 
return -1;
}

Вывод:

ppl 170В коде выше алгоритм parallel_for_each меняет местами имена и фамилии в каждом элементе массива mass, используя параллельные операции. Вывод, созданный алгоритмом STL for_each, демонстрирует, что все работает так, как нада.

Затем мы создаем искомую строку name. Переменная a инициализируется значением, возвращенным функцией findIndex(). Оператор if выводит сообщение в зависимости от значения переменной a, и последняя строка вывода демонстрирует что значение переменной name действительно было найдено в массиве mass.

Использование алгоритма parallel_invoke

Алгоритм parallel_invoke определяется шаблоном, который позволяет решать параллельно до десяти задачь. Вполне очевидно, что нет никакого смысла пытаться решать параллельно больше задач, чем есть процессоров на вашем компьютере. Аргументы функции parallel_invoke — это объекты функции, определяющие задачи, которые будут решаться параллельно. Рассмотрим пример.

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
#include <iostream>
using std::cout;
using std::endl;
#include <array>
using std::array;
#include "ppl.h";
 
int main()
{
array<int, 10> kvadrat;
array<int, 10> cub;
 
Concurrency::parallel_invoke(
[&kvadrat]()->void
{
for(int i=0;i<kvadrat.size();i++)
kvadrat[i]=i*i;
},
[&cub]()->void
{
for(int i=0;i<cub.size();i++)
cub[i]=i*i*i;
}
 
);
 
//вывод результатов
for(int i=0;i<kvadrat.size();i++)
{
cout <<"i= "<<i<<" kvadrat["<<i<<"]= "<<kvadrat[i]
<<" cub["<<i<<"]= "<<cub[i]<<endl;
}
 
return 0;
}

вывод:

parallel_invoke

В примере выше выполняются две функции параллельно, одна из них подсчитывает куб, а вторая квадрат числа.

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

Шаблон класса combinable

combinable предназначен для помощи для помощи в совместном использовании ресурсов, которые могут быть сегментированы в локальные переменные для потоков и объединены после завершения вычисления.

Разсмотрим пример использования combinable для подсчета количества суммы элементов в массиве.

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
#include <iostream>
using std::cout;
using std::endl;
#include <array>
using std::array;
#include "ppl.h"
 
int main()
{
array<double, 10> mass;
Concurrency::combinable<double> sums;
Concurrency::parallel_for(static_cast<size_t>(0),mass.size(),
[&mass,&sums](size_t i)->void
{
mass[i]=i;
sums.local()+=mass[i];//создаем локальные переменные сум
});
//объединяем значение локальных переменных в одну
double sum=sums.combine(
[](double s1,double s2)->double
{
return s1+s2;
});
 
cout <<"sum= "<<sum<<endl;
 
return 0;
}

вывод:

ppl 171в коде выше все ясно, что там происходит. Создается класс combinable<double> sums;, затем в лямбда выражении в строчке sums.local()+=mass[i]; создаются две локальные суммы, как бы они параллельно создаются. А дальше в строке

1
2
3
4
5
double sum=sums.combine(
[](double s1,double s2)->double
{
return s1+s2;
});

из двух локальных переменных сумма записывается в переменную doulbe sum.

Объект класса task_group

Вы можете использовать объект task_group для параллельного решения двух и более задач, где задача может быть определена функтором или  лямбда-выражением. Объект task_group потокобезопасен, таким  образом, можете использовать его для инициализации задачи для разных потоков.

Рассмотрим пример кода в котором параллельно подсчитывается сумма в двух массивах с использованием объекта task_group.

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
#include <iostream>
using std::cout;
using std::endl;
#include <array>
using std::array;
#include <algorithm>
using std::generate;
#include "ppl.h"
 
int main()
{
array<int, 50> chet;
array<int, 50> nechet;
int n(0);
generate(chet.begin(),chet.end(),
[&n]()->int
{
return ++n*2;
});
n=0;
generate(nechet.begin(),nechet.end(),
[&n]()->int
{
return ++n*2-1;
});
 
int sumChet(0), sumNeChet(0);
Concurrency::task_group task;
//определяем функцию которая будет выполнятся параллельно
//в другом процессе.
task.run(
[&sumChet,&chet]
{
for(int i=0;i<chet.size();++i)
{
sumChet+=chet[i];
}
});
 
for(int i=0;i<nechet.size();++i)
{
sumNeChet+=nechet[i];
}
 
task.wait();//ждем пока досчитается задание
 
cout <<"sumChet= "<<sumChet<<endl;
cout <<"sumNeChet= "<<sumNeChet<<endl;
 
return 0;
}

вывод:

ppl 172в коде есть подробные комментарии подробно описывающие как работает код. В функции run можно определить несколько лямбда — выражений, в данном примере мы определили одно лямбда выражение, и оно выполняется отдельным процессом, идущий следом цикл for выполняется уже в том процессе в котором выполняется сама программа.

Объект structured_task_group

Данный объект может использоваться только для инициализации задачи одного потока. В отличие от объектов task_group вы не сможете передать лямбда-выражение или объект функции в функцию run() для объекта structured_task_group.

Извините но пример который был в книге у меня почему то не заработал, поэтому я пока этот объект пропустю.

Отображение набора Мандельброта

И так рассмотрим отображение набора Мандельброта, от есть код на winapi с помощью которого выводится окно с набором Мандельброта.

Показать »

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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
// test444.cpp: определяет точку входа для приложения.
//
 
#include "stdafx.h"
#include "test444.h"
 
#define MAX_LOADSTRING 100
 
// Глобальные переменные:
HINSTANCE hInst; // текущий экземпляр
TCHAR szTitle[MAX_LOADSTRING]; // Текст строки заголовка
TCHAR szWindowClass[MAX_LOADSTRING]; // имя класса главного окна
const size_t MaxIterations(8000);       // Maximum iterations before infinity
 
// Отправить объявления функций, включенных в этот модуль кода:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//функция обработки сообщений для главного окна
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);//функция обработки сообщений для дочернего окна
 
//собственные объявления функций
size_t IteratorPoint(double zReal, double zImaginary, double cReal, double cImaginary);
COLORREF Color(int n);//выбор цвета пикселя на основании n
void DrawSet(HWND hwnd);//Нарисовать набор Мандельброта
 
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
 
// TODO: разместите код здесь.
MSG msg;
HACCEL hAccelTable;
 
// Инициализация глобальных строк
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_TEST444, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
 
// Выполнить инициализацию приложения:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
 
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST444));
 
// Цикл основного сообщения:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
 
return (int) msg.wParam;
}
 
//
//  ФУНКЦИЯ: MyRegisterClass()
//
//  НАЗНАЧЕНИЕ: регистрирует класс окна.
//
//  КОММЕНТАРИИ:
//
//    Эта функция и ее использование необходимы только в случае, если нужно, чтобы данный код
//    был совместим с системами Win32, не имеющими функции RegisterClassEx'
//    которая была добавлена в Windows 95. Вызов этой функции важен для того,
//    чтобы приложение получило "качественные" мелкие значки и установило связь
//    с ними.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
 
wcex.cbSize = sizeof(WNDCLASSEX);
 
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST444));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TEST444);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
 
return RegisterClassEx(&wcex);
}
 
//
//   ФУНКЦИЯ: InitInstance(HINSTANCE, int)
//
//   НАЗНАЧЕНИЕ: сохраняет обработку экземпляра и создает главное окно.
//
//   КОММЕНТАРИИ:
//
//        В данной функции дескриптор экземпляра сохраняется в глобальной переменной, а также
//        создается и выводится на экран главное окно программы.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
 
   hInst = hInstance; // Сохранить дескриптор экземпляра в глобальной переменной
 
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      100, 100, 800, 600, NULL, NULL, hInstance, NULL);
 
   if (!hWnd)
   {
      return FALSE;
   }
 
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
 
   return TRUE;
}
 
//
//  ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  НАЗНАЧЕНИЕ:  обрабатывает сообщения в главном окне.
//
//  WM_COMMAND - обработка меню приложения
//  WM_PAINT -Закрасить главное окно
//  WM_DESTROY - ввести сообщение о выходе и вернуться.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
 
switch (message)
{
case WM_COMMAND:
wmId    = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Разобрать выбор в меню:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: добавьте любой код отрисовки...
DrawSet(hWnd);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
 
// Обработчик сообщений для окна "О программе".
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
 
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
 
size_t IteratePoint(double zReal, double zImaginary, double cReal,
double cImaginary)
{
double zReal2(0.0),zImaginary2(0.0);//компонент квадрата z
size_t n(0);
for(;n<MaxIterations;++n)//Итерация уравнения Zn=Zn-1**2+K
{//для набора Мандельброта K=c и Z0=c
zReal2=zReal*zReal;
zImaginary2=zImaginary*zImaginary;
if(zReal2+zImaginary2>4)//Если дистанция от начала > 2?
break;//точка уйдет в бесконечность, на этом закончить
 
//Вычислить следующее значение Z
zImaginary=2*zReal*zImaginary+cImaginary;
zReal=zReal2-zImaginary2+cReal;
}
return n;
}
 
COLORREF Color(int n)
{
if(n==MaxIterations) return RGB(0,0,0);
 
const int nColors=16;
switch(n%nColors)
{
case 0: return RGB(100,100,100);
case 1: return RGB(100,0,0);
case 2: return RGB(200,0,0);
case 3: return RGB(100,100,0);
case 4: return RGB(200,100,0);
case 5: return RGB(200,200,0);
case 6: return RGB(0,200,0);
case 7: return RGB(0,100,100);
case 8: return RGB(0,200,100);
case 9: return RGB(0,100,200);
case 10: return RGB(0,200,200);
case 11: return RGB(0,0,200);
case 12: return RGB(100,0,100);
case 13: return RGB(200,0,100);
case 14: return RGB(100,0,200);
case 15: return RGB(200,0,200);
default: return RGB(200,200,200);
};
}
 
void DrawSet(HWND hWnd)
{
  // Get client area dimensions, which will be the image size
  RECT rect;
  GetClientRect(hWnd, &rect);
  int imageHeight(rect.bottom);
  int imageWidth(rect.right);
 
  // Create bitmap for one row of pixels in image
  HDC hdc(GetDC(hWnd));                  // Get device context
  HDC memDC = CreateCompatibleDC(hdc);   // Get device context to draw pixels
  HBITMAP bmp = CreateCompatibleBitmap(hdc, imageWidth, 1);
  HGDIOBJ oldBmp = SelectObject(memDC, bmp);   // Selct bitmap into DC
 
  // Client area axes
  const double realMin(-2.1);       // Minimum real value
  double imaginaryMin(-1.3);        // Minimum imaginary value
  double imaginaryMax(+1.3);        // Maximum imaginary value
 
  // Set maximum imaginary so axes are the same scale
  double realMax(realMin+(imaginaryMax-imaginaryMin)*imageWidth/imageHeight);
 
  // Get scale factors to convert pixel coordinates
  double realScale((realMax-realMin)/(imageWidth-1));
  double imaginaryScale((imaginaryMax-imaginaryMin)/(imageHeight-1));
 
  double cReal(0.0), cImaginary(0.0);   // Stores c components
  double zReal(0.0), zImaginary(0.0);   // Stores z components
 
  for(int y = 0 ; y < imageHeight ; ++y)             // Iterate over image rows
  {
    zImaginary = cImaginary = imaginaryMax - y*imaginaryScale;
    for(int x = 0 ; x < imageWidth ; ++x)            // Iterate over pixels in a row
    {
      zReal = cReal = realMin + x*realScale;
      // Set current pixel color based on n
      SetPixel(memDC, x, 0, Color(IteratePoint(zReal, zImaginary, cReal, cImaginary)));
    }
    // Transfer pixel row to client area device context
    BitBlt(hdc, 0, y, imageWidth, 1, memDC, 0, 0, SRCCOPY);
  }
  SelectObject(memDC, oldBmp);
  DeleteObject(bmp);                       // Delete bitmap
  DeleteDC(memDC);                         // and our working DC
  ReleaseDC(hWnd, hdc);                    // Release client area DC
}

Вывод:

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

Рисование набора Мандельброта с помощью алгоритма parallel_invoke

Нам нужно создать новую версию функции DrawSet() по имени DrawSetParallel(), без замены исходной. Сохраним обе версии в исходном коде примера

код

Показать »

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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
// test444.cpp: определяет точку входа для приложения.
//
 
#include "stdafx.h"
#include "test444.h"
#include "ppl.h"
#include <functional>
 
#define MAX_LOADSTRING 100
 
// Глобальные переменные:
HINSTANCE hInst; // текущий экземпляр
TCHAR szTitle[MAX_LOADSTRING]; // Текст строки заголовка
TCHAR szWindowClass[MAX_LOADSTRING]; // имя класса главного окна
const size_t MaxIterations(8000);       // Maximum iterations before infinity
 
// Отправить объявления функций, включенных в этот модуль кода:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//функция обработки сообщений для главного окна
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);//функция обработки сообщений для дочернего окна
 
//собственные объявления функций
size_t IteratorPoint(double zReal, double zImaginary, double cReal, double cImaginary);
COLORREF Color(int n);//выбор цвета пикселя на основании n
void DrawSet(HWND hwnd);//Нарисовать набор Мандельброта
void DrawSetParallel(HWND hwnd);//использование алгоритма parallel_invoke
 
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
 
// TODO: разместите код здесь.
MSG msg;
HACCEL hAccelTable;
 
// Инициализация глобальных строк
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_TEST444, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
 
// Выполнить инициализацию приложения:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
 
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST444));
 
// Цикл основного сообщения:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
 
return (int) msg.wParam;
}
 
//
//  ФУНКЦИЯ: MyRegisterClass()
//
//  НАЗНАЧЕНИЕ: регистрирует класс окна.
//
//  КОММЕНТАРИИ:
//
//    Эта функция и ее использование необходимы только в случае, если нужно, чтобы данный код
//    был совместим с системами Win32, не имеющими функции RegisterClassEx'
//    которая была добавлена в Windows 95. Вызов этой функции важен для того,
//    чтобы приложение получило "качественные" мелкие значки и установило связь
//    с ними.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
 
wcex.cbSize = sizeof(WNDCLASSEX);
 
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST444));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TEST444);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
 
return RegisterClassEx(&wcex);
}
 
//
//   ФУНКЦИЯ: InitInstance(HINSTANCE, int)
//
//   НАЗНАЧЕНИЕ: сохраняет обработку экземпляра и создает главное окно.
//
//   КОММЕНТАРИИ:
//
//        В данной функции дескриптор экземпляра сохраняется в глобальной переменной, а также
//        создается и выводится на экран главное окно программы.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
 
   hInst = hInstance; // Сохранить дескриптор экземпляра в глобальной переменной
 
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      100, 100, 800, 600, NULL, NULL, hInstance, NULL);
 
   if (!hWnd)
   {
      return FALSE;
   }
 
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
 
   return TRUE;
}
 
//
//  ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  НАЗНАЧЕНИЕ:  обрабатывает сообщения в главном окне.
//
//  WM_COMMAND - обработка меню приложения
//  WM_PAINT -Закрасить главное окно
//  WM_DESTROY - ввести сообщение о выходе и вернуться.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
 
switch (message)
{
case WM_COMMAND:
wmId    = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Разобрать выбор в меню:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: добавьте любой код отрисовки...
DrawSetParallel(hWnd);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
 
// Обработчик сообщений для окна "О программе".
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
 
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
 
size_t IteratePoint(double zReal, double zImaginary, double cReal,
double cImaginary)
{
double zReal2(0.0),zImaginary2(0.0);//компонент квадрата z
size_t n(0);
for(;n<MaxIterations;++n)//Итерация уравнения Zn=Zn-1**2+K
{//для набора Мандельброта K=c и Z0=c
zReal2=zReal*zReal;
zImaginary2=zImaginary*zImaginary;
if(zReal2+zImaginary2>4)//Если дистанция от начала > 2?
break;//точка уйдет в бесконечность, на этом закончить
 
//Вычислить следующее значение Z
zImaginary=2*zReal*zImaginary+cImaginary;
zReal=zReal2-zImaginary2+cReal;
}
return n;
}
 
COLORREF Color(int n)
{
if(n==MaxIterations) return RGB(0,0,0);
 
const int nColors=16;
switch(n%nColors)
{
case 0: return RGB(100,100,100);
case 1: return RGB(100,0,0);
case 2: return RGB(200,0,0);
case 3: return RGB(100,100,0);
case 4: return RGB(200,100,0);
case 5: return RGB(200,200,0);
case 6: return RGB(0,200,0);
case 7: return RGB(0,100,100);
case 8: return RGB(0,200,100);
case 9: return RGB(0,100,200);
case 10: return RGB(0,200,200);
case 11: return RGB(0,0,200);
case 12: return RGB(100,0,100);
case 13: return RGB(200,0,100);
case 14: return RGB(100,0,200);
case 15: return RGB(200,0,200);
default: return RGB(200,200,200);
};
}
 
void DrawSet(HWND hWnd)
{
  // Get client area dimensions, which will be the image size
  RECT rect;
  GetClientRect(hWnd, &rect);
  int imageHeight(rect.bottom);
  int imageWidth(rect.right);
 
  // Create bitmap for one row of pixels in image
  HDC hdc(GetDC(hWnd));                  // Get device context
  HDC memDC = CreateCompatibleDC(hdc);   // Get device context to draw pixels
  HBITMAP bmp = CreateCompatibleBitmap(hdc, imageWidth, 1);
  HGDIOBJ oldBmp = SelectObject(memDC, bmp);   // Selct bitmap into DC
 
  // Client area axes
  const double realMin(-2.1);       // Minimum real value
  double imaginaryMin(-1.3);        // Minimum imaginary value
  double imaginaryMax(+1.3);        // Maximum imaginary value
 
  // Set maximum imaginary so axes are the same scale
  double realMax(realMin+(imaginaryMax-imaginaryMin)*imageWidth/imageHeight);
 
  // Get scale factors to convert pixel coordinates
  double realScale((realMax-realMin)/(imageWidth-1));
  double imaginaryScale((imaginaryMax-imaginaryMin)/(imageHeight-1));
 
  double cReal(0.0), cImaginary(0.0);   // Stores c components
  double zReal(0.0), zImaginary(0.0);   // Stores z components
 
  for(int y = 0 ; y < imageHeight ; ++y)             // Iterate over image rows
  {
    zImaginary = cImaginary = imaginaryMax - y*imaginaryScale;
    for(int x = 0 ; x < imageWidth ; ++x)            // Iterate over pixels in a row
    {
      zReal = cReal = realMin + x*realScale;
      // Set current pixel color based on n
      SetPixel(memDC, x, 0, Color(IteratePoint(zReal, zImaginary, cReal, cImaginary)));
    }
    // Transfer pixel row to client area device context
    BitBlt(hdc, 0, y, imageWidth, 1, memDC, 0, 0, SRCCOPY);
  }
  SelectObject(memDC, oldBmp);
  DeleteObject(bmp);                       // Delete bitmap
  DeleteDC(memDC);                         // and our working DC
  ReleaseDC(hWnd, hdc);                    // Release client area DC
}
 
// Draws the Mandelbrot set
void DrawSetParallel(HWND hWnd)
{
  HDC hdc(GetDC(hWnd));                  // Get device context
 
  // Get client area dimensions, which will be the image size
  RECT rect;
  GetClientRect(hWnd, &rect);
  int imageHeight(rect.bottom);
  int imageWidth(rect.right);
 
  // Create bitmap for one row of pixels in image
  HDC memDC1 = CreateCompatibleDC(hdc);   // Get device context to draw pixels
  HDC memDC2 = CreateCompatibleDC(hdc);   // Get device context to draw pixels
  HBITMAP bmp1 = CreateCompatibleBitmap(hdc, imageWidth, 1);
  HBITMAP bmp2 = CreateCompatibleBitmap(hdc, imageWidth, 1);
  HGDIOBJ oldBmp1 = SelectObject(memDC1, bmp1);   // Select bitmap into DC1
  HGDIOBJ oldBmp2 = SelectObject(memDC2, bmp2);   // Select bitmap into DC2
 
  // Client area axes
  const double realMin(-2.1);       // Minimum real value
  double imaginaryMin(-1.3);        // Minimum imaginary value
  double imaginaryMax(+1.3);        // Maximum imaginary value
 
  // Set maximum real so axes are the same scale
  double realMax(realMin+(imaginaryMax-imaginaryMin)*imageWidth/imageHeight);
 
  // Get scale factors to convert pixel coordinates
  double realScale((realMax-realMin)/(imageWidth-1));
  double imaginaryScale((imaginaryMax-imaginaryMin)/(imageHeight-1));
 
    // Lambda expression to create an image row
    std::function<void(HDC&, int)> rowCalc = [&] (HDC& memDC, int yLocal)
    {
      double zReal(0.0), cReal(0.0);
      double zImaginary(imaginaryMax - yLocal*imaginaryScale);
      double cImaginary(zImaginary);
      for(int x = 0 ; x < imageWidth ; ++x)            // Iterate over pixels in a row
      {
        zReal = cReal = realMin + x*realScale;
        // Set current pixel color based on n
        SetPixel(memDC, x, 0, Color(IteratePoint(zReal, zImaginary, cReal, cImaginary)));
      }
    };
 
  for(int y = 1 ; y < imageHeight ; y += 2)             // Iterate over image rows
  {
    Concurrency::parallel_invoke([&]{rowCalc(memDC1, y-1);}, [&]{rowCalc(memDC2, y);});
    // Transfer pixel rows to client area device context
    BitBlt(hdc, 0, y-1, imageWidth, 1, memDC1, 0, 0, SRCCOPY);
    BitBlt(hdc, 0, y, imageWidth, 1, memDC2, 0, 0, SRCCOPY);
  }
  // If there's an odd number of rows, take care of the last one
  if(imageHeight%2 == 1)
  {
    rowCalc(memDC1, imageWidth-1);
    BitBlt(hdc, 0, imageHeight-1, imageWidth, 1, memDC1, 0, 0, SRCCOPY);
  }
 
  SelectObject(memDC1, oldBmp1);
  SelectObject(memDC2, oldBmp2);
  DeleteObject(bmp1);                       // Delete bitmap 1
  DeleteObject(bmp2);                       // Delete bitmap 2
  DeleteDC(memDC1);                         // and our working DC 1
  DeleteDC(memDC2);                         // and our working DC 2
  ReleaseDC(hWnd, hdc);                     // Release client area DC
}

Данный код выводит тоже самое изображение что и предыдущий код, но он выводит код значительно быстрее чем предыдущий, потому что использует параллельные вычисления библиотеки ppl — алгоритм parallel_invoke.

На этом пожалуй и все, что я хотел написать про пароллельные вычисления. PPL — следует использовать тогда когда время выполнения программы занимает секунды, тогда реально от нее может быть пользя.

Да еще есть задачки решенные по PPL, 3 задачи, вы их мойдете перейдя по ссылке http://www.kselax.ru/2014/08/kniga-visual-c-2010-polnyj-kurs-reshenie-zadach-glava-13/

 

 

 

Добавить комментарий Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Рубрики

  • C++ (293)
  • JavaScript (1)
  • linux (1)
  • MFC (39)
  • node.js (2)
  • React (3)
  • vBulletin (5)
  • Visual Studio (9)
  • wordpress (18)
  • Разное (29)

Метки

Ajax bootstrap CentOS CLI expressjs FormData GDlib google Invisible reCAPTCHA JWT media MFC php react-router-dom redux repository wordpress RTTI STL vBulletin vector Visual Studio WINAPI wordpress wp-plugins XMLHttpRequest Двоичное дерево Задачи С++ Игры С++ Исключения С++ О-большое Операторы_С++ Перегрузка операторов С++ Поиск С++ Потоки Проектирование_С++ С++ Типы_С++ Типы С++ Шаблоны С++ библиотеки локализация макросы С++ сортировка С++

Свежие комментарии

  • RA3PKJ к записи visual C++, создание диалоговых окон.
  • admin к записи Как удалить изображение из google
  • Shakanris к записи Программка для заполнения форума на vBulletin 3.8.7
  • костя к записи visual C++, создание диалоговых окон.
  • Татьяна к записи Как удалить изображение из google
©2021 Kselax.ru Theme by ThemeGiant