Леция- 7(Т2)nn.ppt
- Количество слайдов: 26
Выделяют следующие этапы разработки параллельного алгоритма Декомпозиция. На этом этапе выполняются анализ задачи и оценка возможности распараллеливания. Задача и связанные с ней данные разделяются на более мелкие части — подзадачи и фрагменты структур данных. Особенности архитектуры конкретной вычислительной системы на данном этапе могут не учитываться. Проектирование коммуникаций (обменов данными) между задачами. Определяются коммуникации, необходимые для пересылки исходных данных, промежуточных результатов выполнения подзадач, а также коммуникации, необходимые для управления работой подзадач. Выбираются методы коммуникации. Укрупнение. Подзадачи объединяются в более крупные блоки, если это позволяет повысить эффективность алгоритма и снизить трудоемкость разработки. Планирование вычислений. Распределение подзадач между процессорами. Основной критерий выбора способа размещения подзадач — эффективное использование процессоров с минимальными затратами времени на обмены данными.
Декомпозиция (сегментирование, расщепление) Существуют разные методы декомпозиции. Рассмотрим основные подходы. Декомпозиция по данным Вначале сегментируются данные, затем алгоритм их обработки. Данные разбиваются на фрагменты приблизительно одинакового размера. С фрагментами данных связываются операции их обработки, из которых и формируются подзадачи. Затем определяются необходимые пересылки данных. Пересечение частей, на которые разбивается задача, должно быть сведено к минимуму, что позволяет избежать дублирования вычислений. Схема разбиения в дальнейшем может уточняться. Если необходимо для уменьшения числа обменов, допускается увеличение степени перекрытия подзадач. Сначала анализируются структуры данных наибольшего размера, либо те, обращение к которым происходит чаще всего. На разных стадиях расчета могут использоваться разные структуры данных, поэтому могут использоваться как статические, так и динамические схемы декомпозиции этих структур.
Рекурсивная дихотомия Используется для разбиения области на подобласти, которым соответствует примерно одинаковая трудоемкость вычислений, а коммуникации сведены к минимуму. Область сначала разбивается на две части вдоль одного измерения. Разбиение повторяется рекурсивно в каждой новой подобласти столько раз, сколько потребуется для получения необходимого числа подзадач. Рекурсивная координатная дихотомия Может применяться к нерегулярным сеткам. Деление выполняется на каждом шаге вдоль того измерения, по которому сетка имеет наибольшую протяженность. Метод рекурсивной дихотомии графа Используется для нерегулярных сеток. В нем с помощью информации о топологии решетки минимизируется количество ребер, пересекающих границы подобластей. Это позволяет снизить количество коммуникаций.
Функциональная декомпозиция Вначале сегментируется вычислительный алгоритм, а затем уже под эту схему подгоняется схема декомпозиции данных. Метод функциональной декомпозиции может оказаться полезным в ситуации, где нет структур данных, которые очевидно могли бы быть распараллелены. Эффективность декомпозиции обеспечивается выполнением следующих рекомендаций: • количество подзадач после декомпозиции должно примерно на порядок превосходить количество процессоров; • следует избегать лишних вычислений и пересылок данных; • подзадачи должны быть примерно одинакового размера; • в идеале сегментация должна быть такой, чтобы с увеличением объема задачи количество подзадач также возрастало (при сохранении постоянным размера одной подзадачи). Размер подзадачи определяется зернистостью алгоритма. Мерой зернистости является количество операций в блоке. Выделяют три степени зернистости: Мелкозернистый параллелизм — на уровне команд (менее 20 команд на блок, количество параллельно выполняемых подзадач — от единиц до нескольких тысяч, средний масштаб параллелизма около 5 команд на блок). Среднеблочный параллелизм — на уровне процедур. Размер блока до 2000 команд. Выявление такого параллелизма сложнее реализовать, поскольку следует учитывать межпроцедурные зависимости. Требования к коммуникациям меньше, чем в случае параллелизма на уровне команд. Крупноблочный параллелизм — на уровне программ (задач). Соответствует выполнению независимых программ на параллельном компьютере. Крупноблочный параллелизм требует поддержки операционной системой.
Важнейшим условием декомпозиции является независимость подзадач. Существуют следующие виды независимости: Независимость по данным — данные, обрабатываемые одной частью программы, не модифицируются другой ее частью. Независимость по управлению — порядок выполнения частей программы может быть определен только во время выполнения программы (наличие зависимости по управлению предопределяет последовательность выполнения). Независимость по ресурсам — обеспечивается достаточным количеством компьютерных ресурсов. Независимость по выводу — возникает, если две подзадачи не производят запись в одну и ту же переменную, а независимость по вводу-выводу, если операторы ввода/вывода двух или нескольких подзадач не обращаются к одному файлу (или переменной). Полной независимости добиться обычно не удается.
Проектирование коммуникаций Можно выделить следующие основные типы коммуникаций: • локальные — каждая подзадача связана с небольшим набором других подзадач; • глобальные — каждая подзадача связана с большим числом других подзадач; • структурированные — каждая подзадача и подзадачи, связанные с ней, образуют регулярную структуру (например, с топологией решетки); • неструктурированные — подзадачи связаны произвольным графом; • статические — схема коммуникаций не изменяется с течением времени; • динамические — схема коммуникаций изменяется в процессе выполнения программы; • синхронные — отправитель и получатель данных координируют обмен; • асинхронные — обмен данными не координируется. Рекомендации по проектированию коммуникаций: • количество коммуникаций у подзадач должно быть примерно одинаковым, иначе приложение становится плохо масштабируемым; • там, где это возможно, следует использовать локальные коммуникации; • коммуникации должны быть, по возможности, параллельными.
Укрупнение На этапе укрупнения (агломерации) учитывается архитектура вычислительной системы и задачи, полученные на первых двух этапах, объединяются для того, чтобы их число соответствовало числу процессоров. При выполнении агломерации должны быть соблюдены следующие требования: • должны быть снижены накладные расходы на коммуникации; • если при укрупнении приходится дублировать вычисления или данные, это не должно приводить к потере производительности и масштабируемости программы; • результирующие задачи должны иметь примерно одинаковую трудоемкость; • должна быть сохранена масштабируемость; • должна быть сохранена возможность параллельного выполнения; • должна быть снижена стоимость трудоемкости разработки.
Планирование вычислений На этапе планирования определяют, на каких процессорах будут выполняться подзадачи. Основной критерий эффективности здесь — минимизация времени выполнения программы. Стратегия размещения задач на процессорах строится на основе компромисса между требованием максимальной независимости выполняющихся задач (минимизация коммуникаций) и глобальным учетом состояния вычислений. Чаще всего применяются стратегии хозяин/работник, иерархические и децентрализованные стратегии. Хозяин/работник (Master/slave) Главная задача отвечает за размещение подчиненных задач (рис. 1). Подчиненная задача получает исходные данные для обработки от главной задачи и возвращает ей результат работы. Рис. 1. Простая схема хозяин/работник
Иерархическая схема хозяин/работник Подчиненные задачи разделены на непересекающиеся подмножества и у каждого из этих подмножеств есть своя главная задача (рис. 2). Главные задачи подмножеств управляются одной "самой главной" задачей. Рис. 2. Иерархическая схема хозяин/работник
Децентрализованные схемы В этом случае главная задача отсутствует. Задачи обмениваются данными друг с другом, придерживаясь определенной стратегии (рис. 3). Это может быть случайный выбор объекта коммуникации или взаимодействие с небольшим числом ближайших соседей. В гибридной централизованно-распределенной схеме запрос посылается главной задаче, а она передает его подчиненным задачам, используя метод кругового планирования. Динамически сбалансированная загрузка может быть эффективно реализована, если учтены следующие соображения: • если каждый процессор выполняет одну подзадачу, длительность выполнения всей программы будет определяться самой «медленной» подзадачей, поэтому оптимальная производительность достигается, если все подзадачи имеют одинаковый размер; • сбалансированность может быть обеспечена посредством загрузки каждого процессора несколькими задачами.
Многопоточные программы Поток (нить) представляет собой последовательный поток управления (последовательность команд) в рамках одной программы. При создании процесса порождается главный поток, выполняющий инициализацию процесса. Он же начинает выполнение команд. Поток и процесс соотносятся следующим образом: • процесс имеет главный поток, инициализирующий выполнение команд процесса; • любой поток может порождать в рамках одного процесса другие потоки; каждый поток имеет собственный стек; • потоки, соответствующие одному процессу, имеют общие сегменты кода и данных. При разработке многопоточных приложений возникают те же проблемы, что и при разработке параллельных. Это: • гонки за данными; • блокировки; • активные блокировки; • несбалансированность загрузки.
Гонки за данными являются следствием зависимостей, когда несколько потоков модифицируют содержимое одной и той же области памяти. Наличие гонок за данными не всегда является очевидным. Они могут приводить к конфликтам двух типов: • конфликт «чтение-запись» ; • конфликт «запись-запись» . Имеются два способа борьбы с гонками за данными: • использование преимущественно локальных по отношению к потоку, а не разделяемых переменных; • управление доступом к разделяемым переменным с помощью различных средств синхронизации (они могут быть реализованы с помощью семафоров, событий, критических секций, взаимных блокировок - мьютексов). Гонки за данными могут быть скрыты синтаксисом языка программирования. Некоторые примеры приведены в табл. 1.
Таблица 1. Примеры конструкций языка программирования, в которых могут возникать гонки за данными Поток 1 Поток 2 Причина возникновения гонок за данными X += 1 X += 2 Компилятор заменяет операцию += раздельными операциями чтения и записи X A[i] += 1 A[j] += 2 Возможно совпадение значений индексов i и j *p += 1 *q += 2 Указатели p и q могут ссылаться на один адрес Func(1) Func(2) Func может суммировать значение аргумента и значение внутренней разделяемой переменной Add [abc], 1 add [abc], 2 На уровне команд модификация [ abc ] заменяется раздельными операциями чтения и записи
Блокировки Блокировка (тупик) возникает, если поток ожидает выполнение условия, которое не может быть выполнено. Обычно возникновение тупиковой ситуации является следствием конкуренции потоков за ресурс, который удерживается одним из них. Условия возникновения тупика: • доступ к ресурсу эксклюзивен (возможен только одним потоком); • поток может удерживать ресурс, запрашивая другой; • ни один из конкурирующих потоков не может освободить запрашиваемый ресурс. Активной блокировкой называют ситуацию, когда поток не производит вычислений, но и не блокируется. Потоки пытаются преодолеть помеху, создаваемую другим потоком.
Модель параллельной программы в MPI В модели программирования MPI параллельная программа при запуске порождает несколько процессов, взаимодействующих между собой с помощью сообщений. Совокупность всех процессов, составляющих параллельное приложение, или их части, описывается специальной структурой, которая называется коммуникатором (областью взаимодействия). Каждому процессу в области взаимодействия назначается уникальный числовой идентификатор — ранг, значение которого от 0 до np - 1 (np — число процессов). Ранги, назначаемые одному и тому же процессу в разных коммуникаторах, вообще говоря, различны. Структура программы, написанной по схеме хозяин/работник, приведена ниже. program para if (ранг процесса = рангу мастер-процесса) then код мастер-процесса else код подчиненного процесса (подчиненных процессов) endif end Каждый экземпляр программы уже в процессе своего выполнения определяет, является ли он мастер-процессом. Затем, в зависимости от результата этой проверки, выполняется одна из ветвей условного оператора. Первая ветвь соответствует мастер-задаче, а вторая — подчиненной задаче. Способы взаимодействия между подзадачами определяются программистом. Перед использованием процедур передачи сообщений программа должна подключиться к системе обмена сообщениями.
Сообщения Сообщение содержит пересылаемые данные и служебную информацию. Для того, чтобы передать сообщение, необходимо указать: • ранг процесса-отправителя сообщения; • адрес, по которому размещаются пересылаемые данные процесса-отправителя; • тип пересылаемых данных; • количество данных; • ранг процесса, который должен получить сообщение; • адрес, по которому должны быть размещены данные процессом-получателем; тег сообщения; • идентификатор коммуникатора, описывающего область взаимодействия, внутри которой происходит обмен. Тег — это задаваемое пользователем целое число от 0 до 32767, которое играет роль идентификатора сообщения и позволяет различать сообщения, приходящие от одного процесса. Теги могут использоваться и для соблюдения определенного порядка приема сообщений. Прием сообщения начинается с подготовки буфера достаточного размера. В этот буфер записываются принимаемые данные. Операция отправки или приема сообщения считается завершенной, если программа может вновь использовать буферы сообщений.
Разновидности обменов сообщениями В MPI реализованы разные виды обменов. Прежде всего, это двухточечные (задействованы только два процесса) и коллективные (задействованы более двух процессов). Двухточечные обмены используются для организации локальных и неструктурированных коммуникаций. При выполнении глобальных операций используются коллективные обмены. Асинхронные коммуникации реализуются с помощью запросов о получении сообщений. Имеется несколько разновидностей двухточечного обмена. Блокирующие прием/передача — приостанавливают выполнение процесса на время приема сообщения. Неблокирующие прием/передача — выполнение процесса продолжается в фоновом режиме, а программа в нужный момент может запросить подтверждение завершения приема сообщения. Синхронный обмен — сопровождается уведомлением об окончании приема сообщения. Асинхронный обмен — уведомлением не сопровождается.
Привязка к языку Fortran Имена подпрограмм и констант MPI в программах на языке Fortran начинаются с MPI_. При вызове подпрограмм коды завершения передаются через дополнительный параметр целого типа (находится на последнем месте в списке параметров подпрограммы). Код успешного завершения — MPI_SUCCESS. Константы и другие объекты MPI описываются в файле mpif. h, который включается в MPIпрограмму с помощью оператора include. В некоторых подпрограммах используется переменная status, которая является массивом стандартного целого типа. Его размер MPI_STATUS_SIZE. При обращении к подпрограммам MPI используются типы данных MPI, для большинства из которых имеется соответствие базовым типам языка (см. табл. 2) Типы MPI_Datatype и MPI_Comm — эмулируются стандартным целым типом языка Fortran (Integer). В программах на языке C используются библиотечные функции MPI, в программах на языке Fortran — процедуры.
Таблица 2. Типы данныых MPI для языка Fortran Тип данных MPI Тип данных Fortran MPI_INTEGER Integer MPI_REAL Real MPI_DOUBLE_PRECISION Double precision MPI_DOUBLE_COMPLEX Double complex MPI_COMPLEX Complex MPI_LOGICAL Logical MPI_CHARACTER Character MPI_BYTE Нет соответствия MPI_PACKED Нет соответствия Типы, которые имеются не во всех реализациях MPI_INTEGER 1 Integer*1 MPI_INTEGER 2 Integer*2 MPI_INTEGER 4 Integer*4 MPI_REAL 4 Real*4 MPI_REAL 8 Real*8
Привязка к языку C В программах на языке С имена подпрограмм имеют вид Класс_действие_подмножество или Класс_действие В C++ подпрограмма является методом для определенного класса, имя имеет в этом случае вид MPI: : Класс: : действие_подмножество Для некоторых действий введены стандартные наименования: Create — создание нового объекта, Get — получение информации об объекте, Set — установка параметров объекта, Delete — удаление информации, Is — запрос о том, имеет ли объект указанное свойство. Имена констант MPI записываются в верхнем регистре. Их описания находятся в заголовочном файле mpi. h. Входные параметры функций передаются по значению, а выходные (и INOUT) — по ссылке. Соответствие типов MPI стандартным типам языка C приведено в табл. 3.
Таблица 3. Типы данных MPI для языка С Тип данных MPI Тип данных C MPI_CHAR Signed char MPI_SHORT Signed short int MPI_INT Signed int MPI_LONG Signed long int MPI_UNSIGNED_CHAR unsigned char MPI_UNSIGNED_SHORT unsigned short int MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long int MPI_FLOAT Float MPI_DOUBLE Double MPI_LONG_DOUBLE long double MPI_BYTE Нет соответствия MPI_PACKED Нет соответствия
Основные понятия MPI Коммуникатор представляет собой структуру, содержащую либо все процессы, исполняющиеся в рамках данного приложения, либо их подмножество. Процессы, принадлежащие одному и тому же коммуникатору, наделяются общим контекстом обмена. Операции обмена возможны только между процессами, связанными с общим контекстом, то есть, принадлежащие одному и тому же коммуникатору (рис. 3). Каждому коммуникатору присваивается идентификатор. В MPI есть несколько стандартных коммуникаторов: MPI_COMM_WORLD - включает все процессы параллельной программы; MPI_COMM_SELF - включает только данный процесс; MPI_COMM_NULL - пустой коммуникатор, не содержит ни одного процесса. В MPI имеются процедуры, позволяющие создавать новые коммуникаторы, содержащие подмножества процессов. Рис. 3. Коммуникатор
Ранг процесса представляет собой уникальный числовой идентификатор, назначаемый процессу в том или ином коммуникаторе. Ранги в разных коммуникаторах назначаются независимо и имеют целое значение от 0 до число_процессов - 1 (рис. 4). Тег (маркер) сообщения - это уникальный числовой идентификатор, который назначается сообщению и позволяет различать сообщения, если в этом есть необходимость. Если тег не требуется, вместо него можно использовать «джокер» MPI_ANY_TAG. Типовая структура MPI-программы: program para if (process = master) then master clause else slave clause endif end Рис. 4. Ранги процессов
Леция- 7(Т2)nn.ppt