Технология программирования OpenMP

Содержание

Слайд 2

OpenMP

OpenMP – технология параллельного программирования для компьютеров с общей памятью. Стандарт 3.0

OpenMP OpenMP – технология параллельного программирования для компьютеров с общей памятью. Стандарт
принят в мае 2008 года.
Один вариант программы для параллельного и последовательного выполнения.
Любой процесс состоит из нескольких нитей управления, которые имеют общее адресное пространство, но разные потоки команд и раздельные стеки.

Слайд 3

OpenMP

Макрос _OPENMP определён в формате yyyymm, где yyyy и mm – цифры

OpenMP Макрос _OPENMP определён в формате yyyymm, где yyyy и mm –
года и месяца, когда был принят поддерживаемый стандарт OpenMP.
Условная компиляция:
#include
int main(){
#ifdef _OPENMP
printf("OpenMP is supported!\n");
#endif
}

Слайд 4

OpenMP

Распараллеливание в OpenMP: вставка в текст программы специальных директив, а также вызов

OpenMP Распараллеливание в OpenMP: вставка в текст программы специальных директив, а также
вспомогательных функций.
SPMD-модель (Single Program Multiple Data) параллельного программирования: для всех параллельных нитей используется один и тот же код.

Слайд 5

OpenMP

Программа начинается с последовательной области – работает одна нить, при входе в

OpenMP Программа начинается с последовательной области – работает одна нить, при входе
параллельную область порождается ещё несколько нитей, между которыми распределяются части кода.
По завершении параллельной области все нити, кроме одной (нити-мастера), завершаются.
Любое количество параллельных и последовательных областей. Параллельные области могут быть вложенными друг в друга.

Слайд 6

OpenMP

В OpenMP переменные в параллельных областях программы разделяются на два основных класса:
shared

OpenMP В OpenMP переменные в параллельных областях программы разделяются на два основных
(общие; все нити видят одну и ту же переменную);
private (локальные, приватные; каждая нить видит свой экземпляр данной переменной).
По умолчанию, все переменные, порождённые вне параллельной области, при входе в неё остаются общими. Исключение составляют переменные, являющиеся счетчиками итераций в цикле. Переменные, порождённые внутри параллельной области, по умолчанию являются локальными.

Слайд 7

OpenMP

Директивы OpenMP в языке Си задаются указаниями препроцессору, начинающимися с #pragma omp.

OpenMP Директивы OpenMP в языке Си задаются указаниями препроцессору, начинающимися с #pragma

#pragma omp directive-name [опция[[,] опция]...]
Объектом действия большинства директив является один оператор или блок, перед которым расположена директива в исходном тексте программы.

Слайд 8

OpenMP

Чтобы задействовать функции библиотеки OpenMP периода выполнения (исполняющей среды), в программу нужно

OpenMP Чтобы задействовать функции библиотеки OpenMP периода выполнения (исполняющей среды), в программу
включить заголовочный файл omp.h (для программ на языке Фортран – файл omp_lib.h или модуль omp_lib).
Нужно задать количество нитей, выполняющих параллельные области программы, определив значение переменной среды OMP_NUM_THREADS:
export OMP_NUM_THREADS=n

Слайд 9

OpenMP

Функции для работы с системным таймером:
double omp_get_wtime(void);
Возвращает астрономическое время в секундах,

OpenMP Функции для работы с системным таймером: double omp_get_wtime(void); Возвращает астрономическое время
прошедшее с некоторого момента в прошлом.
double omp_get_wtick(void);
Возвращает в вызвавшей нити разрешение таймера в секундах.

Слайд 10

OpenMP

Параллельные и последовательные области:
#pragma omp parallel [опция[[,] опция]...]
Порождаются новые OMP_NUM_THREADS-1 нитей,

OpenMP Параллельные и последовательные области: #pragma omp parallel [опция[[,] опция]...] Порождаются новые
каждая нить получает свой уникальный номер, причём порождающая нить получает номер 0 и становится основной нитью группы («мастером»). При выходе из параллельной области производится неявная синхронизация и уничтожаются все нити, кроме породившей.

Слайд 11

OpenMP

if(условие) – выполнение параллельной области по условию;
num_threads (целочисленное выражение) – явное задание

