Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Параллельные вычисления и системы

..pdf
Скачиваний:
8
Добавлен:
05.02.2023
Размер:
973.31 Кб
Скачать

-MPI_Type_extent - запрос объема памяти, занимаемой переменной производного типа;

-MPI_Type_size - запрос минимального («ужатого») объема сообщения с переменной производного типа при передаче ее в сообщении.;

-MPI_Type_free - сброс описателя пользовательского типа.

Функции выполняют поэлементно заданную операцию над элементами распределенных массивов. Интерфейс MPI содержит 4 распределенные функции:

-MPI_Reduce - массив с результатами размещается в выделенной ветви,

-MPI_Allreduce - массив с результатами размещается во всех ветвях,

-MPI_Reduce_scatter - массив с результатами распределяется между ветвями,

-MPI_Scan : аналогична функции MPI_Allreduce. Но содержимое массива-результата в задаче i является результатом выполнение операции над массивами из задач с номерами от 0 до i включительно.

В MPI предопределены 12 описателей поэлементных операций:

-MPI_MAX и MPI_MIN –поиск поэлементных максимума и мини-

мума,

-MPI_SUM - сумма векторов;

-MPI_PROD - поэлементное произведение векторов;

-MPI_LAND, MPI_BAND, MPI_LOR, MPI_BOR, MPI_LXOR, MPI_BXOR - логические и двоичные операции И, ИЛИ, исключающее ИЛИ,

-MPI_MAXLOC, MPI_MINLOC – поиск

Группа - это некое множество ветвей. Одна ветвь может быть членом нескольких групп.

Область связи ("communication domain") - это абстрактное понятие, связанное с коммуникатором. Области связи автоматически создаются и уничтожаются вместе с коммуникаторами.

Одной области связи могут соответствовать несколько коммуникаторов. Коммуникаторы функционируют независимо и параллельно во времени.

Функции создания-уничтожения групп:

- MPI_Group_xxx - создает новую группу с нужным набором ветвей на базе существующей группы,

11

-MPI_Comm_group создает группу, на которую указывает соответствующий коммуникатор,

-MPI_Group_free – уничтожает группу.

Функции создания коммуникаторов:

-MPI_Comm_dup – копирует один коммуникатор в другой,

-MPI_Comm_split – расщепляет группу существующего коммуникатора на непересекающиеся подгруппы,

-MPI_Comm_create создает коммуникатор для группы.

-MPI_Group_freе.

Задание

Изучить описание MPI – интерфейса.

Разработать библиотеку функций MPI-интерфейса для языка программирования C/C++.

В библиотеку в обязательном порядке должны быть включены функ-

ции MPI_Init(), MPI_Size(), MPI_Rank(). Должна быть реализована мини-

мум одна функция коллективного обмена.

Провести комплексную отладку функций библиотеки команд NETBIOS и библиотеки MPI.

Провести тестирование надежности и пропускной способности функций библиотеки MPI.

Сделать выводы по итогам проделанной работы.

2.5. Практическое занятие «Технология программирования

OpenMP»

Цель работы

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

12

Теоретические сведения

Технология OpenMP в настоящее время является одним из наиболее популярных средств параллельного программирования для компьютеров с общей памятью, базирующейся на традиционных последовательных языках программирования и использовании специальных комментариев. За основу берётся последовательная программа, а для создания её параллельной версии пользователю предоставляется набор директив, функций и переменных окружения. Технология OpenMP нацелена на то, чтобы пользователь имел один и тот же вариант программы как для параллельного, так и для последовательного режима выполнения. Параллелизм в OpenMP реализуется с помощью многопоточности. При запуске программы создается единственный «главный» (master) поток, который затем создает набор «подчиненных» (slave) потоков, и вычисления распределяются между всеми потоками. Предполагается, что потоки выполняются параллельно на машине с несколькими процессорами (ядрами), причём количество процессоров/ядер не обязательно должно быть больше или равно количеству потоков. Другими словами, потоки параллельной программы могут обычным образом конкурировать между собой за время процессора/ядра.

Важным достоинством технологии OpenMP является возможность реализации так называемого инкрементального программирования, когда программист постепенно находит участки в программе, содержащие ресурс параллелизма, с помощью предоставляемых механизмов делает их параллельными, а затем переходит к анализу следующих участков. Таким образом, распараллеленные участки постепенно охватывают всё большую часть программы. Этот подход значительно облегчает процесс адаптации последовательных программ к параллельным компьютерам, а также отладку и оптимизацию программ.

Модель исполнения параллельной программы, подготовленной с помощью технологии OpenMP, можно сформулировать следующим образом:

-программа содержит набор последовательных и параллельных областей (или секций или регионов);

-в начальный момент времени создается главный поток, выполняющий впоследствии все последовательные области программы;

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

