Технология OpenMP

Содержание

Слайд 2

OpenMP - стандарт программного интерфейса приложений для параллельных систем с общей

OpenMP - стандарт программного интерфейса приложений для параллельных систем с общей

памятью. Поддерживает языки C, C++, Фортран.
Первая версия появилась в 1997 году, предназначалась для языка Fortran. Для С/С++ версия разработана в 1998 году. В 2008 году вышла версия OpenMP 3.0.
Есть аналог в .NET - Библиотека параллельных задач (TPL)
Слайд 3

Модель программы в OpenMP Программа состоит из последовательных и параллельных секций.

Модель программы в OpenMP

Программа состоит из последовательных и параллельных секций.
В начальный

момент времени создается главная нить, выполняющая последовательные секции программы.
При входе в параллельную секцию выполняется операция fork, порождающая семейство нитей. Каждая нить имеет свой уникальный числовой идентификатор (главной нити соответствует 0). При распараллеливании циклов все параллельные нити исполняют один код. В общем случае нити могут исполнять различные фрагменты кода.
При выходе из параллельной секции выполняется операция join. Завершается выполнение всех нитей, кроме главной.
Слайд 4

Компоненты OpenMP Директивы компилятора - используются для создания потоков, распределения работы

Компоненты OpenMP

Директивы компилятора - используются для создания потоков, распределения работы между

потоками и их синхронизации. Директивы включаются в исходный текст программы.
Подпрограммы (функции) библиотеки времени выполнения - используются для установки и определения атрибутов потоков. Вызовы этих подпрограмм включаются в исходный текст программы.
Переменные окружения - используются для управления поведением параллельной программы. Переменные окружения задаются для среды выполнения параллельной программы соответствующими командами.
Слайд 5

Подключение Конфигурацию рекомендуется установить в release

Подключение

Конфигурацию рекомендуется установить в release

Слайд 6

Слайд 7

Программа «Hello World» #include int main(int argc, char* argv[]) { #pragma

Программа «Hello World»

#include
int main(int argc, char* argv[])
{
#pragma omp parallel
{
printf("Hello World\n");
}
return

0;
}
Слайд 8

Директивы OpenMP #pragma omp задает границы параллельной секции программы. С данной

Директивы OpenMP

#pragma omp задает границы параллельной секции программы. С данной директивой

могут
использоваться следующие операторы:
• private;
• shared;
• default;
• firstprivate;
• reduction;
• if;
• copyin;
• num_threads.
Слайд 9

#pragma omp for Задает границы цикла, исполняемого в параллельном режиме. #pragma

#pragma omp for

Задает границы цикла, исполняемого в параллельном режиме.
#pragma omp

parallel {
#pragma omp for
for(int i = 1; i < size; ++i)
x[i] = (y[i-1] + y[i+1])/2;
}
С данной директивой могут использоваться следующие операторы:
• private;
• firstprivate;
• lastprivate;
• reduction;
• schedule;
• ordered;
• nowait.
Слайд 10

Слайд 11

#pragma omp parallel { for(int i = 1; i x[i] =

#pragma omp parallel {
for(int i = 1; i < size;

++i)
x[i] = (y[i-1] + y[i+1])/2;
} // каждый поток выполнит полный цикл for, // проделав много лишней работы
//сокращенный способ записи
#pragma omp parallel for
for(int i = 1; i < size; ++i)
x[i] = (y[i-1] + y[i+1])/2;
Слайд 12

При распараллеливании циклов вы должны убедиться в том, что итерации цикла

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

Если цикл не содержит зависимостей, компилятор может выполнять цикл в любом порядке, даже параллельно. Соблюдение этого важного требования компилятор не проверяет — вы сами должны заботиться об этом. Если вы укажете компилятору распараллелить цикл, содержащий зависимости, компилятор подчинится, что приведет к ошибке.
Кроме того, OpenMP налагает ограничения на циклы for, которые могут быть включены в блок #pragma omp for или #pragma omp parallel for block. Циклы for должны соответствовать следующему формату:
for([целочисленный тип] i = инвариант цикла; i {<,>,=,<=,>=} инвариант цикла; i {+,-}= инвариант цикла)
Слайд 13

Общие и частные данные Разрабатывая параллельные программы, вы должны понимать, какие

Общие и частные данные

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