OpenMP if(условие) – выполнение параллельной области по условию; num_threads (целочисленное выражение) –
количества нитей, которые будут выполнять параллельную область;
default(shared|none) – всем переменным в параллельной области, которым явно не назначен класс, будет назначен класс shared; none – всем переменным класс назначается явно; private(список) – переменные, для которых порождается локальная копия в каждой нити; начальное значение не определено;

Слайд 12

OpenMP

firstprivate(список) – переменные, для которых порождается локальная копия в каждой нити; локальные

OpenMP firstprivate(список) – переменные, для которых порождается локальная копия в каждой нити;
копии инициализируются значениями этих переменных в нити-мастере;
shared(список) – переменные, общие для всех нитей;
copyin(список) – переменные, объявленные как threadprivate, которые при входе в параллельную область инициализируются значениями соответствующих переменных в нити-мастере;

Слайд 13

OpenMP

reduction(оператор:список) – задаёт оператор и список общих переменных; для каждой переменной создаются

OpenMP reduction(оператор:список) – задаёт оператор и список общих переменных; для каждой переменной
локальные копии в каждой нити; они инициализируются соответственно типу оператора (для аддитивных – 0 или аналоги, для мультипликативных – 1 или аналоги); после выполнения всех операторов параллельной области выполняется заданный оператор; оператор это: для языка Си – +, *, -, &, |, ^, &&, ||; порядок выполнения операторов не определён, поэтому результат может отличаться от запуска к запуску.

Слайд 14

OpenMP

#include
int main(int argc, char *argv[])
{
printf("Последовательная область 1\n");
#pragma omp parallel
{

OpenMP #include int main(int argc, char *argv[]) { printf("Последовательная область 1\n"); #pragma
printf("Параллельная область\n");
}
printf("Последовательная область 2\n");
}

Слайд 15

OpenMP

#include
int main(int argc, char *argv[])
{
int count = 0;
#pragma omp parallel

OpenMP #include int main(int argc, char *argv[]) { int count = 0;
reduction (+: count)
{
count++;
printf("Текущее значение count: %d\n", count);
}
printf("Число нитей: %d\n", count);
}

Слайд 16

OpenMP

Если внутри параллельной области содержится только один параллельный цикл, одна конструкция sections

OpenMP Если внутри параллельной области содержится только один параллельный цикл, одна конструкция
или одна конструкция workshare, то можно использовать укороченную запись: parallel for, parallel sections или parallel workshare.

Слайд 17

OpenMP

void omp_set_num_threads(int num);
#include
#include
int main(int argc, char *argv[])
{
omp_set_num_threads(2);
#pragma omp parallel

OpenMP void omp_set_num_threads(int num); #include #include int main(int argc, char *argv[]) {
num_threads(3)
{
printf("Параллельная область 1\n");
}
#pragma omp parallel
{
printf("Параллельная область 2\n");
}
}

Слайд 18

OpenMP

Функция omp_get_max_threads() возвращает максимально допустимое число нитей для использования в следующей параллельной

OpenMP Функция omp_get_max_threads() возвращает максимально допустимое число нитей для использования в следующей
области.
int omp_get_max_threads(void);
Функция omp_get_num_procs() возвращает количество процессоров, доступных для использования программе пользователя на момент вызова. Нужно учитывать, что количество доступных процессоров может динамически изменяться.
int omp_get_num_procs(void);

Слайд 19

OpenMP

Параллельные области могут быть вложенными; по умолчанию вложенная параллельная область выполняется одной

OpenMP Параллельные области могут быть вложенными; по умолчанию вложенная параллельная область выполняется
нитью. Это управляется установкой переменной среды OMP_NESTED.
export OMP_NESTED=true
Изменить значение переменной OMP_NESTED можно с помощью вызова функции omp_set_nested().
void omp_set_nested(int nested);
Узнать значение переменной OMP_NESTED можно при помощи функции omp_get_nested().
int omp_get_nested(void);

Слайд 20

OpenMP

Функция omp_in_parallel() возвращает 1, если она была вызвана из активной параллельной области

OpenMP Функция omp_in_parallel() возвращает 1, если она была вызвана из активной параллельной
программы.
int omp_in_parallel(void);
void mode(void){
if(omp_in_parallel()) printf("Параллельная область\n");
else printf("Последовательная область\n");
}
int main(int argc, char *argv[]){
mode();
#pragma omp parallel
mode();
}

