Вложенные параллельные области Если переменная среды OMP_NESTED имеет значение true, то любая нить параллельной области может породить новую параллельную.

Презентация:



Advertisements
Похожие презентации
OpenMP. Различие между тредами и процессами ПроцессыТреды.
Advertisements

Параллельное программирование с использованием технологии OpenMP Аксёнов Сергей Владимирович к.т.н., доцент каф.ОСУ ТПУ Лекция 2 Томский политехнический.
OpenMPOpenMPРазличие между тредами и процессами ПроцессыТреды.
Многопоточное программирование в OpenMP Киреев Сергей ИВМиМГ.
Параллельное программирование с использованием технологии OpenMP Аксёнов Сергей Владимирович к.т.н., доцент каф.ОСУ ТПУ Лекция 3 Томский политехнический.
Технология программирования OpenMP Антонов Александр Сергеевич, к.ф.-м.н., с.н.с. лаборатории Параллельных информационных технологий НИВЦ МГУ.
Параллельное программирование с использованием технологии OpenMP Аксёнов Сергей Владимирович к.т.н., доцент каф.ОСУ ТПУ Томский политехнический университет.
Основы OpenMP Nikita Panov
Интернет Университет Суперкомпьютерных технологий Основные понятия Учебный курс Параллельное программирование с OpenMP Бахтин В.А., кандидат физ.-мат.
Гергель В.П. Общий курс Теория и практика параллельных вычислений Лекция 15 Методы разработки параллельных программ для многопроцессорных систем с общей.
Гергель В.П. Общий курс Теория и практика параллельных вычислений Лекция 16 Методы разработки параллельных программ для многопроцессорных систем с общей.
Интернет Университет Суперкомпьютерных технологий Система поддержки выполнения OpenMP- программ. Переменные окружения, управляющие выполнением OpenMP-
Разработка параллельных приложений для многоядерных систем С.В. Ковальчук НИИ Наукоемких компьютерных технологий, СПбГУ ИТМО.
Нижегородский государственный университет им. Н.И.Лобачевского Факультет Вычислительной математики и кибернетики Введение в OpenMP Гергель В.П., Сысоев.
Практическое занятие 6. Функции. Большинство языков программирования используют понятия функции и процедуры. C++ формально не поддерживает понятие процедуры,
Е.Ю. Алексеева Механико-математический факультет Южно-Уральского государственного университета.
Интернет Университет Суперкомпьютерных технологий Основные понятия Учебный курс Параллельное программирование с OpenMP Бахтин В.А., кандидат физ.-мат.
Технология программирования OpenMP Антонов Александр Сергеевич, к.ф.-м.н., с.н.с. лаборатории Параллельных информационных технологий НИВЦ МГУ 1.
МГУ им. М.В. Ломоносова, Москва, 21 октября 2011г. КОНСОРЦИУМ УНИВЕРСИТЕТОВ РОССИИ Курс: «Технология параллельного программирования OpenMP» Лабораторная.
POSIX Threads & OpenMP Общая память Сергей Петрович Нечаев, Сибирский Суперкомпьютерный центр.
Транксрипт:

Вложенные параллельные области Если переменная среды OMP_NESTED имеет значение true, то любая нить параллельной области может породить новую параллельную область и стать ее мастером:

Полезные функции: Узнать, выполняется ли нить в параллельной области, можно с помощью функции int omp_in_parallel(void), которая возвращает 0, если функция вызвана из последовательной области, и 1 – если из параллельной. Каждая нить может получить свой собственный номер с помощью функции int omp_get_thread_num(void). Функция int omp_get_max_threads(void) возвращает максимально допустимое число нитей для использования в следующей параллельной области. Функция int omp_get_num_procs(void) возвращает количество процессоров, доступных для использования программе пользователя на момент вызова. Количество доступных процессоров может динамически изменяться в процессе функционирования SMP – системы.

Директива parallel Синтаксис: #pragma omp parallel [option [[,]...] Пример директивы (в реальном тексте – одна строка): #pragma omp parallel shared(arrayA) private(i, n) shared(arrayB, arrayC) reduction(+ : summa) { …//блок операторов, выполняющихся параллельно }