(shared), а какие частными (private), — от этого зависит не только производительность, но и корректная работа программы. В OpenMP это различие очевидно, к тому же вы можете настроить его вручную.
Общие переменные доступны всем потокам из группы, поэтому изменения таких переменных в одном потоке видимы другим потокам в параллельном регионе. Что касается частных переменных, то каждый поток из группы располагает их отдельными экземплярами, поэтому изменения таких переменных в одном потоке никак не сказываются на их экземплярах, принадлежащих другим потокам.
По умолчанию все переменные в параллельном регионе — общие, но из этого правила есть три исключения.
Слайд 14

Слайд 15

Слайд 16

Во-первых, частными являются индексы параллельных циклов for. Во-вторых, частными являются локальные

Во-первых, частными являются индексы параллельных циклов for.  
Во-вторых, частными являются локальные

переменные блоков параллельных регионов.
В-третьих, частными будут любые переменные, указанные в разделах private, firstprivate, lastprivate и reduction.
Слайд 17

Слайд 18

float sum = 10.0f; MatrixClass myMatrix; /*Переменная j по умолчанию не

float sum = 10.0f;
MatrixClass myMatrix;
/*Переменная j по умолчанию не является частной, но явно

сделана таковой через раздел firstprivate.*/
int j = myMatrix.RowStart();
int i;
#pragma omp parallel {
/*i, j и sum сделаны частными для каждого потока из группы, т. е. каждый поток будет располагать своей копией каждой из этих переменных.
*/
#pragma omp for firstprivate(j) lastprivate(i) reduction(+: sum)
for(i = 0; i < count; ++i) {
/* переменная doubleI, потому что она объявлена в параллельном регионе. Любые нестатические и не являющиеся членами класса MatrixClass переменные, объявленные в методе myMatrix::GetElement, будут частными.*/
int doubleI = 2 * i;
for(; j < doubleI; ++j){
sum += myMatrix.GetElement(i, j);
}
}
}
Слайд 19

Раздел private говорит о том, что для каждого потока должна быть

Раздел private говорит о том, что для каждого потока должна быть создана

частная копия каждой переменной из списка. Частные копии будут инициализироваться значением по умолчанию (с применением конструктора по умолчанию, если это уместно). Например, переменные типа int имеют по умолчанию значение 0.
У раздела firstprivate такая же семантика, но перед выполнением параллельного региона он указывает копировать значение общей переменной в каждый поток, используя конструктор копирования, если это уместно.
Семантика раздела lastprivate тоже совпадает с семантикой раздела private, но при выполнении последней итерации цикла или раздела конструкции распараллеливания значения переменных, указанных в разделе lastprivate, присваиваются переменным основного потока. Если это уместно, для копирования объектов применяется оператор присваивания копий (copy assignment operator).
Слайд 20

Слайд 21

раздел reduction, но он принимает переменную и оператор. Поддерживаемые этим разделом

раздел reduction, но он принимает переменную и оператор. Поддерживаемые этим разделом операторы перечислены в таблице

, а у переменной должен быть скалярный тип (например, float, int или long, но не std::vector, int [] и т. д.). 
Переменная раздела reduction инициализируется в каждом потоке значением, указанным в таблице. В конце блока кода оператор раздела reduction применяется к каждой частной копии переменной, а также к исходному значению переменной.
Слайд 22

Операторы раздела reduction

Операторы раздела reduction

Слайд 23

nowait - Отменяет барьерную синхронизацию при завершении выполнения параллельной секции. schedule

nowait - Отменяет барьерную синхронизацию при завершении выполнения параллельной секции.
schedule -

По умолчанию в OpenMP для планирования параллельного выполнения циклов for применяется алгоритм, называемый статическим планированием (static scheduling). Это означает, что все потоки из группы выполняют одинаковое число итераций цикла. Если n — число итераций цикла, а T — число потоков в группе, каждый поток выполнит n/T итераций (если n не делится на T без остатка, ничего страшного). Однако OpenMP поддерживает и другие механизмы планирования, оптимальные в разных ситуациях: динамическое планирование (dynamic scheduling), планирование в период выполнения (runtime scheduling) и управляемое планирование (guided scheduling).
ordered - обеспечивает сохранение того порядка выполнения итераций цикла, который соответствует последовательному выполнению программы.
Слайд 24

Слайд 25

Слайд 26

Слайд 27

Слайд 28

Параллельная обработка в конструкциях, отличных от циклов OpenMP поддерживает параллелизм и

Параллельная обработка в конструкциях, отличных от циклов

OpenMP поддерживает параллелизм и на уровне функций. Этот механизм