Слайд 21

OpenMP

Если в параллельной области какой-либо участок кода должен быть выполнен лишь один

OpenMP Если в параллельной области какой-либо участок кода должен быть выполнен лишь
раз, то его нужно выделить директивами single:
#pragma omp single [опция [[,] опция]...]
private(список);
firstprivate(список);
copyprivate(список) – новые значения переменных списка будут доступны всем одноименным частным переменным (private и firstprivate); опция не может использоваться совместно с опцией nowait; переменные списка не должны быть перечислены в опциях private и firstprivate данной директивы single;

Слайд 22

OpenMP

nowait – после выполнения участка происходит неявная барьерная синхронизация нитей: их дальнейшее

OpenMP nowait – после выполнения участка происходит неявная барьерная синхронизация нитей: их
выполнение происходит только тогда, когда все достигнут данной точки; если этом нет необходимости, опция nowait позволяет нитям, дошедшим до конца участка, продолжить выполнение без синхронизации.
#pragma omp parallel
{
printf("Сообщение 1\n");
#pragma omp single nowait
{
printf("Одна нить\n");
}
printf("Сообщение 2\n");
}

Слайд 23

OpenMP

Директива master выделяет участок кода, который будет выполнен только нитью-мастером. Остальные нити

OpenMP Директива master выделяет участок кода, который будет выполнен только нитью-мастером. Остальные
пропускают данный участок и продолжают работу с оператора, расположенного следом. Неявной синхронизации не предполагает.
#pragma omp master
#pragma omp parallel private(n)
{
n=1;
#pragma omp master
n=2;
printf("значение n: %d\n", n);
}

Слайд 24

OpenMP

Все нити в параллельной области нумеруются последовательными целыми числами от 0 до

OpenMP Все нити в параллельной области нумеруются последовательными целыми числами от 0
N-1, где N — количество нитей, выполняющих данную область.
Вызов функции omp_get_thread_num() позволяет нити получить свой уникальный номер в текущей параллельной области.
int omp_get_thread_num(void);
Вызов функции omp_get_num_threads() позволяет нити получить количество нитей в текущей параллельной области.
int omp_get_num_threads(void);

Слайд 25

OpenMP