Список опций директивы parallel if (scalar_expression) private (list) firstprivate (list) shared (list) default (shared | none) reduction (operator: list) copyin (list) num_threads (integer_expression)

Опции директивы parallel if(scalar_expression) Условное выполнение параллельной области. Создание нитей параллельной области осуществляется только при выполнении указанного условия. shared(список переменных) Явно задаёт список переменных, общих для всех нитей. private(список переменных) По умолчанию переменные, видимые в области, объемлющей блок параллельного исполнения, являются общими (shared). Переменные, объявленные внутри блока, по умолчанию считаются закрытыми (private). Опция private явно задает список закрытых переменных. Только shared- переменные в объемлющем параллельном блоке могут быть аргументами опции private. Эти переменные «размножаются» по нитям в момент создания нитей.

Опции директивы parallel firstprivate( список переменных ) Опция firstprivate обладает той же семантикой, что и опция private. Дополнительно, все копии переменных в каждой нити инициализируются текущим значением исходной переменной в момент входа в блок. default( private | firstprivate | shared | none ) Всем переменным в задаче, которым явно не назначен класс, будет назначен класс private, firstprivate или shared соответственно. none означает, что всем переменным в задаче класс должен быть назначен явно. В языке Си задаются только варианты shared или none; copyin( список переменных ) Задаёт список переменных, объявленных как threadprivate, которые при входе в параллельную область инициализируются значениями соответствующих переменных, находящихся в нити-мастере;

Опции директивы parallel reduction( оператор : список переменных ) задаёт оператор и список общих переменных; для каждой переменной указанного списка создаются локальные (закрытые) копии в каждой нити; локальные копии инициализируются соответственно типу оператора (для аддитивных операций – 0 или его аналоги, для мультипликативных операций – 1 или её аналоги); после выполнения всех операторов параллельной области над локальными копиями переменных выполняется заданный оператор: для языка Си+, *, -, &, |, ^, &&, || для языка Фортран+, *, -,.and.,.or.,.eqv.,.neqv., max, min, iand, ior, ieor; порядок выполнения операторов (над нитями) не определён, поэтому результат может отличаться от запуска к запуску. num_threads (целочисленное выражение) Явное задание количества нитей, которые будут выполнять параллельную область; по умолчанию выбирается последнее значение, установленное с помощью функции void omp_set_num_threads(int), или значение переменной среды OMP_NUM_THREADS;

Директива single Синтаксис: #pragma omp single [опция [[,] опция]...] Пример директивы (в реальном тексте – одна строка): #pragma omp single private(i, n) nowait { …//блок операторов, выполняющихся одной нитью }

Опции директивы single private( список переменных ) firstprivate( список переменных ) copyprivate( список переменных ) nowait

Директива master Синтаксис: #pragma omp master Пример директивы: #pragma omp master { … //блок операторов, выполняющихся главной данной //параллельной области – нитью-мастером. Все остальные нити просто // пропускают данный участок и продолжают работу с оператора, //расположенного следом за структурным блоком, помеченным этой //директивой. Неявной синхронизации с нитью-мастером не производится. }

