Слайд 1ДИРЕКТИВЫ OPENMP - PARALLEL
Основная директива для создания параллельной области
int main
()
{
//последовательная область, выполняется корневой тред
. . .
//Начало
параллельной области
#pragma omp parallel [опции]
{
//операторы выполняются всеми тредами
. . .
//все треды завершают работу, остается только корневой тред
}
//последовательная область, выполняется корневой тред
. . .
}
Слайд 2СИНТАКСИС ДИРЕКТИВЫ PARALLEL
#pragma omp parallel [опции ...] newline
{
}
if (scalar_expression)
num_threads
(integer_expression)
private (list)
firstprivate (list)
shared (list)
default (shared | none)
reduction (operator: list)
copyin (list)
Слайд 3ОПЦИЯ IF
if (scalar_expression)
– распараллеливание по условию.
Если значение выражения ≠
0,
то осуществляется распараллеливание.
Иначе операторы
параллельной области выполняются
единственным корневым тредом.
Слайд 4ПРИМЕР
#include
#include
using namespace std;
int main()
{
int n;
cout
"one thread"
>> n;
omp_set_num_threads(n);
#pragma omp parallel if( n>1 )
{
int k = omp_get_thread_num();
cout << "in thread #" << k << endl;
}
cout << "one thread" << endl;
return 0;}
Слайд 5ОПЦИЯ NUM_THREADS
num_threads (integer_expression)
– явное задание количества тредов,
которые будут выполнять
операторы параллельной области.
По умолчанию выбирается последнее
значение, установленное функцией
omp_set_num_threads(),
или (если
не вызывалась функция)
значение переменной
OMP_NUM_THREADS
Слайд 6ПРИМЕР
#include
#include
using namespace std;
int main()
{
int n;
cout
"one thread"
>> n;
#pragma omp parallel if( n>1 ) num_threads(n)
{
int k = omp_get_thread_num();
cout << "in thread #" << k << endl;
}
cout << "one thread" << endl;
return 0;
}
Слайд 7ЧЕМ ОПРЕДЕЛЯЕТСЯ КОЛИЧЕСТВО ТРЕДОВ?
Количество тредов в параллельной области определяется следующими
параметрами в порядке старшинства:
Значением опции if
Значением опции num_threads
Функцией omp_set_num_threads()
Значением переменной окружения OMP_NUM_THREADS
По умолчанию
– обычно это число CPU в узле.
Слайд 8ОПЦИИ ДОСТУПНОСТИ ДАННЫХ
Данные –
Разделяемые, или общие (для всех тредов)
Локальные (копии
в каждом треде).
Преимущество OpenMP – динамическое определение количества копий –
В
одной параллельной области
переменная х – локальная
В другой – разделяемая.
Слайд 9ОПЦИЯ PRIVATE
private (list)
- задаёт список переменных, для которых создается локальная
копия в каждом треде.
Переменные должны быть объявлены
до вхождения в
параллельную область.
Начальное значение локальных копий переменных из списка не определено ? задается в параллельной области.
Слайд 10ПРИМЕР
float s = 0;
#pragma omp parallel private(s)
{
s = s +
1;//некорректно
}
Значение копий переменной в параллельной области не определено
Слайд 11ОПЦИЯ FIRSTPRIVATE
firstprivate (list)
- задаёт список переменных, для которых создается локальная
копия в каждом треде.
Переменные должны быть объявлены
до вхождения в
параллельную область.
Начальное значение локальных копий переменных из списка определяется их значением в корневом треде.
Слайд 12ПРИМЕР
float s = 0;
#pragma omp parallel firstprivate(s)
{
s = s +
1;//корректно
}
Значение копий переменной в параллельной области определяется последним значением в
последовательной области
Слайд 13ОПЦИЯ SHARED
shared (list)
- задаёт список переменных, которые являются общими для
всех тредов.
Переменные должны быть объявлены
до вхождения в параллельную область.
Все
треды могут не только считывать, но и изменять их значения ? корректность использования обеспечивает программист.
Слайд 14ОПЦИЯ DEFAULT
default (shared|none)
default (shared)
всем переменным в параллельной области, которым явно
не назначена локализация, будет назначена shared
(эта опция используется по
умолчанию)
default (none)
всем переменным в параллельной области локализация должна быть назначена явно.
Слайд 15ОПЦИЯ REDUCTION
reduction (operator: list)
operator: +, *, -, &, |,
^, &&, ||
задаёт оператор и список переменных (ранее объявленных);
для
каждой переменной создаются локальные копии в каждом треде;
локальные копии инициализируются :
для + - | ^ || – 0 или аналоги,
для * & && – 1 или аналоги;
над локальными копиями переменных после выполнения всех операторов параллельной области выполняется заданный оператор
Слайд 16ПРИМЕР
...
int n = 0;
#pragma omp parallel reduction (+: n)
{
n++;
cout
"Текущее значение n:”;
cout
тредов: “ << n << endl;
...
Слайд 17ДИРЕКТИВЫ OPENMP – PARALLEL FOR
Основная директива для распараллеливания вычислений (распределения
итераций цикла между тредами)
. . .
//Начало параллельной области
#pragma
omp parallel for [опции]
{
//должен быть цикл
. . .
}
Слайд 18ОГРАНИЧЕНИЯ НА ПАРАЛЛЕЛЬНЫЕ ЦИКЛЫ
Результат программы не зависит от того,
какой
именно тред выполнит конкретную итерацию цикла.
Нельзя использовать побочный выход
(break, goto) из параллельного цикла.
Размер блока итераций, указанный в опции schedule, не должен изменяться в рамках цикла.
Формат параллельных циклов:
for([int_type] i = инвариант цикла;
i {<,>,=,<=,>=} инвариант цикла;
i {+,-}= инвариант цикла)
Слайд 19СИНТАКСИС ДИРЕКТИВЫ PARALLEL FOR
#pragma omp parallel for[опции ...] newline
{ ...for ...
}
schedule
(type [,chunk])
ordered
private (list)
firstprivate (list)
lastprivate (list)
shared
(list)
reduction (operator: list)
collapse (n)
nowait
Слайд 20СИНТАКСИС ДИРЕКТИВЫ FOR
#pragma omp for[опции ...] newline
{ ...for ...
}
Используется внутри параллельной
области, заданной директивой parallel, для указания на распараллеливание конкретного цикла.
Блок
не является обязательным для единственного оператора:
#pragma omp for[опции ...] newline
for ...
Слайд 21ПРИМЕР: ВЫЧИСЛЕНИЕ СУММЫ
void main ()
{
int i;
double ZZ, res=0.0;
omp_set_num_threads(2)
#pragma omp parallel
for reduction(+:res) private(ZZ)
for (i=0; i< 1000; i++)
{
ZZ = func(i);
res =
res + ZZ;
}
}
Слайд 22ОЦЕНКА ВРЕМЕНИ ВЫПОЛНЕНИЯ ПОСЛЕДОВАТЕЛЬНОЙ И ПАРАЛЛЕЛЬНОЙ ПРОГРАММ
Функция double omp_get_wtime()
возвращает
в вызвавшем треде время в секундах, прошедшее с некоторого момента
в прошлом.
Если фрагмент кода окружить вызовами функции, то разность возвращаемых значений равна времени выполнения команд данного фрагмента.
Функция double omp_get_wtick() возвращает в вызвавшем треде разрешение таймера в секундах.
Это время можно рассматривать как меру точности таймера
Слайд 23ПРИМЕР ЗАМЕРА ВРЕМЕНИ
...
double start_time, end_time, tick;
start_time = omp_get_wtime();
...
end_time = omp_get_wtime();
tick
= omp_get_wtick();
cout
Слайд 25ПРИМЕР: ВЫЧИСЛЕНИЕ ЧИСЛА Π
void main ()
{ long num_steps;
cout
> num_steps;
double step =
1./ num_steps;
double x, pi, sum = 0.0;
#pragma omp parallel for private(x) reduction(+:sum)
for (int i = 0; i <= num_steps; i++)
{ x = i*step;
sum = sum + 4 /(1 + x*x);
}
pi = step * sum;
int my_precision;
cout <<"precision = ";
cin >> my_precision;
cout.precision(my_precision);//default value = 6
cout << "pi = " << pi << endl;
}
Слайд 26ОПЦИИ ДИРЕКТИВЫ PARALLEL FOR
#pragma omp parallel for[опции ...] newline
{ ...for ...
}
schedule
(type [,chunk])
ordered
private (list)
firstprivate (list)
lastprivate (list)
shared
(list)
reduction (operator: list)
collapse (n)
nowait
Слайд 27ОПЦИЯ LASTPRIVATE
lastprivate (list)
переменным из списка присваивается результат с последней
итерации цикла - значение из команд того треда, который бы
последним исполнялся последовательно
Пример
int i,k;
#pragma omp parallel for private(i) lastprivate(k)
for(i=0; i<10; i++)
k = i*i;
// последов. область, i - не определено, k - определено
cout <<"k = “ << k; // k == 81
Слайд 28ОПЦИЯ SCHEDULE – УПРАВЛЕНИЕ НАГРУЗКОЙ
schedule (type [,num_iters])
В зависимости от параметров
(type, num_iters) выполнение итераций цикла
распределяется между тредами.
По умолчанию
num_iters=1
Если опция schedule не указана, то по умолчанию распределение зависит от реализации (CPU, OC).
Возможные значения type
dynamic
guided
runtime num_iters не задается
static
Слайд 29STATIC – СТАТИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ
ЗАГРУЗКИ ТРЕДОВ:
- КАЖДЫЙ ТРЕД (С НУЛЕВОГО)
БЕРЕТ ДЛЯ ВЫПОЛНЕНИЯ БЛОК ИЗ ИТЕРАЦИЙ ЦИКЛА ,
-OСТАВШИЕСЯ
ИТЕРАЦИИ СНОВА ПОСЛЕДОВАТЕЛЬНО РАСПРЕДЕЛЯЮТСЯ ПО ТРЕДАМ, ПОКА НЕ БУДУТ ВЫПОЛНЕНЫ ВСЕ ИТЕРАЦИИ.
ЕСЛИ
НЕ УКАЗАНО, ТО ИТЕРАЦИИ РАВНОМЕРНО РАСПРЕДЕЛЯЮТСЯ МЕЖДУ ТРЕДАМИ.
…
#pragma omp for schedule(static,2)
for (i=0; i{
S1;
S2;
…
Sm;
}
Слайд 30DYNAMIC – ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ЗАГРУЗКИ ТРЕДОВ:
КАЖДЫЙ ТРЕД БЕРЕТ ДЛЯ
ВЫПОЛНЕНИЯ БЛОК ИЗ ИТЕРАЦИЙ ЦИКЛА.
ОСВОБОДИВШИЕСЯ ТРЕДЫ СНОВА БЕРУТ
ПО
СЛУЧАЙНЫХ ИТЕРАЦИЙ, ПОКА НЕ БУДУТ ВЫПОЛНЕНЫ ВСЕ ИТЕРАЦИИ
…
#pragma omp for schedule(dynamic)
for (i=0; i{
S1;
S2;
…
Sm;
}
Слайд 31GUIDED – ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ЗАГРУЗКИ ТРЕДОВ:
- КАЖДЫЙ ТРЕД БЕРЕТ
ДЛЯ ВЫПОЛНЕНИЯ N0 (ЗАВИСИТ ОТ РЕАЛИЗАЦИИ) ИТЕРАЦИЙ,
КОТОРОЕ (ОТЛИЧИЕ ОТ
DYNAMIC)
НА СЛЕДУЮЩИХ ШАГАХ УМЕНЬШАЕТСЯ ДО
N0 ПРОПОРЦИОНАЛЬНО:
<КОЛИЧЕСТВО ИТЕРАЦИЙ ЦИКЛА> /<ЧИСЛО ТРЕДОВ>
NI ПРОПОРЦИОНАЛЬНО
<КОЛИЧЕСТВО ОСТАВШИХСЯ ИТЕРАЦИЙ ЦИКЛА> /<ЧИСЛО ТРЕДОВ>
…
Слайд 32RUNTIME – ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ЗАГРУЗКИ ТРЕДОВ
CПОСОБ РАСПРЕДЕЛЕНИЯ ИТЕРАЦИЙ ВЫБИРАЕТСЯ
ВО ВРЕМЯ РАБОТЫ ПРОГРАММЫ
ПО ЗНАЧЕНИЮ ПЕРЕМЕННОЙ СРЕДЫ OMP_SCHEDULE.
Слайд 33ПРИМЕР 1
#include
#include
#include
int main(int argc, char *argv[])
{int i;
#pragma
omp parallel private(i)
{//#pragma omp for schedule (static)
//#pragma omp for schedule
(static, 1)
//#pragma omp for schedule (static, 2)
//#pragma omp for schedule (dynamic)
//#pragma omp for schedule (dynamic, 2)
//#pragma omp for schedule (guided)
#pragma omp for schedule (guided, 2)
for (i=0; i<10; i++)
{ printf("thread %d executed iteration %d\n", omp_get_thread_num(), i);
Sleep(1);
}
}}
Слайд 35ЗАДАНИЕ
Варьируя число итераций, тредов и размер начального блока, проанализировать распределение
итераций по тредам.
Изменяется ли распределение итераций по тредам при нескольких
запусках одной и той же программы?
Примечание. void Sleep(int k) – задержка в миллисекундах (здесь для имитации вычислений).
!Если задать значение параметра (0), то работа потока может быть приостановлена для того, чтобы позволить другим ожидающим потокам выполняться (в примере 2).
Слайд 36ПРИМЕР 2 msdn.microsoft.com/ru-ru/library/x5aw0hdf(v=vs.90).aspx
Слайд 37ЗАДАНИЕ
Протестировать программу, изменяя значения основных параметров.
Отобразить результат графически.
Для каких режимов
существенно количество тредов?
Как изменится работа программы, если установить явно время
задержки?
Добавить свои комментарии в текст программы.
Слайд 39ОПЦИЯ COLLAPSE
collapse(n) — n последовательных тесновложенных циклов ассоциируется с данной
директивой.
Для циклов образуется общее пространство итераций, которое делится между
тредами.
Если опция не задана, то директива относится только к одному - непосредственно следующему за ней циклу.
Слайд 40ОПЦИЯ ORDERED
Опция для указания о том, что в цикле могут
встречаться директивы ordered.
В этом случае определяется блок внутри тела
цикла, который должен выполняться в порядке, установленном в последовательном цикле
Слайд 41ОПЦИЯ NOWAIT
По умолчанию в конце параллельного цикла происходит неявная барьерная
синхронизация параллельно работающих тредов –
дальнейшее выполнение происходит только тогда, когда
все треды достигнут данной точки (барьера).
Если подобная задержка не нужна, используют опцию nowait.
Это позволяет тредам,
уже дошедшим до конца цикла, продолжить выполнение без синхронизации с остальными тредами.
Слайд 42ПРИМЕР
#include
#define CHUNKSIZE 100
#define N 1000
main ()
{
int i, chunk;
float a[N], b[N], c[N];
// Some
initializations
for (i=0; i < N; i++)
a[i] = b[i] = i * 1.0;
chunk = CHUNKSIZE;
#pragma omp parallel shared(a,b,c,chunk) private(i)
{
#pragma omp for schedule(dynamic,chunk) nowait
for (i=0; i < N; i++)
c[i] = a[i] + b[i];
}
// end of parallel section
}
Слайд 43ЗАКЛЮЧЕНИЕ ПО РАСПАРАЛЛЕЛИВАНИЮ ЦИКЛОВ
При распараллеливании цикла надо убедиться в том,
что итерации данного цикла не имеют информационных зависимостей.
Если цикл
не содержит зависимостей, его итерации можно выполнять в любом порядке, в том числе параллельно.
Соблюдение этого требования компилятор
не проверяет, вся ответственность - на программисте.
Если дать указание компилятору распараллелить цикл, содержащий зависимости, результат работы программы может оказаться некорректным.
Задание – подобрать пример такого цикла, проверить выполнение параллельной программы.
Слайд 44ДИРЕКТИВА SECTIONS
Используется для реализации функционального параллелизма.
Эта директива определяет набор независимых
секций кода, каждая из которых выполняется своим тредом.
Слайд 45СИНТАКСИС ДИРЕКТИВЫ SECTIONS
#pragma omp sections [опции ...] newline
private (list)
firstprivate (list)
lastprivate (list)
reduction (operator: list)
nowait
{
#pragma
omp section newline
structured_block //отдельный тред
#pragma omp section newline
structured_block //отдельный тред
…
}
Слайд 46ПРИМЕР
int main()
{ int n;
#pragma omp parallel private(n)
{ n=omp_get_thread_num();
#pragma omp sections
{
#pragma omp section
{ printf("section1,
thread %d\n", n);
}
#pragma omp section
{ printf("section2, thread %d\n", n);
}
#pragma omp section
{ printf("section3,
thread %d\n", n);
}
}
printf("parallel region, thread %d\n", n);
}
}