2. План лекции
Системы параллельного программирования
Типовые модели программирования и шаблоны
Знакомство с технологией OpenMP
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 2 / 66
3. Системы параллельного программирования
Много возможных моделей программирования, отражающих
различные архитектуры параллельных систем
Модели программирования, заточенные под конкретную
параллельную систему
Время жизни программ обычно больше времени жизни систем
Высокоуровневые абстракции, переносимость
Середина 90-х: зоопарк систем
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 3 / 66
4. Текущие стандарты де-факто
Общая память
OpenMP
Распределенная память
MPI (Message Passing Interface)
Гибридные архитектуры
OpenMP + MPI
Только MPI или OpenMP
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 4 / 66
5. Другие технологии
POSIX threads
C++11
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 5 / 66
6. Другие технологии
POSIX threads
C++11
Cilk
Intel Threading Building Blocks
Java Fork-Join Framework
Grand Central Dispatch
.NET Task Parallel Library
Parallel Patterns Library
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 5 / 66
7. Другие технологии
POSIX threads
C++11
Cilk
Intel Threading Building Blocks
Java Fork-Join Framework
Grand Central Dispatch
.NET Task Parallel Library
Parallel Patterns Library
PVM
Global Arrays
Unified Parallel C
X10
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 5 / 66
8. Другие технологии
POSIX threads
C++11
Cilk
Intel Threading Building Blocks
Java Fork-Join Framework
Grand Central Dispatch
.NET Task Parallel Library
Parallel Patterns Library
PVM
Global Arrays
Unified Parallel C
X10
MapReduce
Dryad
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 5 / 66
9. Подходы
Расширение “последовательного” языка
OpenMP
Cilk
UPC
Библиотека к “последовательному” языку
Pthreads
MPI
TBB
Язык со встроенной поддержкой многопроцессности
Java, C#, Erlang, Go, X10, C++12
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 6 / 66
10. Критерии оценки программы
Ясность
Масштабируемость
Эффективность
Удобство сопровождения
Согласованность с целевой системой
Переносимость
Эквивалентность последовательной программе
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 7 / 66
11. Типовые модели программирования и шаблоны
SPMD (Single Program Multiple Data)
Loop Parallelism
Master/Worker
Fork/Join
Тесно пересекаются друг с другом
Допускают комбинирование
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 8 / 66
12. Single Program Multiple Data
Все исполнители запускают одну программу
Инициализация
Получение уникального идентификатора
Выполнение программы с учетом идентификатора
Разное поведение: if(myId=...), for(..myId..)
Распределение данных между исполнителями
Идентификаторы входят в правила разбиения данных
(Операции обмена данными между исполнителями)
Завершение программы
Редукция результатов
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 9 / 66
13. Пример
1 # include < stdio .h >
2
3 int main ( int argc , char * argv []) {
4 const int num_steps = 1 E9 ;
5 double x , sum = 0.0;
6 const double step = 1.0/ num_steps ;
7 int i ;
8
9 for ( i = 0; i < num_steps ; i ++) {
10 x = ( i +0.5)* step ;
11 sum += 4.0/(1.0+ x * x );
12 }
13
14 printf ( " Pi = %.10 f n " , step * sum );
15 return 0;
16 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 10 / 66
14. Пример (MPI)
1 int main ( int argc , char * argv []) {
2 // init variables ...
3
4 MPI_Init (& argc , & argv );
5 MPI_Comm_rank ( MPI_COMM_WORLD , & my_id );
6 MPI_Comm_size ( MPI_COMM_WORLD , & numprocs );
7
8 i_start = my_id * ( num_steps / numprocs );
9 i_end = i_start + ( num_steps / numprocs );
10 if ( my_id == ( numprocs -1)) i_end = num_steps ;
11
12 for ( i = i_start ; i < i_end ; i ++)
13 {
14 x = ( i +0.5)* step ;
15 sum += 4.0/(1.0+ x * x );
16 }
17 sum *= step ;
18
19 MPI_Reduce (& sum , & pi , 1 , MPI_DOUBLE , MPI_SUM , 0 , MPI_COMM_WORLD );
20
21 if ( my_id == 0) printf ( " Pi = %.10 f n " , pi );
22
23 MPI_Finalize ();
24
25 return 0;
26 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 11 / 66
15. SPMD
Плюсы
Накладные расходы изолированы в начале и конце программы
Поддержка сложной координации между исполнителями
Возможность использования в любых MIMD-системах
Минусы
Программа сильно отличается от последовательной версии
Сложная логика распределения данных и балансировки нагрузки
вредит ясности программы
Плохо подходит для динамической балансировки нагрузки
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 12 / 66
16. Loop Parallelism
Вычислительно интенсивная часть алгоритма скрыта внутри цикла
Есть готовая последовательная программа
Итерации внутри цикла являются независимыми
Требуется распараллелить итерации, минимально модифицируя
при этом код исходной программы
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 13 / 66
17. Пример (OpenMP)
1 # include < stdio .h >
2 # include < omp .h >
3
4 int main ( int argc , char * argv []) {
5 const int num_steps = 1 E9 ;
6 double x , sum = 0.0;
7 const double step = 1.0/ num_steps ;
8 int i ;
9
10 # pragma omp parallel for private ( x ) reduction (+: sum )
11 for ( i = 0; i < num_steps ; i ++) {
12 x = ( i +0.5)* step ;
13 sum += 4.0/(1.0+ x * x );
14 }
15
16 printf ( " Pi = %.10 f n " , step * sum );
17 return 0;
18 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 14 / 66
18. Loop Parallelism
Плюсы
Минимальная модификация последовательной программы с
сохранением семантики
Инкрементальное распараллеливание
Эквивалентность последовательной программе
Минусы
Ориентация только на системы с общей памятью (OpenMP)
Оптимизация доступа к памяти может потребовать рефакторинга
Ограниченная масштабируемость
См. закон Амдала
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 15 / 66
19. Master/Worker
Требуется динамическая балансировка нагрузки между группой
исполнителей
Сложность заданий изменяется сильно и непредсказуемо
Вычисления не сводятся к простым циклам
Возможности отдельных исполнителей отличаются друг от друга
и могут (непредсказуемо) изменяться во время вычислений
Большинство распределенных вычислительных систем
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 16 / 66
21. Master/Worker - Часто встречающиеся вариации
Master становится worker-ом
Неявное распределение итераций в Loop Parallelism
Random work stealing
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 18 / 66
22. Master/Worker
Плюсы
Автоматическая балансировка нагрузки, не требует явного
назначения исполнителей
Хорошо работает для задач с независимыми заданиями
Подходит для различных видов платформ
Минусы
Неприменим для задач с зависимыми заданиями (требуется
координация и одновременное выполнение)
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 19 / 66
23. Рекурсивный Fork/Join
Задания создаются динамически, между заданиями существуют
сложные зависимости
Divide and Conquer, рекурсивные данные
Подходы
Каждому заданию по исполнителю
Плохо масштабируется
Пул исполнителей
Общая очередь заданий
Work stealing
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 20 / 66
24. Пример (Cilk)
1 cilk int fib ( int n )
2 {
3 if ( n < 2) return n ;
4 else
5 {
6 int x , y ;
7
8 x = spawn fib (n -1);
9 y = spawn fib (n -2);
10
11 sync ;
12
13 return ( x + y );
14 }
15 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 21 / 66
26. OpenMP (Open Multi-Processing)
Интерфейс параллельного программирования для
многопроцессорных систем с общей памятью
Поддерживает языки C/C++ и Fortran
Включает набор директив компилятора, библиотечных функций и
переменных окружения
Разрабатывается в рамках OpenMP Architecture Review Board с
1997 года
http://www.openmp.org/
OpenMP 2.5 (2005), OpenMP 3.0 (2008), OpenMP 3.1 (2011)
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 23 / 66
27. Реализации
OpenMP 2.5
GCC 4.2 и выше
Microsoft Visual C++ 2008 / 2010
Intel C/C++ Compiler 10.1 и выше
OpenMP 3.0
GCC 4.4 и выше
Intel C/C++ Compiler 11.0 и выше
См. http://openmp.org/wp/openmp-compilers/
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 24 / 66
37. Число потоков
По умолчанию обычно равно числу процессоров
Может быть задано явно
Переменная окружения OMP_NUM_THREADS
Функция omp_set_num_threads()
Директива #pragma omp parallel ... num_threads(N)
Может изменяться от секции к секции
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 34 / 66
38. Вычисление π
1 # include < stdio .h >
2
3 int main ( int argc , char * argv []) {
4
5 const int num_steps = 1 E9 ;
6 double x , sum = 0.0;
7 const double step = 1.0/ num_steps ;
8 int i ;
9
10 for ( i = 0; i < num_steps ; i ++) {
11 x = ( i +0.5)* step ;
12 sum += 4.0/(1.0+ x * x );
13 }
14
15 printf ( " Pi = %.10 f n " , step * sum );
16 return 0;
17 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 35 / 66
39. Вариант 1 (SMPD)
1 # include < stdio .h >
2 # include < omp .h >
3
4 int main ( int argc , char * argv []) {
5 double start = omp_get_wtime ();
6
7 const int num_steps = 1 E9 ;
8 double x , sum = 0.0;
9 const double step = 1.0/ num_steps ;
10 int i ;
11
12 int num_threads = omp _ge t_m a x _t h re a ds ();
13 double sum_tmp [ num_threads ];
14
15 # pragma omp parallel private (i , x )
16 {
17 int id = omp_get_threa d_n um ();
18 for ( i = id ; i < num_steps ; i = i + num_threads ) {
19 x = (i -0.5)* step ;
20 sum_tmp [ id ] += 4.0/(1.0+ x * x );
21 }
22 }
23 for ( i = 0; i < num_threads ; i ++) {
24 sum += sum_tmp [ i ];
25 }
26
27 printf ( " Pi = %.10 f n " , step * sum );
28 printf ( " Time : % f n " , omp_get_wtime () - start );
29 return 0;
30 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 36 / 66
40. Вариант 2 (for, critical, nowait)
1 # pragma omp parallel
2 {
3 int id = om p_ get_thread_num ();
4 # pragma omp for private ( x ) nowait
5 for ( i = 0; i < num_steps ; i ++) {
6 x = ( i +0.5)* step ;
7 sum_tmp [ id ] += 4.0/(1.0+ x * x );
8 }
9 # pragma omp critical
10 sum += sum_tmp [ id ];
11 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 37 / 66
41. Ложное разделение данных (false sharing)
1 # pragma omp parallel for private (j , i )
2 for ( j =0; j < N ; j ++){
3 for ( i =0; i < M ; i ++){
4 A [ j ] += work (i , j );
5 }
6 }
1 double temp ;
2
3 # pragma omp parallel for private (j ,i , temp )
4 for ( j =0; j < N ; j ++){
5 temp = 0.0;
6 for ( i =0; i < M ; i ++){
7 temp += work (i , j );
8 }
9 A [ j ] += temp ;
10 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 38 / 66
42. Вариант 3 (борьба с false sharing)
1 double sum_tmp [ num_threads *8];
2
3 # pragma omp parallel
4 {
5 int id = om p_ get_thread_num ();
6 int pos = id *8;
7 # pragma omp for private ( x ) nowait
8 for ( i = 0; i < num_steps ; i ++) {
9 x = ( i +0.5)* step ;
10 sum_tmp [ pos ] += 4.0/(1.0+ x * x );
11 }
12 # pragma omp critical
13 sum += sum_tmp [ pos ];
14 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 39 / 66
43. Оптимизация при компиляции
gcc -O1(2,3) ...
gcc: компиляция на форсаже с турбо-наддувом (Крис Касперски)
http://www.insidepro.com/kk/231/231r.shtml
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 40 / 66
44. Вариант 4 (for + reduction)
1 # include < stdio .h >
2 # include < omp .h >
3
4 int main ( int argc , char * argv []) {
5 double start = omp_get_wtime ();
6
7 const int num_steps = 1 E9 ;
8 double x , sum = 0.0;
9 const double step = 1.0/ num_steps ;
10 int i ;
11
12 # pragma omp parallel for private ( x ) reduction (+: sum )
13 for ( i = 1; i <= num_steps ; i ++) {
14 x = (i -0.5)* step ;
15 sum += 4.0/(1.0+ x * x );
16 }
17
18 printf ( " Pi = %.10 f n " , step * sum );
19 printf ( " Time : % f n " , omp_get_wtime () - start );
20 return 0;
21 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 41 / 66
47. Объединение циклов
1 for ( i =0; i < N ; i ++) {
2 FFT ( Npoints , A , B );
3 filter ( Npoints , B , H );
4 invFFT ( Npoints , B , A );
5 }
6 for ( i =0; i < N ; i ++) {
7 FFT ( Npoints , C , B );
8 filter ( Npoints , B , H );
9 invFFT ( Npoints , B , C );
10 }
1 for ( i =0; i < N ; i ++) {
2 FFT ( Npoints , A , B );
3 filter ( Npoints , B , H );
4 invFFT ( Npoints , B , A );
5 FFT ( Npoints , C , B );
6 filter ( Npoints , B , H );
7 invFFT ( Npoints , B , C );
8 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 44 / 66
48. Развертывание вложенных циклов
1 for ( j =0; j < N ; j ++) {
2 for ( i =0; i < M ; i ++) {
3 A [ i ][ j ] = work (i , j );
4 }
5 }
1 # pragma omp parallel for private ( ij , j , i )
2 for ( ij =0; ij < N * M ; ij ++) {
3 j = ij / N ;
4 i = ij % M ;
5 A [ i ][ j ] = work (i , j );
6 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 45 / 66
49. Распределение итераций между потоками
Атрибут schedule(type, chunk)
Type
static
Статическое распределение по принципу round-robin
dynamic
Динамическое распределение по принципу master-worker
guided
Динамическое распределение с уменьшающимися порциями
runtime
Значение берется из переменной окружения OMP_SCHEDULE
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 46 / 66
51. Множество Мандельброта
Z (n + 1) = Z (n)2 + C , Z (0) = 0
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 48 / 66
52. Вариант 1 (static)
1 #pragma omp parallel for
2 for ( int pix =0; pix < num_pixels ; ++ pix )
3 {
4 const int x = pix % width , y = pix / width ;
5 complex c = begin + complex ( x ... , y ..);
6 int n = M a nd e l brotCalculate (c , maxiter );
7 if ( n == maxiter ) n = 0;
8 pixels [ pix ] = n ;
9 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 49 / 66
53. Вариант 2 (dynamic)
1 #pragma omp parallel for schedule(dynamic,16)
2 for ( int pix =0; pix < num_pixels ; ++ pix )
3 {
4 const int x = pix % width , y = pix / width ;
5 complex c = begin + complex ( x ... , y ..);
6 int n = M a nd e l brotCalculate (c , maxiter );
7 if ( n == maxiter ) n = 0;
8 pixels [ pix ] = n ;
9 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 50 / 66
54. Вариант 1* (static + chunk)
1 #pragma omp parallel for schedule(static,1)
2 for ( int pix =0; pix < num_pixels ; ++ pix )
3 {
4 const int x = pix % width , y = pix / width ;
5 complex c = begin + complex ( x ... , y ..);
6 int n = M a nd e l brotCalculate (c , maxiter );
7 if ( n == maxiter ) n = 0;
8 pixels [ pix ] = n ;
9 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 51 / 66
55. Вариант 3 (ordered)
1 #pragma omp parallel for ordered schedule(static,1)
2
3 for ( int pix =0; pix < num_pixels ; ++ pix )
4 {
5 const int x = pix % width , y = pix / width ;
6 complex c = begin + complex ( x ... , y ..);
7 int n = M a nd e l brotCalculate (c , maxiter );
8 if ( n == maxiter ) n = 0;
9
10 # pragma omp ordered
11 {
12 // print pixel on screen
13 }
14 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 52 / 66
56. Quicksort
1 void quicksort_serial ( int arr [] , int low , int high ) {
2 int i = low ;
3 int j = high ;
4 int y = 0;
5 int z = arr [( low + high ) / 2];
6 do {
7 while ( arr [ i ] < z ) i ++;
8 while ( arr [ j ] > z ) j - -;
9 if ( i < j ) {
10 y = arr [ i ];
11 arr [ i ] = arr [ j ];
12 arr [ j ] = y ;
13 i ++;
14 j - -;
15 }
16 } while ( i <= j );
17
18 if ( low < j )
19 quicksort_serial ( arr , low , j );
20 if ( i < high )
21 quicksort_serial ( arr , i , high );
22 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 53 / 66
59. Вариант 1.3 (max_active_levels)
1 omp_set_nested (1);
2 o m p _ s e t _ m a x _ a c t i v e _ l e v e l s (4);
3 ...
4
5 # pragma omp parallel sections
6 {
7 # pragma omp section
8 {
9 if ( low < j )
10 quicksort_nested ( arr , low , j );
11 }
12 # pragma omp section
13 {
14 if ( i < high )
15 quicksort_nested ( arr , i , high );
16 }
17 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 56 / 66
60. Вариант 1.4 (порог)
1 if ( high - low < THRESHOLD ||
2 (j - low < THRESHOLD || high - i < THRESHOLD )) {
3 if ( low < j )
4 quicksort_nested ( arr , low , j );
5 if ( i < high )
6 quicksort_nested ( arr , i , high );
7 } else {
8
9 # pragma omp parallel sections
10 {
11 # pragma omp section
12 {
13 if ( low < j )
14 quicksort_nested ( arr , low , j );
15 }
16 # pragma omp section
17 {
18 if ( i < high )
19 quicksort_nested ( arr , i , high );
20 }
21 }
22 }
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 57 / 66
68. Домашнее задание (ДЗ №2, Задача 1)
Реализуйте параллельную версию алгоритма K-средних1 с
использованием OpenMP
Измерьте время выполнения, ускорение и эффективность для
различного числа потоков и размера задачи
Ускорение следует измерять относительно последовательной
программы без OpenMP
Время работы последовательной программы на тестовом наборе
должно быть не менее 10 секунд
Для всех запусков с одним размером задачи следует использовать
одинаковый набор данных
Прокомментируйте полученные результаты
1
http://en.wikipedia.org/wiki/K-means_clustering#Standard_algorithm
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 65 / 66
69. Учебный кластер
Железо
20 узлов (4 x Xeon E5645 2.40 GHz, 32 Gb RAM, 4 x 1 Tb)
Дисковое пространство ∼ 4 Tb
Gigabit Ethernet
Все что нужно для компиляции и запуска OpenMP-программ
gcc 4.4.3
Запуск программ на узлах кластера через систему очередей
Для доступа к кластеру пришлите на oleg.sukhoroslov@gmail.com
письмо:
Тема: "Учебный кластер"
Содержание: Ваша фамилия в транслите с маленькой буквы
(например, pupkin)
Инструкция по работе на кластере
Coming soon, см. на вики
О.В. Сухорослов 04 ()
Параллельное программирование 16.03.2011 66 / 66