#include
#include
int main(int argc, char *argv[])
{
int n=1;
printf("в посл. области

OpenMP #include #include int main(int argc, char *argv[]) { int n=1; printf("в
(1): %d\n", n);
#pragma omp parallel private(n)
{
printf("Значение n на нити (1): %d\n", n);
/* Присвоим переменной n номер текущей нити */
n=omp_get_thread_num();
printf("Значение n на нити (2): %d\n", n);
}
printf("в посл. области (2): %d\n", n);
}

Слайд 26

OpenMP

#include
#include
int main(int argc, char *argv[])
{
int n=1;
printf("в посл. области

OpenMP #include #include int main(int argc, char *argv[]) { int n=1; printf("в
(1): %d\n", n);
#pragma omp parallel firstprivate(n)
{
printf("Значение n на нити (1): %d\n", n);
/* Присвоим переменной n номер текущей нити */
n=omp_get_thread_num();
printf("Значение n на нити (2): %d\n", n);
}
printf("в посл. области (2): %d\n", n);
}

Слайд 27

OpenMP

Если в параллельной области встретился оператор цикла, то, согласно общему правилу, он

OpenMP Если в параллельной области встретился оператор цикла, то, согласно общему правилу,
будет выполнен всеми нитями текущей группы, то есть каждая нить выполнит все итерации данного цикла. Для распределения итераций цикла между различными нитями можно использовать директиву for.
#pragma omp for [опция [[,] опция]...]
Эта директива относится к идущему следом за данной директивой блоку, включающему операторы for.

Слайд 28

OpenMP

private(список);
firstprivate(список);
lastprivate(список) – переменным, перечисленным в списке, присваивается результат с последнего витка цикла;
reduction(оператор:список);
schedule(type[,

OpenMP private(список); firstprivate(список); lastprivate(список) – переменным, перечисленным в списке, присваивается результат с
chunk]) – опция задаёт, каким образом итерации цикла распределяются между нитями;

Слайд 29

OpenMP

collapse(n) — опция указывает, что n последовательных тесновложенных циклов ассоциируется с данной

OpenMP collapse(n) — опция указывает, что n последовательных тесновложенных циклов ассоциируется с
директивой; для циклов образуется общее пространство итераций, которое делится между нитями; если опция collapse не задана, то директива относится только к одному непосредственно следующему за ней циклу;
ordered – опция, говорящая о том, что в цикле могут встречаться директивы ordered; в этом случае определяется блок внутри тела цикла, который должен выполняться в том порядке, в котором итерации идут в последовательном цикле;
nowait.

Слайд 30

OpenMP

На вид параллельных циклов накладываются достаточно жёсткие ограничения. В частности, предполагается, что

OpenMP На вид параллельных циклов накладываются достаточно жёсткие ограничения. В частности, предполагается,
корректная программа не должна зависеть от того, какая именно нить какую итерацию параллельного цикла выполнит. Нельзя использовать побочный выход из параллельного цикла. Размер блока итераций, указанный в опции schedule, не должен изменяться в рамках цикла.
Формат параллельных циклов упрощённо можно представить следующим образом:
for([целочисленный тип] i = инвариант цикла; i {<,>,=,<=,>=} инвариант цикла; i {+,-}= инвариант цикла)

Слайд 31

OpenMP

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

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

Слайд 32

OpenMP

#include
#include
int main(int argc, char *argv[])
{
int A[10], B[10], C[10], i,

OpenMP #include #include int main(int argc, char *argv[]) { int A[10], B[10],
n;
for (i=0; i<10; i++){A[i]=i; B[i]=2*i; C[i]=0;}
#pragma omp parallel shared(A,B,C) private(i,n)
{
n=omp_get_thread_num();
#pragma omp for
for (i=0; i<10; i++){
C[i]=A[i]+B[i];
printf("Нить %d сложила элементы %d\n",n,i);
}
}
}

Слайд 33

OpenMP

В опции schedule параметр type задаёт следующий тип распределения итераций:
static – блочно-циклическое

OpenMP В опции schedule параметр type задаёт следующий тип распределения итераций: static
распределение итераций цикла; размер блока – chunk. Первый блок из chunk итераций выполняет нулевая нить, второй блок — следующая и т.д. до последней нити, затем распределение снова начинается с нулевой нити. Если значение chunk не указано, то всё множество итераций делится на непрерывные куски примерно одинакового размера (конкретный способ зависит от реализации), и полученные порции итераций распределяются между нитями.

Слайд 34

OpenMP

dynamic – динамическое распределение итераций с фиксированным размером блока: сначала каждая нить

OpenMP dynamic – динамическое распределение итераций с фиксированным размером блока: сначала каждая
получает chunk итераций (по умолчанию chunk=1), та нить, которая заканчивает выполнение своей порции итераций, получает первую свободную порцию из chunk итераций. Освободившиеся нити получают новые порции итераций до тех пор, пока все порции не будут исчерпаны. Последняя порция может содержать меньше итераций, чем все остальные.

Слайд 35

OpenMP

guided – динамическое распределение итераций, при котором размер порции уменьшается с некоторого

OpenMP guided – динамическое распределение итераций, при котором размер порции уменьшается с
начального значения до величины chunk (по умолчанию chunk=1) пропорционально количеству ещё не распределённых итераций, делённому на количество нитей, выполняющих цикл. Размер первоначально выделяемого блока зависит от реализации. В ряде случаев такое распределение позволяет аккуратнее разделить работу и сбалансировать загрузку нитей. Количество итераций в последней порции может оказаться меньше значения chunk.

Слайд 36

OpenMP

auto – способ распределения итераций выбирается компилятором и/или системой выполнения. Параметр chunk

OpenMP auto – способ распределения итераций выбирается компилятором и/или системой выполнения. Параметр
при этом не задаётся.
runtime – способ распределения итераций выбирается во время работы программы по значению переменной среды OMP_SCHEDULE. Параметр chunk при этом не задаётся.

Слайд 37

OpenMP

#include
#include
int main(int argc, char *argv[])
{
int i;
#pragma omp parallel private(i)

OpenMP #include #include int main(int argc, char *argv[]) { int i; #pragma
{
#pragma omp for schedule (static, 2)
for (i=0; i<10; i++){
printf("Нить %d выполнила итерацию %d\n",
omp_get_thread_num(), i);
sleep(1);
}
}
}

Слайд 38

OpenMP

Значение по умолчанию переменной OMP_SCHEDULE зависит от реализации. Если переменная задана неправильно,

OpenMP Значение по умолчанию переменной OMP_SCHEDULE зависит от реализации. Если переменная задана
то поведение программы при задании опции runtime также зависит от реализации.
Задать значение переменной OMP_SCHEDULE в Linux в командной оболочке bash можно при помощи команды следующего вида:
export OMP_SCHEDULE="dynamic,1"

Слайд 39

OpenMP

Изменить значение переменной OMP_SCHEDULE из программы можно с помощью вызова функции omp_set_schedule().
void

OpenMP Изменить значение переменной OMP_SCHEDULE из программы можно с помощью вызова функции
omp_set_schedule(omp_sched_t type, int chunk);
Допустимые значения констант описаны в файле omp.h. Как минимум, следующие варианты:
typedef enum omp_sched_t {
omp_sched_static = 1,
omp_sched_dynamic = 2,
omp_sched_guided = 3,
omp_sched_auto = 4
} omp_sched_t;

Слайд 40

OpenMP

При помощи вызова функции omp_get_schedule() пользователь может узнать текущее значение переменной OMP_SCHEDULE.

OpenMP При помощи вызова функции omp_get_schedule() пользователь может узнать текущее значение переменной

void omp_get_schedule(omp_sched_t* type, int* chunk);
При распараллеливании цикла нужно убедиться в том, что итерации данного цикла не имеют информационных зависимостей. В этом случае его итерации можно выполнять в любом порядке, в том числе параллельно. Компилятор это не проверяет. Если дать указание компилятору распараллелить цикл, содержащий зависимости, результат работы может оказаться некорректным.

Слайд 41

OpenMP

Директива sections определяет набор независимых секций кода, каждая из которых выполняется своей

OpenMP Директива sections определяет набор независимых секций кода, каждая из которых выполняется
нитью.
#pragma omp sections [опция [[,] опция]...]
private(список);
firstprivate(список);
lastprivate(список) – переменным присваивается результат из последней секции;
reduction(оператор:список);
nowait.

Слайд 42

OpenMP

Директива section задаёт участок кода внутри секции sections для выполнения одной нитью.
#pragma

OpenMP Директива section задаёт участок кода внутри секции sections для выполнения одной
omp section
Перед первым участком кода в блоке sections директива section не обязательна. Какие нити будут задействованы для выполнения какой секции, не специфицируется. Если количество нитей больше количества секций, то часть нитей для выполнения данного блока секций не будет задействована. Если количество нитей меньше количества секций, то некоторым (или всем) нитям достанется более одной секции.

Слайд 43

OpenMP

int n;
#pragma omp parallel private(n)
{
n=omp_get_thread_num();
#pragma omp sections
{
#pragma omp

OpenMP int n; #pragma omp parallel private(n) { n=omp_get_thread_num(); #pragma omp sections
section
printf("Первая секция, процесс %d\n", n);
#pragma omp section
printf("Вторая секция, процесс %d\n", n);
}
printf("Параллельная область, процесс %d\n", n);
}

Слайд 44

OpenMP

int n=0;
#pragma omp parallel
{
#pragma omp sections lastprivate(n)
{
#pragma omp section

OpenMP int n=0; #pragma omp parallel { #pragma omp sections lastprivate(n) {
n=1;
#pragma omp section
n=2;
}
printf("Значение n на нити %d: %d\n",
omp_get_thread_num(), n);
}
printf("Значение n в конце: %d\n", n);

Слайд 45

OpenMP

Директива task применяется для выделения отдельной независимой задачи.
#pragma omp task [опция [[,]

OpenMP Директива task применяется для выделения отдельной независимой задачи. #pragma omp task
опция]...]
Текущая нить выделяет в качестве задачи ассоциированный с директивой блок операторов. Задача может выполняться немедленно после создания или быть отложенной на неопределённое время и выполняться по частям. Размер таких частей, а также порядок выполнения частей разных отложенных задач определяется реализацией.