называется секциями OpenMP (OpenMP sections).
Синтаксис:
#pragma omp parallel sections
{
#pragma omp section
//Вызов первой функции
#pragma omp section
//Вызов второй функции
}
Слайд 29

Слайд 30

void QuickSort (int numList[], int nLower, int nUpper) { if (nLower

void QuickSort (int numList[], int nLower, int nUpper)
{
if (nLower

< nUpper)
{
// Разбиение интервала сортировки
int nSplit = Partition (numList, nLower, nUpper);
#pragma omp parallel sections
{
#pragma omp section
QuickSort(numList, nLower, nSplit - 1);
#pragma omp section
QuickSort (numList, nSplit + 1, nUpper);
}
}
}
Слайд 31

Синхронизация Барьерная синхронизация. В некоторых ситуациях ее целесообразно выполнять наряду с

Синхронизация

Барьерная синхронизация. В некоторых ситуациях ее целесообразно выполнять наряду с неявной. Используется #pragma omp

barrier.
Критическая секция - директива #pragma omp critical [имя]. Если используется именованная критическая секцию, тогда доступ к блоку кода является взаимоисключающим только для других критических секций с тем же именем (это справедливо для всего процесса). Если имя не указано, директива ставится в соответствие некоему имени, выбираемому системой. Доступ ко всем неименованным критическим секциям является взаимоисключающим.
Слайд 32

Слайд 33

Слайд 34

Слайд 35

Слайд 36

Слайд 37

В параллельных регионах часто встречаются блоки кода, доступ к которым желательно

В параллельных регионах часто встречаются блоки кода, доступ к которым желательно предоставлять только

одному потоку, — например, блоки кода, отвечающие за запись данных в файл. Во многих таких ситуациях не имеет значения, какой поток выполнит код, важно лишь, чтобы этот поток был единственным. Для этого в OpenMP служит директива #pragma omp single.
Иногда возможностей директивы single недостаточно. В ряде случаев требуется, чтобы блок кода был выполнен основным потоком, — например, если этот поток отвечает за обработку GUI и вам нужно, чтобы какую-то задачу выполнил именно он. Тогда применяется директива #pragma omp master. В отличие от директивы single при входе в блок master и выходе из него нет никакого неявного барьера.
Чтобы завершить все незавершенные операции над памятью перед началом следующей операции, используйте директиву #pragma omp flush
Слайд 38

Функции OpenMP void omp_set_num_threads(int threads); Задает количество потоков (threads) при выполнении

Функции OpenMP

void omp_set_num_threads(int threads);
Задает количество потоков (threads) при выполнении параллельных секций

программы.
int omp_get_num_threads(void);
Возвращает количество потоков, используемых для выполнения параллельной секции.
int omp_get_max_threads(void);
Возвращает максимальное количество потоков, которые можно использовать для выполнения параллельных секций программы.
int omp_get_thread_num(void);
Возвращает идентификатор нити, из которой вызывается данная функция.
Слайд 39

int omp_get_num_procs(void); Возвращает количество процессоров, доступных в данный момент программе. int

int omp_get_num_procs(void);
Возвращает количество процессоров, доступных в данный момент программе.
int

omp_in_parallel(void);
Возвращает не ноль при вызове из активной параллельной секции программы.
void omp_set_dynamic(int threads);
Включает или отключает возможность динамического назначения количества потоков при выполнении параллельной секции. По умолчанию эта возможность отключена.
int omp_get_dynamic(void);
Возвращает не ноль, если динамическое назначение количества потоков разрешено.
Слайд 40

Переменные окружения OpenMP OMP_NUM_THREADS Задает количество нитей при выполнении параллельных секций

Переменные окружения OpenMP

OMP_NUM_THREADS
Задает количество нитей при выполнении параллельных секций программы.
OMP_SCHEDULE
Задает способ

распределения итераций циклов между нитями. Возможные значения: static; dynamic; guided.
OMP_DYNAMIC
Если этой переменной присвоено значение false, динамическое распределение итераций циклов между нитями запрещено, если true – разрешено.
OMP_NESTED
Если этой переменной присвоено значение false, вложенный параллелизм запрещен, если true – разрешен.
Слайд 41

void omp_set_nested(int nested); Разрешает или запрещает вложенный параллелизм. По умолчанию вложенный

void omp_set_nested(int nested);
Разрешает или запрещает вложенный параллелизм. По умолчанию вложенный параллелизм

запрещен.
int omp_get_nested(void);
Определяет, разрешен ли вложенный параллелизм.