Слайд 1СИНТАКСИС ДИРЕКТИВЫ 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
Слайд 2ОПЦИЯ COLLAPSE
collapse(n) — n последовательных тесновложенных циклов ассоциируется с данной
директивой.
Для циклов образуется общее пространство итераций, которое делится между
тредами.
Если опция не задана, то директива относится только к одному - непосредственно следующему за ней циклу.
Слайд 3ОПЦИЯ ORDERED
Опция для указания о том, что в цикле могут
встречаться директивы ordered.
В этом случае определяется блок внутри тела
цикла, который должен выполняться в порядке, установленном в последовательном цикле
Слайд 4ОПЦИЯ NOWAIT
По умолчанию в конце параллельного цикла происходит неявная барьерная
синхронизация параллельно работающих тредов –
дальнейшее выполнение происходит только тогда, когда
все треды достигнут данной точки (барьера).
Если подобная задержка не нужна, используют опцию nowait.
Это позволяет тредам,
уже дошедшим до конца цикла, продолжить выполнение без синхронизации с остальными тредами.
Слайд 5ПРИМЕР
#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
}
Слайд 6ЗАКЛЮЧЕНИЕ ПО РАСПАРАЛЛЕЛИВАНИЮ ЦИКЛОВ
При распараллеливании цикла надо убедиться в том,
что итерации данного цикла не имеют информационных зависимостей.
Если цикл
не содержит зависимостей, его итерации можно выполнять в любом порядке, в том числе параллельно.
Соблюдение этого требования компилятор
не проверяет, вся ответственность - на программисте.
Если дать указание компилятору распараллелить цикл, содержащий зависимости, результат работы программы может оказаться некорректным.
Задание – подобрать пример такого цикла, проверить выполнение параллельной программы.
Слайд 7ДИРЕКТИВА SECTIONS
Используется для реализации функционального параллелизма.
Эта директива определяет набор независимых
секций кода,
каждая из которых выполняется своим тредом.
Слайд 8СИНТАКСИС ДИРЕКТИВЫ 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 //отдельный тред
…
}
Слайд 9ПРИМЕР
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);
}
}
Слайд 10Перед первым участком кода в блоке sections директива section не
обязательна.
Какие треды будут выполнять какую секцию, не определено.
Если
число тредов больше количества секций,
то часть тредов для выполнения данного блока секций не будет задействована.
Если число тредов меньше количества секций,
то некоторым тредам достанется более одной секции.
ОСОБЕННОСТИ ВЫПОЛНЕНИЯ
Слайд 11Опция lastprivate определенно связана с последней section:
int n=0;
#pragma omp parallel
{ #pragma
omp sections lastprivate(n)
{ #pragma omp section
{ n=1;
}
#pragma omp section
{ n=2;
}
#pragma omp section
{ n=3;
}
}
printf(«n on
thread%d: %d\n", omp_get_thread_num(), n);
}
printf(«n out of parallel region: %d\n", n);
ОСОБЕННОСТИ ВЫПОЛНЕНИЯ
Слайд 12Переменная n объявлена как lastprivate.
Три треда, выполняющие секции section,
присваивают своей локальным копиям n разные значения.
По выходе из
области sections значение n из последней секции присваивается локальным копиям во всех тредах.
Поэтому все треды напечатают число 3.
Это же значение сохранится для переменной n и в последовательной области.
ОСОБЕННОСТИ ВЫПОЛНЕНИЯ
Слайд 13С. – ожидание одних (выполняющихся тредов) другими (уже выполненными).
С. действует
в некоторых случаях (см. ранее for, sections) по умолчанию.
Опция nowait
отменяет ожидание.
Самый распространенный способ синхронизации в OpenMP – барьер.
СИНХРОНИЗАЦИЯ
Слайд 14Синтаксис директивы:
#pragma omp barrier
Треды, выполняющие текущую параллельную область, дойдя до
этой директивы, останавливаются и ждут,
пока все треды не дойдут
до этой точки программы, после чего разблокировываются и продолжают работать дальше.
ДИРЕКТИВА BARRIER
Слайд 15#pragma omp parallel
{
printf("Message 1\n");
printf("Message 2\n");
#pragma omp barrier
printf("Message 3\n");
}
Выдачи разных тредов
Message 1 и Message 2 могут появиться в произвольном порядке.
Выдача
Message 3 со всех тредов придёт строго после двух предыдущих выдач.
ПРИМЕР – СИНХРОНИЗАЦИЯ ПЕЧАТИ
Слайд 16Синтаксис директивы:
#pragma omp ordered
{
...
}
Директива определяет блок внутри параллельного цикла, который
должен выполняться в том порядке, в котором итерации шли бы
в последовательном цикле.
Директива for должна содержать опцию ordered
ДИРЕКТИВА ORDERED
Слайд 17Для вложенных циклов выделенный блок операторов относится к самому внутреннему
из циклов.
Тред, выполняющий первую итерацию цикла, выполняет операции выделенного
блока.
Тред, выполняющий k-ую итерацию, должен сначала дождаться выполнения всех операций выделенного блока всеми тредами, выполняющими предыдущие итерации.
ОСОБЕННОСТИ ВЫПОЛНЕНИЯ
Слайд 18int i, n;
omp_set_num_threads(4);
#pragma omp parallel private (i, n)
{ n=omp_get_thread_num();
#pragma omp for
ordered
for (i=0; i
i);
#pragma omp ordered
{
printf("ordered:thread %d,iter %d\n",n, i);
}
}
}
Внутри тела цикла идут две выдачи –
одна вне блока ordered, а вторая – внутри него.
ПРИМЕР – СИНХРОНИЗАЦИЯ ПЕЧАТИ
Слайд 19В результате:
первая выдача будет неупорядоченной,
вторая идёт в порядке по
возрастанию номера итерации.
ПРИМЕР – СИНХРОНИЗАЦИЯ ПЕЧАТИ
Слайд 20В коде можно выделять критические разделы.
С помощью критических разделов можно
предотвратить одновременный доступ к одному сегменту кода (критическому разделу!) из
нескольких тредов.
Один тред получает доступ только тогда, когда другие не выполняют данный код.
КРИТИЧЕСКИЕ РАЗДЕЛЫ
Слайд 21Синтаксис директивы:
#pragma omp critical [name]
{
...
}
ДИРЕКТИВА CRITICAL
Слайд 22В каждый момент времени критический раздел может выполнять не более
одного треда.
Если критический раздел уже выполняет какой-либо тред, то
все другие треды будут заблокированы,
пока вошедший тред не закончит выполнение.
Как только работавший тред выйдет из критического раздела, один из заблокированных войдет в него.
Если на входе в критический раздел стояло несколько тредов, то случайным образом выбирается один из них.
Остальные заблокированные треды продолжают ожидание.
ОСОБЕННОСТИ ВЫПОЛНЕНИЯ
Слайд 23main()
{
int x;
x = 0;
#pragma omp parallel
shared(x)
{
#pragma omp critical
x = x + 1;
}
// end of parallel section
}
ПРИМЕР – КОРРЕКТНАЯ РАБОТА С ОБЩЕЙ ПЕРЕМЕННОЙ
Слайд 24Блок выполняется единственным тредом
Синтаксис директивы:
#pragma omp single [опции ...] newline
private (list)
firstprivate (list)
copyprivate (list)
nowait
{
structured_block //единственный
тред
}
ДИРЕКТИВА SINGLE
Слайд 25После выполнения треда новые значения переменных списка будут доступны всем
одноименным частным переменным (private и firstprivate), описанным в начале параллельной
области и используемым всеми её тредами.
Опция не может использоваться совместно с опцией nowait.
Переменные списка не могут быть перечислены в опциях private и firstprivate данной директивы single.
ОПЦИЯ COPYPRIVATE
Слайд 26int n;
#pragma omp parallel private(n)
{ n=omp_get_thread_num();
printf("n (start): %d\n", n);
#pragma omp single
copyprivate(n)
{ n=100;
}
printf("n (finish): %d\n", n);
}
Каждый тред присвоит переменной n значение, равное
своему номеру,
и напечатает его.
В области single один из тредов присвоит переменной n значение 100.
На выходе из области это значение будет присвоено переменной n на всех тредах.
В конце параллельной области значение n печатается ещё раз .
На всех тредах n=100.
ПРИМЕР (COPYPRIVATE)
Слайд 27Блок выполняется единственным master-тредом.
Остальные треды пропускают блок и продолжают работу
с оператора, расположенного за ним.
Неявной синхронизации директива не предполагает.
Синтаксис
директивы:
#pragma omp master newline
{
structured_block //master-тред
}
ДИРЕКТИВА MASTER
Слайд 28Используется для корректного обновления общих переменных.
Применяется к отдельному оператору типа
присваивания.
Синтаксис директивы:
#pragma omp atomic
statement_expression
ДИРЕКТИВА ATOMIC
Слайд 29statement_expression:
x op= expr;//составное присваивание
x = x op expr;
x++;
++x;
x--;
--x;
х – скалярная
переменная,
expr – скалярное выражение,
в котором не присутствует переменная
х.
op - оператор:
+
*
-
/
&
^
|
<<
>>
Слайд 30На время выполнения оператора блокируется доступ к данной переменной всем
запущенным в данный момент тредам, кроме треда, выполняющего операцию.
Атомарной
является только работа с переменной в левой части оператора присваивания.
Вычисления в правой части не обязаны быть атомарными
ОСОБЕННОСТИ ВЫПОЛНЕНИЯ
Слайд 31int count = 0;
#pragma omp parallel
{
#pragma omp atomic
count++;
}
printf("number of threads:
%d\n", count);
Чтобы предотвратить одновременное изменение несколькими тредами значения переменной, стоящей
в левой части оператора присваивания, используется директива atomic
ПРИМЕР
Слайд 32Используется для согласования состояния памяти (консистентность).
Синтаксис директивы:
#pragma omp flush [(list)]
ДИРЕКТИВА
FLUSH
Слайд 33Значения всех переменных (или переменных из списка), временно хранящиеся в
регистрах и кэш-памяти текущего треда, будут занесены в основную память.
Все
изменения переменных, сделанные тредом во время работы, станут видимы остальным тредам.
Если какая-то информация хранится в буферах вывода, то буферы будут сброшены и т.п.
Операция производится только с данными вызвавшего треда.
Данные, изменявшиеся другими тредами,
не затрагиваются.
ОСОБЕННОСТИ ВЫПОЛНЕНИЯ
Слайд 34Неявно flush без параметров присутствует (если нет опции nowait):
в
директиве barrier,
на входе и выходе областей действия директив parallel,
critical, ordered,
на выходе for, sections, single
в вызовах функций семафоров
flush вызывается для переменной, участвующей в операции, ассоциированной с директивой atomic.
flush не применяется на входе и выходе области действия директивы master