13

ные потоки исполняют один и тот же код, но с разными данными. В общем случае потоки могут исполнять различные фрагменты кода;

- при выходе из параллельной области всеми потоками выполняется операция join. Завершается выполнение всех потоков, кроме главного.

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

Под OpenMP понимается совокупность следующих компонент:

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

-Подпрограммы библиотеки времени выполнения - используются для установки и определения атрибутов потоков. Вызовы этих подпрограмм включаются программистом в исходный текст.

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

Использование директив компилятора и подпрограмм библиотеки времени выполнения подчиняется правилам, которые различаются для разных языков программирования. Совокупность таких правил для одного языка программирования называется привязкой к языку. Технология OpenMP создана и поддерживается для языков С, С++ и Fortran. На этом практическом занятии предполагается использование языка С/С++ в среде разработки Microsoft Visual Studio (начиная с версии 2005). Для того, чтобы ее компилятор нужным образом реагировал на директивы OpenMP и строил параллельную программу, в командную строку его запуска должна быть включена опция /openmp (например, путем включения флажка «OpenMP support» в свойствах проекта на закладке Configuration properties\C/C++\Language).

Для того, чтобы в программе на языке С/С++ стали доступны возможности технологии OpenMP, в нее нужно включить заголовочный файл omp.h: #include <omp.h>

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

14

В программах на языке C/C++ все прагмы, имена функций и переменных окружения OpenMP начинаются со строки «omp». Формат директивы:

#pragma omp директива [опция_1[, опция_2, ...]]

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

Перечень директив OpenMP включает в себя:

Директива задания параллельно выполняемой секции:

#pragma omp parallel [опция_1[, опция_2, ...]] <структурный блок кода>

С помощью опций этой директивы можно указать:

-требуемое количество потоков n (num_threads(n));

-условие, при котором параллельная область действительно создается (if(условие));

-список общих переменных для всех потоков данной секции (shared(список переменных));

-список переменных, которые будут локальными в каждом потоке секции (private(список переменных)), причем их начальные значения не будут определены;

-список переменных, которые будут локальными в каждом потоке секции (firstprivate(список переменных)), причем в качестве их начальных значений будут установлены значения одноименных переменных из главного потока;

-способ назначения класса памяти default(shared|none) всем переменным потоков, которым класс не назначен явно с помощью опции shared (слово none означает, что класс памяти всех локальных переменных должен быть задан явно); в реализациях для языка Fortran могут назначаться классы private и firstprivate;

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

-оператор сведения и список общих переменных reduction(оператор : список переменных); для каждой указанной в списке переменной создаются локальные копии в каждом потоке; локальные копии инициализи-

15

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

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

#pragma omp for [опция_1[, опция_2, ...]] <структурный блок кода>

С использованием опций директивы for можно указать:

-список переменных, которые будут локальными в каждом потоке секции (private(список переменных)), причем их начальные значения не будут определены;

-список переменных, которые будут локальными в каждом потоке секции (firstprivate(список переменных)), причем в качестве их начальных значений будут установлены значения одноименных переменных из главного потока;

-список переменных главного потока (lastprivate (список переменных)), которым будут присвоены значения, полученные при выполнении последней итерации цикла;

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

-способ распределения итераций цикла между потоками параллельной секции (schedule(type[, chunk])); параметр chunk этой опции определяет количество итераций на один поток (по умолчанию 1), параметр type указывает тип распределения и может иметь значения:

static – статический, т.е. при компиляции,

dynamic – динамический, т.е. при выполнении,

16

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

auto – способ распределения выбирается компилятором или исполняющей системой,

runtime – способ распределения задается специальной переменной окружения операционной системы;

-возможность (опция ordered) появления в теле цикла директивы ordered, которая требует исполнения охваченного ею блока операторов в точности в той же последовательности, какая реализуется в последовательной версии данного цикла;

