Параллельное программирование на основе MPI
В вычислительных системах с распределенной памятью (см. рис. 6.6) процессоры работают независимо друг от друга. Для организации параллельных вычислений в таких условиях необходимо иметь возможность распределять вычислительную нагрузку и организовать информационное взаимодействие ( передачу данных ) между процессорами.
Решение всех перечисленных вопросов и обеспечивает интерфейс передачи данных ( message passing interface - MPI ).
- В общем плане, для распределения вычислений между процессорами необходимо проанализировать алгоритм решения задачи, выделить информационно независимые фрагменты вычислений, провести их программную реализацию и затем разместить полученные части программы на разных процессорах. В рамках MPI принят более простой подход - для решения поставленной задачи разрабатывается одна программа и эта единственная программа запускается одновременно на выполнение на всех имеющихся процессорах! При этом для того, чтобы избежать идентичности вычислений на разных процессорах, можно, во-первых, подставлять разные данные для программы на разных процессорах, а во-вторых, в MPI имеются средства для идентификации процессора, на котором выполняется программа (и тем самым, предоставляется возможность организовать различия в вычислениях в зависимости от используемого программой процессора).
Подобный способ организации параллельных вычислений получил наименование модели "одна программа множество процессов" ( single program multiple processes or SPMP ) ).
- Для организации информационного взаимодействия между процессорами в самом минимальном варианте достаточно операции приема и передачи данных (при этом, конечно, должна существовать техническая возможность коммуникации между процессорами - каналы или линии связи ) В MPI существует целое множество операций передачи данных. Они обеспечивают разные способы пересылки данных, реализуют практически все рассмотренные в разделе 3 коммуникационные операции. Именно данные возможности является наиболее сильной стороной MPI (об этом, в частности свидетельствует и само название MPI).
Следует отметить, что попытки создания программных средств передачи данных между процессорами начались предприниматься практически сразу с появлением локальных компьютерных сетей - ряд таких средств, представлен, например, в Воеводин В.В. и Воеводин Вл.В. (2002), Buyya (1999), Andrews (2000) и многих других. Однако подобные средства часто были неполными и, самое главное, являлись несовместимыми. Таким образом, одна из самых серьезных проблем в программировании - переносимость программ при переводе программного обеспечения на другие компьютерные системы - проявлялась при разработке параллельных программ в самой максимальной степени. Как результат, уже с 90-х годов стали предприниматься усилия по стандартизации средств организации передачи сообщений в многопроцессорных вычислительных системах. Началом работ, непосредственно приведшей к появлению MPI, послужило проведение рабочего совещания по стандартам для передачи сообщений в среде распределенной памяти (the Workshop on Standards for Message Passing in a Distributed Memory Environment, Williamsburg, Virginia, USA, April 1992). По итогам совещания была образована рабочая группа, позднее преобразованная в международное сообщество MPI Forum, результатом деятельности которых явилось создание и принятие в 1994 г. стандарта интерфейса передачи сообщений ( message passing interface - MPI ) версии 1.0. В последующие годы стандарт MPI последовательно развивался. В 1997 г. был принят стандарт MPI версии 2.0.
Итак, теперь можно пояснить, что означает понятие MPI. Во-первых, MPI - это стандарт, которому должны удовлетворять средства организации передачи сообщений. Во-вторых, MPI - это программные средства, которые обеспечивают возможность передачи сообщений и при этом соответствуют всем требованиям стандарта MPI. Так, по стандарту, эти программные средства должны быть организованы в виде библиотек программных модулей ( библиотеки MPI ) и должны быть доступны для наиболее широко используемых алгоритмических языков C и Fortran. Подобную "двойственность" MPI следует учитывать при использовании терминологии. Как правило, аббревиатура MPI используется для упоминания стандарта, а сочетание "библиотека MPI" указывает на ту или иную программную реализацию стандарта. Однако достаточно часто для краткости обозначение MPI используется и для библиотек MPI и, тем самым, для правильной интерпретации термина следует учитывать контекст.
Вопросы, связанные с разработкой параллельных программ с использованием MPI, достаточно широко рассмотрены в литературе - краткий обзор полезных материалов содержится в конце данного раздела. Здесь же, еще не приступая к изучению MPI, приведем ряд его важных положительных моментов:
- MPI позволяет в значительной степени снизить остроту проблемы переносимости параллельных программ между разными компьютерными системами - параллельная программа, разработанная на алгоритмическом языке C или Fortran с использованием библиотеки MPI, как правило, будет работать на разных вычислительных платформах,
- MPI содействует повышению эффективности параллельных вычислений, поскольку в настоящее время практически для каждого типа вычислительных систем существуют реализации библиотек MPI, в максимальной степени учитывающие возможности используемого компьютерного оборудования,
- MPI уменьшает, в определенном плане, сложность разработки параллельных программ, т.к., с одной стороны, большая часть рассмотренных в разделе 3 основных операций передачи данных предусматривается стандартом MPI, а с другой стороны, уже имеется большое количество библиотек параллельных методов, созданных с использованием MPI.
6.1 MPI: основные понятия и определения
Рассмотрим ряд понятий и определений, являющихся основополагающими для стандарта MPI.
6.1.1. Понятие параллельной программы
Под параллельной программой в рамках MPI понимается множество одновременно выполняемых процессов. Процессы могут выполняться на разных процессорах, но на одном процессоре могут располагаться и несколько процессов (в этом случае их исполнение осуществляется в режиме разделения времени). В предельном случае для выполнения параллельной программы может использоваться один процессор - как правило, такой способ применяется для начальной проверки правильности параллельной программы.
Каждый процесс параллельной программы порождается на основе копии одного и того же программного кода ( модель SPMP ). Данный программный код, представленный в виде исполняемой программы, должен быть доступен в момент запуска параллельной программы на всех используемых процессорах. Исходный программный код для исполняемой программы разрабатывается на алгоритмических языках C или Fortran с использованием той или иной реализации библиотеки MPI.
Количество процессов и число используемых процессоров определяется в момент запуска параллельной программы средствами среды исполнения MPI-программ и в ходе вычислений меняться не может. Все процессы программы последовательно перенумерованы от 0 до np-1, где np есть общее количество процессов. Номер процесса именуется рангом процесса.
6.1.2. Операции передачи данных
Основу MPI составляют операции передачи сообщений. Среди предусмотренных в составе MPI функций различаются парные ( point-to-point ) операции между двумя процессами и коллективные ( collective ) коммуникационные действия для одновременного взаимодействия нескольких процессов.
Для выполнения парных операций могут использоваться разные режимы передачи, среди которых синхронный, блокирующий и др. - полное рассмотрение возможных режимов передачи будет выполнено в подразделе 6.3.
Как уже отмечалось ранее, стандарт MPI предусматривает необходимость реализации большинства основных коллективных операций передачи данных - см. подразделы 6.2 и 6.4.
6.1.3. Понятие коммуникаторов
Процессы параллельной программы объединяются в группы. Под коммуникатором в MPI понимается специально создаваемый служебный объект, объединяющий в своем составе группу процессов и ряд дополнительных параметров ( контекст ), используемых при выполнении операций передачи данных.
Как правило, парные операции передачи данных выполняются для процессов, принадлежащих одному и тому же коммуникатору. Коллективные операции применяются одновременно для всех процессов коммуникатора. Как результат, указание используемого коммуникатора является обязательным для операций передачи данных в MPI.
В ходе вычислений могут создаваться новые и удаляться существующие группы процессов и коммуникаторы. Один и тот же процесс может принадлежать разным группам и коммуникаторам. Все имеющиеся в параллельной программе процессы входят в состав создаваемого по умолчанию коммуникатора с идентификатором MPI_COMM_WORLD.
При необходимости передачи данных между процессами из разных групп необходимо создавать глобальный коммуникатор ( intercommunicator ).
Подробное рассмотрение возможностей MPI для работы с группами и коммуникаторами будет выполнено в подразделе 6.6.
6.1.4. Типы данных
При выполнении операций передачи сообщений для указания передаваемых или получаемых данных в функциях MPI необходимо указывать тип пересылаемых данных. MPI содержит большой набор базовых типов данных, во многом совпадающих с типами данных в алгоритмических языках C и Fortran. Кроме того, в MPI имеются возможности для создания новых производных типов данных для более точного и краткого описания содержимого пересылаемых сообщений.
Подробное рассмотрение возможностей MPI для работы с производными типами данных будет выполнено в подразделе 6.5.
6.1.5. Виртуальные топологии
Как уже отмечалось ранее, парные операции передачи данных могут быть выполнены между любыми процессами одного и того же коммуникатора, а в коллективной операции принимают участие все процессы коммуникатора. В этом плане, логическая топология линий связи между процессами имеет структуру полного графа (независимо от наличия реальных физических каналов связи между процессорами).
Вместе с этим (и это уже отмечалось в разделе 3), для изложения и последующего анализа ряда параллельных алгоритмов целесообразно логическое представление имеющейся коммуникационной сети в виде тех или иных топологий.
В MPI имеется возможность представления множества процессов в виде решетки произвольной размерности (см. рис. 6.7). При этом, граничные процессы решеток могут быть объявлены соседними и, тем самым, на основе решеток могут быть определены структуры типа тор.
Кроме того, в MPI имеются средства и для формирования логических (виртуальных) топологий любого требуемого типа. Подробное рассмотрение возможностей MPI для работы с топологиями будет выполнено в подразделе 6.7.
И, наконец, последний ряд замечаний перед началом рассмотрения MPI:
- Описание функций и все приводимые примеры программ будут представлены на алгоритмическом языке C; особенности использования MPI для алгоритмического языка Fortran будут даны в п. 6.8.1,
- Краткая характеристика имеющихся реализаций библиотек MPI и общее описание среды выполнения MPI программ будут рассмотрены в п. 6.8.2,
- Основное изложение возможностей MPI будет ориентировано на стандарт версии 1.2 ( MPI-1 ); дополнительные свойства стандарта версии 2.0 буду представлены в п. 6.8.3.
Приступая к изучению MPI, можно отметить, что, с одной стороны, MPI достаточно сложен - в стандарте MPI предусматривается наличие более 125 функций. С другой стороны, структура MPI является тщательно продуманной - разработка параллельных программ может быть начата уже после рассмотрения всего лишь 6 функций MPI. Все дополнительные возможности MPI могут осваиваться по мере роста сложности разрабатываемых алгоритмов и программ. Именное в таком стиле - от простого к сложному - и будет далее представлен весь учебный материал по MPI.
6.2. Введение в разработку параллельных программ с использованием MPI
6.2.1 Основы MPI
Приведем минимально-необходимый набор функций MPI, достаточный для разработки достаточно простых параллельных программ.
6.2.1.1. Инициализация и завершение MPI программ
Первой вызываемой функцией MPI должна быть функция:
int MPI_Init ( int *agrc, char ***argv ).
для инициализации среды выполнения MPI-программы. Параметрами функции являются количество аргументов в командной строке и текст самой командной строки.
Последней вызываемой функцией MPI обязательно должна являться функция:
int MPI_Finalize (void)
Как результат, можно отметить, что структура параллельной программы, разработанная с использованием MPI, должна иметь следующий вид:
#include "mpi.h" int main ( int argc, char *argv[] ) { <программный код без использования MPI функций> MPI_Init ( &agrc, &argv ); <программный код с использованием MPI функций > MPI_Finalize(); <программный код без использования MPI функций > return 0; }
Следует отметить:
- Файл mpi.h содержит определения именованных констант, прототипов функций и типов данных библиотеки MPI,
- Функции MPI_Init и MPI_Finalize являются обязательными и должны быть выполнены (и только один раз) каждым процессом параллельной программы,
- Перед вызовом MPI_Init может быть использована функция MPI_Initialized для определения того, был ли ранее выполнен вызов MPI_Init.
Рассмотренные примеры функций дают представление синтаксиса именования функций в MPI. Имени функции предшествует префикс MPI, далее следует одно или несколько слов названия, первое слово в имени функции начинается с заглавного символа, слова разделяются знаком подчеркивания. Названия функций MPI, как правило, поясняют назначение выполняемых функцией действий.
6.2.1.2. Определение количества и ранга процессов
Определение количества процессов в выполняемой параллельной программе осуществляется при помощи функции:
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_Init:
#include "mpi.h" int main ( int argc, char *argv[] ) { int ProcNum, ProcRank; <программный код без использования MPI функций> MPI_Init ( &agrc, &argv ); MPI_Comm_size ( MPI_COMM_WORLD, &ProcNum); MPI_Comm_rank ( MPI_COMM_WORLD, &ProcRank); <программный код с использованием MPI функций > MPI_Finalize(); <программный код без использования MPI функций > return 0; }
Следует отметить:
- Коммуникатор MPI_COMM_WORLD, как отмечалось ранее, создается по умолчанию и представляет все процессы выполняемой параллельной программы,
- Ранг, получаемый при помощи функции MPI_Comm_rank, является рангом процесса, выполнившего вызов этой функции, т.е. переменная ProcRank будет принимать различные значения в разных процессах.