ПОД (пособие) (1184372), страница 40
Текст из файла (страница 40)
Относительновнутреннего цикла никаких указаний нет, поэтому он будет выполняться последовательнокаждой нитью.Параллелизм на уровне независимых фрагментов оформляется в OpenMP с помощьюдирективы SECTIONS : END SECTIONS:!$OMP SECTIONS< фрагмент 1>!$OMP SECTIONS< фрагмент 2>!$OMP SECTIONS< фрагмент 3>!$OMP END SECTIONSВ данном примере программист описал, что все три фрагмента информационнонезависимы, и их можно исполнять в любом порядке, в частности, параллельно друг другу.Каждый из таких фрагментов будет выполнен какой-либо одной нитью.Если в параллельной секции какой-то участок кода должен быть выполнен лишь один раз(такая ситуация иногда возникает, например, при работе с общими переменными), то егонужно поставить между директивами SINGLE : END SINGLE. Такой участок кода будетвыполнен нитью, первой дошедшей до данной точки программы.Одно из базовых понятий OpenMP - классы переменных. Все переменные, используемые впараллельной секции, могут быть либо общими, либо локальными.
Общие переменныеописываются директивой SHARED, а локальные директивой PRIVATE. Каждая общаяпеременная существует лишь в одном экземпляре и доступна для каждой нити под одним итем же именем. Для каждой локальной переменной в каждой нити существует отдельныйэкземпляр данной переменной, доступный только этой нити. Предположим, что следующийфрагмент расположен в параллельной секции:I = OMP_GET_THREAD_NUM()PRINT *, IЕсли переменная I в данной параллельной секции была описана как локальная, то на выходебудет получен весь набор чисел от 0 до OMP_NUM_THREADS-1, идущих, вообще говоря, впроизвольном порядке, но каждое число встретиться только один раз.
Если же переменная Iбыла объявлена общей, то единственное, что можно сказать с уверенностью - мы получимпоследовательность из OMP_NUM_THREADS чисел, лежащих в диапазоне от 0 доOMP_NUM_THREADS-1 каждое. Сколько и каких именно чисел будет впоследовательности заранее сказать нельзя. В предельном случае, это может быть даженабор из OMP_NUM_THREADS одинаковых чисел I0.
Предположим, что все процессы,кроме процесса I0, выполнили первый оператор, но затем их выполнение по какой-топричине было прервано. В это время процесс с номером I0 присвоил это значениепеременной I, а поскольку данная переменная является общей, то одно и тоже значениезатем и будет выведено каждой нитью.Целый набор директив в OpenMP предназначен для синхронизации работы нитей.
Самыйраспространенный способ синхронизации - барьер. Он оформляется с помощью директивы127!$OMP BARRIER .Все нити, дойдя до этой директивы, останавливаются и ждут пока все нити не дойдут доэтой точки программы, после чего все нити продолжают работать дальше.Пара директив MASTER : END MASTER выделяет участок кода, который будет выполнентолько нитью-мастером.
Остальные нити пропускают данный участок и продолжают работус выполнения оператора, расположенного следом за директивой END MASTER.С помощью директив!$OMP CRITICAL [ (<имя_критической_секции>) ]...!$OMP END CRITICAL [ (< имя_ критической_секции >) ],оформляется критическая секция программы. В каждый момент времени в критическойсекции может находиться не более одной нити.
Если критическая секция уже выполняетсякакой-либо нитью P0, то все другие нити, выполнившие директиву для секции с даннымименем, будут заблокированы, пока нить P0 не закончит выполнение данной критическойсекции. Как только P0 выполнит директиву END CRITICAL, одна из заблокированных навходе нитей войдет в секцию.
Если на входе в критическую секцию стояло несколько нитей,то случайным образом выбирается одна из них, а остальные заблокированные нитипродолжают ожидание. Все неименованные критические секции условно ассоциируются содним и тем же именем.Частым случаем использования критических секций на практике является обновлениеобщих переменных. Например, если переменная SUM является общей и оператор видаSUM=SUM+Expr находится в параллельной секции программы, то при одновременномвыполнении данного оператора несколькими нитями можно получить некорректныйрезультат. Чтобы избежать такой ситуации можно воспользоваться механизмомкритических секций или специально предусмотренной для таких случаев директивойATOMIC:!$OMP ATOMICSUM=SUM+Expr .Данная директива относится к идущему непосредственно за ней оператору, гарантируякорректную работу с общей переменной, стоящей в левой части оператора присваивания.Поскольку в современных параллельных вычислительных системах может использоватьсясложная структура и иерархия памяти, пользователь должен иметь гарантии того, что внеобходимые ему моменты времени каждая нить будет видеть единый согласованный образпамяти.
Именно для этих целей и предназначена директива!$OMP FLUSH [ список_переменных ].Выполнение данной директивы предполагает, что значения всех переменных, временнохранящиеся в регистрах, будут занесены в основную память, все изменения переменных,сделанные нитями во время их работы, станут видимы остальным нитям, если какая-тоинформация хранится в буферах вывода, то буферы будут сброшены и т.п. Посколькувыполнение данной директивы в полном объеме может повлечь значительных накладныхрасходов, а в данный момент нужна гарантия согласованного представления не всех, а лишьотдельных переменных, то эти переменные можно явно перечислить в директиве списком.Ограничения на распараллеливание циклов.Ограничения на используемые операторы в векторизуемых циклахСущественным ограничением на конструкции, которые могут применяться ввекторизуемых циклах, является использование только операторов присваивания и128арифметических выражений.
Никакие команды перехода (условные ветвления, вызовыподпрограмм и функций, циклические операторы или безусловные переходы) не могутбыть использованы в теле векторизуемого цикла.Индексные выражения не должны иметь индекс в индексе А(В(С))Из перечисленных запретов существует только два исключения. Первое использование встроенных (INTRINSIC) в транслятор арифметических функций.Большинство таких функций реализуются в библиотеках языка, а некоторые транслируютсяв последовательности машинных команд. Обычно каждая функция имеет две реализации скалярную и векторную. Про встроенные функции транслятор "знает" все, чтобысгенерировать векторный код.
Примером может служить функция cos(x). Скалярнаяреализация может брать свой аргумент в определенном регистре (например, r0) ивозвращать значение в том же регистре сохраняя прежними значения остальных регистров.Векторный выриант может брать аргумент в векторном регистре (например, v0) и там жеоставлять результат. Поэтому транслятору достаточно вычислить массив аргументов взаданном регистре, вызвать библиотечную функцию и далее работать с полученныммассивом значений.Важное замечание для любителей Си. В этом языке все математические функциивнешние - они описываются в файле math.h и содержаться в дополнительной (!) библиотекеlibm.a - и они не векторизуются (чаще всего). В ФОРТРАНе большое число функций (в т.ч.и комплексного аргумента) встроены.
Разработчики программного обеспечения длявекторных ЭВМ обычно расширяют стандартный набор функций, что позволяетиспользовать их в векторизуемых циклах.Второе исключение - использование условного оператора присваиванияif( x(i) .lt. 0.0 ) z(i) = 0.0Все арифметические операции (в т.ч. и само присваивание) будут выполняться не над всемвекторным регистром, а только над теми его элементами, для которых было справедливовычисленное логическое выражение (маскируемые операции).
Команда сравненияустанавливает маску для каждого элемента вектора: "истина", если элемент вектора меньшенуля, и "ложь", если элемент больше или равен нулю. Команда присваивания не затронет теэлементы массива z, для которых маска равна "ложь". Векторный процессор будетисполнять команды для вычисления арифметического выражения и команду присваивания,даже если не будет ни одного значения маски "истина". Это важное примечание. Командыисполнения по маске всегда будут занимать процессорное время.Синхронизация параллельных процессов.
Барьеры.Основным механизмом синхронизации параллельных процессов, взаимодействующих спомощью передачи сообщений, является барьер. Барьер - это точка параллельнойпрограммы, в которой процесс ждёт все остальные процессы, с которыми онсинхронизирует свою работу. Лишь только после того, как все процессы,синхронизирующие свою работу, достигли барьера, они продолжают дальнейшиевычисления. Если по какой-то причине хотя бы один из этих процессов не достигаетбарьера, то остальные процессы "зависают" в этой точке программы, а программа в целомуже никогда не сможет завершиться нормально.Барьеры – весьма своеобразное средство синхронизации. Идея его в том, чтобы вопределенной точке ожидания собралось заданное число потоков управления.
Только послеэтого они смогут продолжить выполнение. (Поговорка "семеро одного не ждут" к барьерамне применима.)129Барьеры полезны для организации коллективных распределенных вычислений вмногопроцессорной конфигурации, когда каждый участник (поток управления) выполняетчасть работы, а в точке сбора частичные результаты объединяются в общий итог.Отметим, что для барьеров отсутствует вариант синхронизации с контролем времениожидания. Это вполне понятно, поскольку в случае срабатывания контроля барьер окажетсяв неработоспособном состоянии (требуемое число потоков, скорее всего, уже не соберется).Критические секции.
Двоичные и общие семафоры.Важным понятием синхронизации процессов является понятие "критическая секция"программы. Критическая секция - это часть программы, в которой осуществляется доступ кразделяемым данным. Чтобы исключить эффект гонок по отношению к некоторомуресурсу, необходимо обеспечить, чтобы в каждый момент в критической секции, связаннойс этим ресурсом, находился максимум один процесс.