Простейший пример OpenMP-программы с директивами parallel, master, single int main(){ #pragma omp parallel { … #pragma omp master printf("Master. Threads: %d, thread: %d\n", omp_get_num_threads(), omp_get_thread_num()); … printf((Parallel. Threads: %d, thread: %d\n") omp_get_num_threads(), omp_get_thread_num()); #pragma omp single printf("Single. Threads: %d, thread: %d\n", omp_get_num_threads(),omp_get_thread_num()); } … return 0; } Parallel. Threads: 4, thread: 1 Master. Threads: 4, thread: 0 Parallel. Threads: 4, thread: 3 Parallel. Threads: 4, thread: 2 Single. Threads: 4, thread: 0 Parallel. Threads: 4, thread: 0 Результат работы:

Директива threadprivate Синтаксис: #pragma omp threadprivate( список переменных ) Пример директивы: #pragma omp threadprivate( m, alpha, vectorA) Директива threadprivate указывает компилятору, что переменные из указанного в ней списка должны быть размножены по нитям для того, чтобы каждая нить имела свою локальную копию. Это может быть нужно для того, чтобы сделать локальные копии статических переменных языка Си, которые по умолчанию являются общими для всех нитей всех параллельных областей. Создаваемые локальные копии не видимы в последовательных участках выполнения программы (т.е. вне параллельных фрагментов), но существуют в течение всего времени выполнения программы. Указываемые в списке переменные должны быть уже определены в программе; объявление переменных в директиве должно предшествовать использованию переменных в потоках.

Директива threadprivate Действие опции private других директив распространяется только на программный код параллельных фрагментов, но не параллельных областей – т.е., например, любая локальная переменная, определенная в параллельном фрагменте, будет невидима в функции, вызываемой из этого фрагмента. Если в функции нужно иметь доступ к этим переменным, то их надо передавать через параметры функций. Другой возможный вариант - использовать директиву threadprivate. Сформированные в нитях значения таких переменных сохраняются и пока выполняются последовательные фрагменты между параллельными областями программы. Их значения можно переустановить в начале параллельного фрагмента по значениям из основного потока при помощи параметра copyin директивы parallel. Если на локальные копии ссылаются в разных параллельных областях, то для сохранения их значений необходимо, чтобы не было объемлющих параллельных областей, количество нитей в обеих областях совпадало, а переменная OMP_DYNAMIC была установлена в false с начала первой области до начала второй. Для корректного использования локальных копий глобальных объектов нужно гарантировать, что они используются в разных частях программы одними и теми же нитями. Переменные, объявленные как threadprivate, не могут использоваться в опциях других директив, кроме опций copyin, copyprivate, schedule, num_threads, if.

Использование директивы threadprivate #include int k; #pragma omp threadprivate(k) int testing(){ omp_set_num_threads( 4 ); #pragma omp parallel { k=omp_get_num_threads()-omp_get_thread_num(); #pragma omp master printf("Master. Thread: %d\n",omp_get_thread_num()); } #pragma omp parallel { printf("Parallel. Thread: %d, k:%d\n", omp_get_thread_num(), k); #pragma omp single printf("Single. Thread: %d\n", omp_get_thread_num()); } return 0; } Master. Thread: 0 Parallel. Thread: 1, k:3 Parallel. Thread: 3, k:1 Parallel. Thread: 2, k:2 Parallel. Thread: 0, k:4 Single. Thread: 3 Результат работы:

Директивы распределения работы между нитями OpenMP предлагает программисту несколько различных вариантов распределения работы между параллельно исполняемыми нитями: Низкоуровневое распараллеливание Параллельные циклы Параллельные секции Задачи (tasks) – только начиная с версии 3.0

Низкоуровневое распараллеливание int threadsCount; int threadNum; #pragma omp parallel private(threadNum) shared(threadsCount) { threadsCount = omp_get_num_threads();//получить количество нитей threadNum = omp_get_thread_num();//получить номер данной нити switch (threadNum) { case 0 : //если это мастер-нить …//блок операторов для выполнения в мастер-нити break; case 1 : //если это нить 1 …//блок операторов для выполнения в нити 1 break; case 2 : //если это нить 2 …//блок операторов для выполнения в нити 2 break; … }

Параллельные циклы. Директива for Синтаксис: #pragma omp for [опция [[,] опция]...] Эта директива относится к идущему следом за данной директивой блоку, включающему оператор цикла for, который должен удовлетворять ряду ограничений. Главное из них состоит в том, что заголовок цикла должен выглядеть так: for([целочисленный тип] i = инвариант цикла; i {,=, =} инвариант цикла; i {+,-}= инвариант цикла) Если директива параллельного выполнения стоит перед гнездом циклов, завершающихся одним оператором, то директива воздействует только на самый внешний цикл.

Параллельные циклы Кроме того, должны выполняться следующие требования: 1.Корректная программа не должна зависеть от того, какая именно нить какую именно итерацию параллельного цикла выполнит. 2.Нельзя использовать побочный выход из параллельного цикла (операторы goto, break, continue). 3.Размер блока итераций, указанный в опции schedule директивы for (если эта опция указана), не должен изменяться в рамках цикла. 4.Итеративная переменная распределяемого цикла по смыслу должна быть локальной, поэтому в случае, если она специфицирована в качестве общей переменной, то она неявно делается локальной при входе в цикл. После завершения цикла значение итеративной переменной цикла не определено, если она не указана в опции lastprivate. Все эти ограничения введены для того, чтобы компилятор OpenMP мог обеспечить точное определение количества итераций в момент входа в цикл.

Примеры директивы for #define ARRAYSIZE double arrayX[ ARRAYSIZE ] ; double arrayY[ ARRAYSIZE ] ; … #pragma parallel for //директивы parallel и for могут быть записаны вместе for( i = 0; i < ARRAYSIZE; i +=1) { arrayX[ i ] = 0.0; arrayY[ i ] = 0.0; } double arrayX[ ARRAYSIZE ] = …; #pragma omp parallel for//неверно! Причин много for( i=0; i< ARRAYSIZE; i+=1) { for( j=i+1; j< ARRAYSIZE; j++) if( arrayX[j]

Результаты работы неверной программы пузырьковой сортировки на двухядерном ПК: double arrayX[] = {3, 82, 17, 33, 82, 91, 4, 65, 7, 88, 41, 6, 55, 32, 5, 8, 27, 73, 56, 39, 74, 15, 44, 15, 93, 44, 26, 84, 63, 3, 9, 42, 74}; При количестве нитей 2: 3, 3, 4, 5, 6, 7, 8, 9, 15, 15, 17, 26, 27, 32, 33, 39, 41, 42, 44, 44, 55, 56, 63, 65, 73, 74, 74, 82, 82, 84, 88, 91, 93 При количестве нитей 4 ( первый запуск ): 3, 3, 3, 15, 4, 8, 26, 4, 5, 6, 7, 8, 8, 9, 15, 15, 17, 88, 65, 55, 42, 41, 32, 27, 82, 63, 63, 73, 74, 74, 84, 93, 93 Второй запуск: 3, 9, 4, 3, 33, 3, 3, 74, 15, 5, 5, 6, 7, 7, 15, 15, 17, 88, 73, 65, 55, 42, 74, 41, 32, 27, 26, 39, 42, 44, 63, 84, 93

Опции директивы for private(список переменных) firstprivate( список переменных ) lastprivate(список переменных) reduction( оператор : список переменных ) nowait schedule( type[, chunk] ) ordered collapse( n )

Опции директивы for schedule(type[, chunk]) Задаёт, каким образом итерации цикла распределяются между нитями; static – блочно-циклическое распределение итераций цикла; размер блока – chunk. Первый блок из chunk итераций выполняет нулевая нить, второй блок следующая и т.д. до последней нити, затем распределение снова начинается с нулевой нити. Если значение chunk не указано, то всё множество итераций делится на непрерывные куски примерно одинакового размера (конкретный способ зависит от реализации), и полученные порции итераций распределяются между нитями. Примеры: schedule(static, 13) schedule(static) dynamic – динамическое распределение итераций с фиксированным размером блока: сначала каждая нить получает chunk итераций (по умолчанию chunk = 1), затем та нить, которая заканчивает выполнение своей порции итераций, получает первую свободную порцию из chunk итераций и т.д.. Освободившиеся нити получают новые порции итераций до тех пор, пока все порции не будут исчерпаны. Последняя порция может содержать меньше итераций, чем все остальные. Примеры: schedule(dynamic) или schedule(dynamic, 4)

Опции директивы for guided ( начиная с версии 2.0 ) – динамическое распределение итераций, при котором размер порции уменьшается с некоторого начального значения до величины chunk (по умолчанию chunk=1) пропорционально количеству ещё не распределённых итераций, делённому на количество нитей, выполняющих цикл. Размер первоначально выделяемого блока зависит от реализации. В ряде случаев такое распределение позволяет аккуратнее разделить работу и сбалансировать загрузку нитей. Количество итераций в последней порции может оказаться меньше значения chunk. Примеры: schedule(guided, 2) schedule(guided) auto – способ распределения итераций выбирается компилятором и/или системой выполнения. Параметр chunk при этом не задаётся. runtime – способ распределения итераций выбирается во время работы программы по значению переменной среды OMP_SCHEDULE. Параметр chunk при этом значении опции не задаётся.