Слайд 46

OpenMP

if(условие) — порождение новой задачи только при выполнении некоторого условия; если условие

OpenMP if(условие) — порождение новой задачи только при выполнении некоторого условия; если
не выполняется, то задача будет выполнена текущей нитью и немедленно;
untied — опция означает, что в случае откладывания задача может быть продолжена любой нитью из числа выполняющих данную параллельную область; если данная опция не указана, то задача может быть продолжена только породившей её нитью;

Слайд 47

OpenMP

default(shared|none);
private(список);
firstprivate(список);
shared(список).

OpenMP default(shared|none); private(список); firstprivate(список); shared(список).

Слайд 48

OpenMP

Для гарантированного завершения в точке вызова всех запущенных задач используется директива taskwait.
#pragma

OpenMP Для гарантированного завершения в точке вызова всех запущенных задач используется директива
omp taskwait
Нить, выполнившая данную директиву, приостанавливается до тех пор, пока не будут завершены все ранее запущенные данной нитью независимые задачи.

Слайд 49

OpenMP

Самый распространенный способ синхронизации в OpenMP – барьер. Он оформляется с помощью

OpenMP Самый распространенный способ синхронизации в OpenMP – барьер. Он оформляется с
директивы barrier.
#pragma omp barrier
Нити, выполняющие текущую параллельную область, дойдя до этой директивы, останавливаются и ждут, пока все нити не дойдут до этой точки программы, после чего разблокируются и продолжают работать дальше. Кроме того, для разблокировки необходимо, чтобы все синхронизируемые нити завершили все порождённые ими задачи (task).

