Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Техехин - Системное программное обеспечение - взаимодействие процессов (1114927), страница 24
Текст из файла (страница 24)
В таком случае для перенесения программы содной архитектуры на другую было бы достаточно простойперекомпиляции, а программист, освоивший данное средство,получил бы возможность создавать эффективные программы дляширокого класса параллельных архитектур. Одним из таких широкораспространенных средств параллельного программированияявляется MPI.150MPI представляет собой стандарт, описывающий некотороемножествофункцийдляобменасообщениямимеждупараллельными процессами. Существует множество реализаций MPIдля различных параллельных архитектур, как с распределенной, таки с общей памятью.
Как правило, эти реализации оформлены в виденабора библиотечных функций, которые можно использовать припрограммировании на языках Фортран и Си.В модели программирования MPI приложение представляетсобой совокупность процессов или нитей (иначе называемыхветвями), общающихся друг с другом путем передачи сообщений.При этом для организации обмена не является важным, будут липроцессы исполняться на одном процессоре или вычислительномузле или на разных – механизм обмена данными в обоих случаяходинаков. Во всем, что не касается передачи и приема сообщений,ветви являются независимыми и изолированными друг от друга.Отметим, что ветви приложения могут обмениваться сообщениями всреде MPI только между собой, но не с процессами другихприложений, исполняющимися в той же вычислительной системе.Помимо функций для обмена сообщениями, MPI предоставляетвозможности для взаимной синхронизации процессов и для решенияряда других вспомогательных задач.Количество ветвей в данном приложении задается в моментего запуска, т.е.
не существует возможности порождать ветвидинамически во время исполнения приложения18. Запуск MPIприложения осуществляется с помощью специальной программы(чаще всего она называется mpirun), которой обычно указываетсяколичество ветвей, которые необходимо породить, имяисполняемого файла, а также входные параметры приложения.При написании программы с использованием MPI ееисходный текст должен содержать код для всех ветвей сразу, однаково время исполнения у каждой ветви имеется возможностьопределить свой собственный порядковый номер и общееколичество ветвей и в зависимости от этого исполнять ту или инуючасть алгоритма (данный подход в чем-то аналогичениспользованию системного вызова fork())18Заметим, что в версии стандарта MPI-2 описывается возможность динамическогопорождения ветвей, однако на момент написания данного пособия нам неизвестно ни об однойреализации MPI, в которой поддерживалось бы динамическое порождение ветвей, поэтомудалее мы будем рассматривать только модель со статическим порождением ветвей в моментзапуска приложения.1517.2.3 Функции общего назначения.
Общая структурапрограммы.Все без исключения функции Си-библиотеки MPI возвращаютцелое значение, описывающее код завершения операции.Существует специальная константа MPI_SUCCESS, соответствующаяуспешному завершению функции. В случае неудачного завершениявозвращаемое значение будет отлично от MPI_SUCCESS, и равноодному из кодов ошибок, определенных в MPI и описывающихразличные исключительные ситуации.Все типы данных, константы и функции, относящиеся к Сибиблиотеке MPI, описаны в заголовочном файле <mpi.h>.Коммуникаторы и группы.Важными понятиями MPI являются группы ветвей икоммуникаторы.
Группа ветвей представляет собой упорядоченноемножество ветвей. Каждой ветви в группе назначается уникальныйпорядковый номер – целое число в диапазоне [0, N-1], где N –количество ветвей в группе.При запуске приложения все его порожденные ветви образуютначальную группу. Библиотека MPI предоставляет функции,позволяющие создавать новые группы на основе существующихпутем их разбиения, объединения, исключения ветвей из групп и т.п.операций.С каждой группой ветвей MPI связан так называемый«коммуникационный контекст», или «коммуникационное поле»,задающее множество всех возможных участников операций обменаданными и уникальным образом описывающее каждого участника.Кроме того, коммуникационный контекст может использоваться дляхранения некоторых общих для всех ветвей данной группы данных.Для описания коммуникационного контекста в MPI служат объектыспециального типа – коммуникаторы.
Коммуникатор, илиописатель коммуникационного контекста, присутствует в качествепараметра во всех вызовах MPI, связанных с обменом данными.Подчеркнем, что любая операция обмена данными может бытьосуществлена только внутри определенного коммуникационногоконтекста, поэтому, например, сообщение, отправленное с помощьюкакого-либо коммуникатора, может бытьполучено только спомощью этого же самого коммуникатора.Коммуникаторы MPI описываются специальным типомданных MPI_Comm.
При запуске приложения сразу послеинициализации среды выполнения MPI каждой ветви становятся152доступны два предопределенных коммуникатора, обозначаемыхконстантами MPI_COMM_WORLD и MPI_COMM_SELF. MPI_COMM_WORLDпредставляетсобойкоммуникатор,описывающийкоммуникационный контекст начальной группы ветвей, т.е. всехветвей, порожденных при запуске приложения. MPI_COMM_SELF – этокоммуникатор, включающий одну-единственную ветвь – текущую, исоответственно, для каждой ветви он будет иметь свое значение.Каждая ветвь приложения имеет возможность узнать общееколичество ветвей в своей группе и свой собственный уникальныйномер в ней.
Для этого служат функции MPI_Comm_size() иMPI_Comm_rank():#include <mpi.h>int MPI_Comm_size(MPI_Comm comm, int *size);int MPI_Comm_rank(MPI_Comm comm, int *rank);В первом параметре каждой из этих функций передаетсякоммуникатор,описывающийкоммуникационныйконтекстинтересующей группы ветвей, во втором – указатель нацелочисленную переменную, в которую будет записан результат:для функции MPI_Comm_size() – количество ветвей в группе; дляфункции MPI_Comm_rank() – уникальный номер текущей ветви вгруппе.Отметим, что в MPI предусмотрена также возможность обменасообщениями между ветвями, принадлежащими разным группам.Для этого существует возможность создать так называемый интеркоммуникатор, объединяющий коммуникационные контексты,заданные двумя различными коммуникаторами, каждый из которыхописывает коммуникационный контекст некоторой группы.
Послесоздания такого коммуникатора его можно использовать дляорганизации обмена данными между ветвями этих групп.Обрамляющие функции. Инициализация и завершение.Самым первым вызовом MPI в любой программе,использующей эту библиотеку, должен быть вызов функцииMPI_Init() – инициализация среды выполнения MPI.#include <mpi.h>int MPI_Init(int *argc, char ***argv);Этой функции передаются в качестве параметров указатели напараметры, полученные функцией main(), описывающие аргументыкомандной строки. Смысл этого заключается в том, что при загрузкеMPI-приложения mpirun может добавлять к его аргументам153командной строки некоторые дополнительные параметры,необходимые для инициализации среды выполнения MPI.
В этомслучае вызов MPI_Init() также изменяет массив аргументовкомандной строки и их количество, удаляя эти дополнительныеаргументы.Еще раз отметим, что функция MPI_Init() должна бытьвызвана до любой другой функции MPI, кроме того, в любойпрограмме она должна вызываться не более одного раза.По завершении работы с MPI в каждой ветви необходимовызвать функцию закрытия библиотеки – MPI_Finalize():#include <mpi.h>int MPI_Finalise(void);После вызова этой функции невозможен вызов ни однойфункции библиотеки MPI (в том числе и повторный вызовMPI_Init())Для аварийного завершения работы с библиотекой MPIслужит функция MPI_Abort().#include <mpi.h>int MPI_Abort(MPI_Comm comm, int errorcode);Эта функция немедленно завершает все ветви приложения,входящие в коммуникационный контекст, который описываеткоммуникатор comm.
Код завершения приложения передается впараметре errorcode. Отметим, что MPI_Abort() вызываетзавершения всех ветвей данного коммуникатора даже в том случае,если она была вызвана только в какой-либо одной ветви.Синхронизация: барьеры.Для непосредственной синхронизации ветвей впредусмотрена одна специальная функция – MPI_Barrier().MPI#include <mpi.h>int MPI_Barrier(MPI_Comm comm);С помощью этой функции все ветви, входящие вопределенный коммуникационный контекст, могут осуществить такназываемую барьерную синхронизацию. Суть ее в следующем: впрограмме выделяется некоторая точка синхронизации, в которойкаждая из ветвей вызывает MPI_Barrier().
Эта функция блокируетвызвавшую ее ветвь до тех пор, пока все остальные ветви из данногокоммуникационного контекста также не вызовут MPI_Barrier().После этого все ветви одновременно разблокируются. Таким154образом, возврат управления из MPI_Barrier() осуществляется вовсех ветвях одновременно.Барьерная синхронизация используется обычно в тех случаях,когда необходимо гарантировать завершение работы над некоторойподзадачей во всех ветвях перед переходом к выполнениюследующей подзадачи.Важно понимать, что функция MPI_Barrier(), как и другиеколлективные функции, которые мы подробно рассмотрим ниже,должна быть обязательно вызвана во всех ветвях, входящих вданный коммуникационный контекст. Из семантики функцииMPI_Barrier() следует, что если хотя бы в одной ветви иззаданного коммуникационного контекста MPI_Barrier() вызвана небудет, это приведет к «зависанию» всех ветвей, вызвавшихMPI_Barrier().Пример 30. Использование барьерной синхронизации.В данном примере иллюстрируется общая схема построенияпрограммы с использованием библиотеки MPI, а такжеиспользование функций общего назначения и барьернойсинхронизации.На экран выводится общее количество ветвей в приложении,затем каждая ветвь выводит свой уникальный номер.
После этоговетвь с номером 0 печатает на экран аргументы командной строки.Барьерная синхронизация необходима для того, чтобы сообщения опорядковых номерах ветвей не смешивались с сообщением обобщем количестве ветвей и с выводом аргументов команднойстроки.#include <mpi.h>#include <stdio.h>int main(int argc, char **argv){int size, rank, i;MPI_Init(&argc,библиотеку */&argv);/*ИнициализируемMPI_Comm_size(MPI_COMM_WORLD, &size);/*Узнаемколичествоприложении...