-отмену (nowait) неявной барьерной синхронизации потоков, достигших конца выполнения своей части итераций цикла (при отсутствии этой опции участок программы после цикла будет выполняться только тогда, когда все потоки выполнят все свои итерации;

- глубину n вложенных друг в друга циклов (collapse(n)), пространство итераций которых подлежит распределению между параллельными потоками (доступно только в реализации 2.5 технологии OpenMP);

Директивы parallel и for можно объединять в одну директиву, в которой можно указывать опции как директивы parallel, так и директивы for:

#pragma omp parallel for [опция_1[, опция_2, ...]]

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

#pragma omp ordered <структурный блок кода>

Директива задания области нециклического параллелизма (участки структурного блока кода, которые могут выполняться параллельно, выделяются с помощью описываемой далее директивы section):

#pragma omp sections [опция_1[, опция_2, ...]] <структурный блок кода>

В качестве опций этой директивы можно указывать private, firstprivate, lastprivate, reduction и nowait, имеющие в точности такой же синтаксис и тот же смысл, что и у директивы for.

Директива определения участка нециклического кода для одного потока: #pragma omp section

17

С помощью этой директивы внутри участка кода, охваченного директивой sections, выделяются отдельные фрагменты для исполнения параллельными потоками. Перед самым первым фрагментом участка нециклического кода директиву section можно не указывать.

Директива объявления списка локальных переменных потоков

#pragma omp threadprivate(список переменных)

Эта директива позволяет сделать локальные копии для статических переменных языка С/С++ (и COMMON-блоков языка Фортран), которые по умолчанию являются общими.

Директива создания отдельной независимой задачи (начиная с версии 2.5 OpenMP): #pragma omp task [опция_1[, опция_2, ...]]

<структурный блок кода>

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

Возможные опции:

-if, default, private, firstprivate и shared – имеют такой же синтаксис и смысл, как и в предыдущих директивах;

-untied – означает, что в случае откладывания задача может быть продолжена любым потоком из числа выполняющих данную параллельную область; если данная опция не указана, то задача может быть продолжена только породившим её потоком;

Директива ожидания потоком завершения всех независимых задач, запущенных именно из данного потока:

#pragma omp taskwait

Директива, требующая исполнения охваченного ею участка кода в точности одним (любым) потоком:

#pragma omp single [опция_1[, опция_2, ...]] <структурный блок кода>

Опции директивы позволяют указать:

-списки переменных private и firstprivate, имеющие такой же синтаксис и семантику, как в ранее описанных директивах;

-список переменных (copyprivate(список переменных)), значения которых после выполнения структурного блока, заданного директивой single, будут занесены во все одноименные локальные переменные

18

(private и firstprivate), заданные для охватывающей параллельной секции; эта опция не может использоваться совместно с опцией nowait; переменные списка не должны быть перечислены в опциях private и firstprivate данной директивы single;

- отмену (nowait) неявной барьерной синхронизации потоков, достигших точки, следующей за блоком операторов, охваченным директивой single; при отсутствии этой опции такая синхронизация выполняется.

Директива, требующая исполнения охваченного ею участка кода главным потоком программы:

#pragma omp master <структурный блок кода>

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

Директива явной барьерной синхронизации: #pragma omp barrier

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

тивы task и taskwait).

Директива, объявляющая критическую секцию – участок парал-

лельной области программы, который единовременно может выполняться не более, чем одним потоком: #pragma omp critical [()]

<структурный блок кода>

Если критическая секция уже выполняется каким-либо потоком, то все остальные, выполнившие эту директиву для секции с данным именем, будут заблокированы, пока вошедший в секцию поток не закончит выполнение этого блока кода. Как только это произойдет, один из заблокированных потоков войдет в критическую секцию. Если на входе в критическую секцию стояло несколько потоков, то случайным образом выбирается один из них, а остальные заблокированные продолжают ожидание.

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

19

параллельных областях. Побочные входы и выходы из критической секции запрещены.

Директива блокировки доступа к общей переменной из левой части оператора присваивания на время выполнения всех действий с этой переменной в данном операторе: #pragma omp atomic

<оператор присваивания>

Атомарной (блокирующей выполнение остальных потоков) является только работа с переменной из левой части оператора присваивания, при этом вычисления в его правой части, использующие другие переменные, не обязаны быть атомарными.

Директива актуализации значений переменных потока:

#pragma omp flush [(список переменных)]

Значения всех переменных (или переменных из списка, если он задан), временно хранящиеся в регистрах и кэш-памяти текущего потока, заносятся в основную память; следовательно, все изменения переменных, сделанные потоком во время работы, станут видимы остальным потокам; если какая-то информация хранится в буферах вывода, то буферы будут сброшены на внешние носители и т.п. Эти действия производятся только с данными выполнившего директиву потока, а данные, изменявшиеся другими потоками, не затрагиваются. До полного завершения этих действий никакие другие операции с участвующими в директиве flush переменными не могут выполняться. Поэтому выполнение директивы flush без списка переменных может повлечь значительные накладные расходы. Если в данный момент нужна гарантия согласованного представления не всех, а лишь некоторых переменных, то именно их следует явно перечислить в директиве списком.

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

OMP_NUM_THREADS – количество потоков в новой параллельной области.

OMP_NESTED – возможность создания вложенных параллельных областей.

OMP_MAX_ACTIVE_LEVELS – максимальная глубина вложенности параллельных областей.

OMP_DYNAMIC – возможность динамического определения количества потоков (имеет больший приоритет, чем OMP_NUM_THREADS). OMP_THREAD_LIMIT – максимальное количество потоков программы.

20