Слайд 50

OpenMP

#include
#include
int main(int argc, char *argv[])
{
#pragma omp parallel
{
printf("Сообщение 1\n");

OpenMP #include #include int main(int argc, char *argv[]) { #pragma omp parallel
printf("Сообщение 2\n");
#pragma omp barrier
printf("Сообщение 3\n");
}
}

Слайд 51

OpenMP

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

OpenMP Директива ordered определяет блок внутри тела цикла, который должен выполняться в
порядке, в котором итерации идут в последовательном цикле.
#pragma omp ordered
Относится к самому внутреннему из объемлющих циклов, а в параллельном цикле должна быть задана опция ordered. Нить, выполняющая первую итерацию цикла, выполняет операции данного блока. Нить, выполняющая любую следующую итерацию, должна сначала дождаться выполнения всех операций блока всеми нитями, выполняющими предыдущие итерации. Может использоваться, например, для упорядочения вывода от параллельных нитей.

Слайд 52

OpenMP

#pragma omp parallel private (i, n)
{
n=omp_get_thread_num();
#pragma omp for ordered
for

OpenMP #pragma omp parallel private (i, n) { n=omp_get_thread_num(); #pragma omp for
(i=0; i<5; i++)
{
printf("Нить %d, итерация %d\n", n, i);
#pragma omp ordered
{
printf("ordered: Нить %d, итерация %d\n", n, i);
}
}
}
}

Слайд 53

OpenMP

С помощью директивы critical оформляется критическая секция программы.
#pragma omp critical [(<имя_критической_секции>)]
В каждый

OpenMP С помощью директивы critical оформляется критическая секция программы. #pragma omp critical
момент времени в критической секции может находиться не более одной нити. Все другие нити, выполнившие директиву для секции с данным именем, будут заблокированы, пока вошедшая нить не закончит выполнение. Как только работавшая нить выйдет из критической секции, одна из заблокированных нитей войдет в неё. Если на входе стояло несколько нитей, то случайным образом выбирается одна из них, а остальные продолжают ожидание.

Слайд 54

OpenMP

Все неименованные критические секции условно ассоциируются с одним и тем же именем.

OpenMP Все неименованные критические секции условно ассоциируются с одним и тем же
Имеющие одно и тоже имя рассматриваются единой секцией, даже если находятся в разных параллельных областях. Побочные входы и выходы из критической секции запрещены.
int n;
#pragma omp parallel
{
#pragma omp critical
{
n=omp_get_thread_num();
printf("Нить %d\n", n);
}
}
}

Слайд 55

OpenMP

#pragma omp atomic
Данная директива относится к идущему непосредственно за ней оператору присваивания

OpenMP #pragma omp atomic Данная директива относится к идущему непосредственно за ней
(на используемые в котором конструкции накладываются достаточно понятные ограничения), гарантируя корректную работу с общей переменной, стоящей в его левой части. На время выполнения оператора блокируется доступ к данной переменной всем запущенным в данный момент нитям, кроме нити, выполняющей операцию. Атомарной является только работа с переменной в левой части оператора присваивания, при этом вычисления в правой части не обязаны быть атомарными.

Слайд 56

OpenMP

#include
#include
int main(int argc, char *argv[])
{
int count = 0;
#pragma omp

OpenMP #include #include int main(int argc, char *argv[]) { int count =
parallel
{
#pragma omp atomic
count++;
}
printf("Число нитей: %d\n", count);
}

Слайд 57

OpenMP

Один из вариантов синхронизации в OpenMP реализуется через механизм замков (locks). В

OpenMP Один из вариантов синхронизации в OpenMP реализуется через механизм замков (locks).
качестве замков используются общие переменные. Данные переменные должны использоваться только как параметры примитивов синхронизации.
Замок может находиться в одном из трёх состояний: неинициализированный, разблокированный или заблокированный. Разблокированный замок может быть захвачен некоторой нитью. При этом он переходит в заблокированное состояние. Нить, захватившая замок, и только она может его освободить, после чего замок возвращается в разблокированное состояние.

Слайд 58

OpenMP

Есть два типа замков: простые замки и множественные замки. Множественный замок может

OpenMP Есть два типа замков: простые замки и множественные замки. Множественный замок
многократно захватываться одной нитью перед его освобождением, в то время как простой замок может быть захвачен только однажды. Для множественного замка вводится понятие коэффициента захваченности (nesting count). Изначально он устанавливается в ноль, при каждом следующем захватывании увеличивается на единицу, а при каждом освобождении уменьшается на единицу. Множественный замок считается разблокированным, если его коэффициент захваченности равен нулю.

Слайд 59

OpenMP

Для инициализации простого или множественного замка используются соответственно функции omp_init_lock() и omp_init_nest_lock().
void

OpenMP Для инициализации простого или множественного замка используются соответственно функции omp_init_lock() и
omp_init_lock(omp_lock_t *lock);
void omp_init_nest_lock(omp_nest_lock_t *lock);
После выполнения функции замок переводится в разблокированное состояние. Для множественного замка коэффициент захваченности устанавливается в ноль.

Слайд 60

OpenMP

Функции omp_destroy_lock() и omp_destroy_nest_lock() используются для переведения простого или множественного замка в

OpenMP Функции omp_destroy_lock() и omp_destroy_nest_lock() используются для переведения простого или множественного замка
неинициализированное состояние.
void omp_destroy_lock(omp_lock_t *lock);
void omp_destroy_nest_lock (omp_nest_lock_t *lock);

Слайд 61

OpenMP

Для захватывания замка используются функции omp_set_lock() и omp_set_nest_lock().
void omp_set_lock(omp_lock_t *lock);
void omp_set_nest_lock (omp_nest_lock_t

OpenMP Для захватывания замка используются функции omp_set_lock() и omp_set_nest_lock(). void omp_set_lock(omp_lock_t *lock);
*lock);
Вызвавшая эту функцию нить дожидается освобождения замка, а затем захватывает его. Замок при этом переводится в заблокированное состояние. Если множественный замок уже захвачен данной нитью, то нить не блокируется, а коэффициент захваченности увеличивается на единицу.

Слайд 62

OpenMP

Для освобождения замка используются функции omp_unset_lock() и omp_unset_nest_lock().
void omp_unset_lock(omp_lock_t *lock);
void omp_unset_nest_lock(omp_lock_t *lock);
Вызов

OpenMP Для освобождения замка используются функции omp_unset_lock() и omp_unset_nest_lock(). void omp_unset_lock(omp_lock_t *lock);
этой функции освобождает простой замок, если он был захвачен вызвавшей нитью. Для множественного замка уменьшает на единицу коэффициент захваченности. Если коэффициент станет равен нулю, замок освобождается. Если после освобождения есть нити, заблокированные на операции, захватывающей данный замок, он будет сразу захвачен одной из ожидающих нитей.

Слайд 63

OpenMP

omp_lock_t lock;
int n;
omp_init_lock(&lock);
#pragma omp parallel private (n)
{
n=omp_get_thread_num();

OpenMP omp_lock_t lock; int n; omp_init_lock(&lock); #pragma omp parallel private (n) {
omp_set_lock(&lock);
printf("Начало закрытой секции, %d\n", n);
sleep(5);
printf("Конец закрытой секции, %d\n", n);
omp_unset_lock(&lock);
}
omp_destroy_lock(&lock);

Слайд 64

OpenMP

Для неблокирующей попытки захвата замка используются функции omp_test_lock() и omp_test_nest_lock().
int omp_test_lock(omp_lock_t *lock);
int

OpenMP Для неблокирующей попытки захвата замка используются функции omp_test_lock() и omp_test_nest_lock(). int
omp_test_nest_lock(omp_lock_t *lock);
Данная функция пробует захватить указанный замок. Если это удалось, то для простого замка функция возвращает 1, а для множественного замка – новый коэффициент захваченности. Если замок захватить не удалось, в обоих случаях возвращается 0.

Слайд 65

OpenMP

omp_lock_t lock;
int n;
omp_init_lock(&lock);
#pragma omp parallel private (n)
{
n=omp_get_thread_num();

OpenMP omp_lock_t lock; int n; omp_init_lock(&lock); #pragma omp parallel private (n) {
while (!omp_test_lock (&lock)){
printf("Секция закрыта, %d\n", n);
sleep(2);
}
printf("Начало закрытой секции, %d\n", n);
sleep(5);
printf("Конец закрытой секции, %d\n", n);
omp_unset_lock(&lock);
}
omp_destroy_lock(&lock);

Слайд 66

OpenMP

#pragma omp flush [(список)]
Выполнение данной директивы предполагает, что значения всех переменных (или

OpenMP #pragma omp flush [(список)] Выполнение данной директивы предполагает, что значения всех
переменных из списка, временно хранящиеся в регистрах и кэш-памяти текущей нити, будут занесены в основную память; все изменения переменных, сделанные нитью во время работы, станут видимы остальным нитям; если какая-то информация хранится в буферах вывода, то буферы будут сброшены и т.п. Операция производится только с данными вызвавшей нити, данные, изменявшиеся другими нитями, не затрагиваются. Выполнение данной директивы в полном объёме может повлечь значительные накладные расходы.

Слайд 67

OpenMP

Переменная среды OMP_STACKSIZE задаёт размер стека для создаваемых из программы нитей. Значение

OpenMP Переменная среды OMP_STACKSIZE задаёт размер стека для создаваемых из программы нитей.
переменной может задаваться в виде size | sizeB | sizeK | sizeM | sizeG, где size – положительное целое число, а буквы B, K, M, G задают соответственно, байты, килобайты, мегабайты и гигабайты. Если ни одной из этих букв не указано, размер задаётся в килобайтах. Если задан неправильный формат или невозможно выделить запрошенный размер стека, результат будет зависеть от реализации.
export OMP_STACKSIZE=2000K

Слайд 68

OpenMP

Переменная среды OMP_WAIT_POLICY задаёт поведение ждущих процессов. Если задано значение ACTIVE, то

OpenMP Переменная среды OMP_WAIT_POLICY задаёт поведение ждущих процессов. Если задано значение ACTIVE,
ждущему процессу будут выделяться циклы процессорного времени, а при значении PASSIVE ждущий процесс может быть отправлен в спящий режим, при этом процессор может быть назначен другим процессам.

Слайд 69

OpenMP

Переменная среды OMP_THREAD_LIMIT задаёт максимальное число нитей, допустимых в программе. Если значение

OpenMP Переменная среды OMP_THREAD_LIMIT задаёт максимальное число нитей, допустимых в программе. Если
переменной не является положительным целым числом или превышает максимально допустимое в системе число процессов, поведение программы будет зависеть от реализации. Значение переменной может быть получено при помощи функции omp_get_thread_limit().
int omp_get_thread_limit(void);

Слайд 70

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

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

OpenMP

Имя файла: Технология-программирования-OpenMP.pptx
Количество просмотров: 131
Количество скачиваний: 0