Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Терехин - Системное программное обеспечение - взаимодействие процессов (2002) (1114651), страница 18
Текст из файла (страница 18)
Поскольку в MPP-системе каждый узел представляет собой относительно самостоятельную единицу, то, как правило, управление массивно-параллельной системой в целом осуществляется одним из двух способов:
-
На каждом узле может работать полноценная операционная система, функционирующая отдельно от других узлов. При этом, разумеется, такая ОС должна поддерживать возможность коммуникации с другими узлами в соответствии с особенностями данной архитектуры.
-
«Полноценная» ОС работает только на управляющей машине, а на каждом из узлов MPP-системы работает некоторый сильно «урезанный» вариант ОС, обеспечивающий работу задач на данном узле.
В массивно-параллельной архитектуре отсутствует возможность осуществлять обмен данными между ВУ напрямую через память, поэтому взаимодействие между процессорами реализуется с помощью аппаратно и программно поддерживаемого механизма передачи сообщений между ВУ. Соответственно, и программы для MPP-систем обычно создаются в рамках модели передачи сообщений.
Системы с общей памятью – SMP.
В качестве наиболее распространенного примера систем с общей памятью рассмотрим архитектуру SMP16 – симметричную мультипроцессорную систему. SMP-системы состоят из нескольких однородных процессоров и массива общей памяти, который обычно состоит из нескольких независимых блоков. Слово «симметричный» в названии данной архитектуры указывает на то, что все процессоры имеют доступ напрямую (т.е. возможность адресации) к любой точке памяти, причем доступ любого процессора ко всем ячейкам памяти осуществляется с одинаковой скоростью. Общая схема SMP-архитектуры изображена на Рис. 23.
Рис. 23 Архитектура SMP
Процессоры подключены к памяти либо с помощью общей шины, либо с помощью коммутатора. Отметим, что в любой системе с общей памятью возникает проблема кэширования: так как к некоторой ячейке общей памяти имеет возможность обратиться каждый из процессоров, то вполне возможна ситуация, когда некоторое значение из этой ячейки памяти находится в кэше одного или нескольких процессоров, в то время как другой процессор изменяет значение по данному адресу. В этом случае, очевидно, значения, находящиеся в кэшах других процессоров, больше не могут быть использованы и должны быть обновлены. В SMP-архитектурах обычно согласованность данных в кэшах поддерживается аппаратно.
Очевидно, что наличие общей памяти в SMP-архитектурах позволяет эффективно организовать обмен данными между задачами, выполняющимися на разных процессорах, с использованием механизма разделяемой памяти. Однако сложность организации симметричного доступа к памяти и поддержания согласованности кэшей накладывает существенное ограничение на количество процессоров в таких системах – в реальности их число обычно не превышает 32 – в то время, как стоимость таких машин весьма велика. Некоторым компромиссом между масштабируемостью и однородностью доступа к памяти являются NUMA-архитектуры, которые мы рассмотрим далее.
Системы с неоднородным доступом к памяти – NUMA.
Системы с неоднородным доступом к памяти (NUMA17) представляют собой промежуточный класс между системами с общей и распределенной памятью. Память в NUMA-системах является физически распределенной, но логически общедоступной. Это означает, что каждый процессор может адресовать как свою локальную память, так и память, находящуюся на других узлах, однако время доступа к удаленным ячейкам памяти будет в несколько раз больше, нежели время доступа к локальной памяти. Заметим, что единой адресное пространство и доступ к удаленной памяти поддерживаются аппаратно. Обычно аппаратно поддерживается и когерентность (согласованность) кэшей во всей системе
Системы с неоднородным доступом к памяти строятся из однородных базовых модулей, каждый из которых содержит небольшое число процессоров и блок памяти. Модули объединены между собой с помощью высокоскоростного коммутатора. Обычно вся система работает под управлением единой ОС. Поскольку логически программисту предоставляется абстракция общей памяти, то модель программирования, используемая в системах NUMA, обычно в известной степени аналогична той, что используется на симметричных мультипроцессорных системах, и организация межпроцессного взаимодействия опирается на использование разделяемой памяти.
Масштабируемость NUMA-систем ограничивается объемом адресного пространства, возможностями аппаратуры поддержки когерентности кэшей и возможностями операционной системы по управлению большим числом процессоров.
Кластерные системы.
Отдельным подклассом систем с распределенной памятью являются кластерные системы, которые представляют собой некоторый аналог массивно-параллельных систем, в котором в качестве ВУ выступают обычные рабочие станции общего назначения, причем иногда узлы кластера могут даже одновременно использоваться в качестве пользовательских рабочих станций. Кластер, объединяющий компьютеры разной мощности или разной архитектуры, называют гетерогенным (неоднородным). Для связи узлов используется одна из стандартных сетевых технологий, например, Fast Ethernet.
Главными преимуществами кластерных систем, благодаря которым они приобретают все большую популярность, являются их относительная дешевизна, возможность масштабирования и возможность использования при построении кластера тех вычислительных мощностей, которые уже имеются в распоряжении той или иной организации.
При программировании для кластерных систем, как и для других систем с распределенной памятью, используется модель передачи сообщений.
7.2.2Модель программирования MPI.
Как мы видим, при написании программ для параллельных архитектур выбор модели программирования сильно зависит от конкретной архитектуры, на которой предполагается выполнять программу. Например, если целевой архитектурой является система с общей памятью, то для обмена данными между процессами целесообразно использовать механизм разделяемой памяти, если же программа пишется для работы на системе с распределенной памятью, то необходимо организовывать обмен с помощью сообщений. Таким образом, если программист имеет возможность доступа к системе с общей памятью и с распределенной памятью, ему придется создавать отдельную версии своей программы для работы на каждой из этих систем, осваивая при этом различные модели программирования.
В то же время, хотелось бы иметь некоторый единый механизм взаимодействия, который был бы реализован, и притом эффективно, для большинства или хотя бы для многих конкретных параллельных систем. В таком случае для перенесения программы с одной архитектуры на другую было бы достаточно простой перекомпиляции, а программист, освоивший данное средство, получил бы возможность создавать эффективные программы для широкого класса параллельных архитектур. Одним из таких широко распространенных средств параллельного программирования является MPI.
MPI представляет собой стандарт, описывающий некоторое множество функций для обмена сообщениями между параллельными процессами. Существует множество реализаций MPI для различных параллельных архитектур, как с распределенной, так и с общей памятью. Как правило, эти реализации оформлены в виде набора библиотечных функций, которые можно использовать при программировании на языках Фортран и Си.
В модели программирования MPI приложение представляет собой совокупность процессов или нитей (иначе называемых ветвями), общающихся друг с другом путем передачи сообщений. При этом для организации обмена не является важным, будут ли процессы исполняться на одном процессоре или вычислительном узле или на разных – механизм обмена данными в обоих случаях одинаков. Во всем, что не касается передачи и приема сообщений, ветви являются независимыми и изолированными друг от друга. Отметим, что ветви приложения могут обмениваться сообщениями в среде MPI только между собой, но не с процессами других приложений, исполняющимися в той же вычислительной системе. Помимо функций для обмена сообщениями, MPI предоставляет возможности для взаимной синхронизации процессов и для решения ряда других вспомогательных задач.
Количество ветвей в данном приложении задается в момент его запуска, т.е. не существует возможности порождать ветви динамически во время исполнения приложения18. Запуск MPI-приложения осуществляется с помощью специальной программы (чаще всего она называется mpirun), которой обычно указывается количество ветвей, которые необходимо породить, имя исполняемого файла, а также входные параметры приложения.
При написании программы с использованием MPI ее исходный текст должен содержать код для всех ветвей сразу, однако во время исполнения у каждой ветви имеется возможность определить свой собственный порядковый номер и общее количество ветвей и в зависимости от этого исполнять ту или иную часть алгоритма (данный подход в чем-то аналогичен использованию системного вызова fork())
7.2.3 Функции общего назначения. Общая структура программы.
Все без исключения функции Си-библиотеки MPI возвращают целое значение, описывающее код завершения операции. Существует специальная константа MPI_SUCCESS, соответствующая успешному завершению функции. В случае неудачного завершения возвращаемое значение будет отлично от MPI_SUCCESS, и равно одному из кодов ошибок, определенных в MPI и описывающих различные исключительные ситуации.
Все типы данных, константы и функции, относящиеся к Си-библиотеке MPI, описаны в заголовочном файле <mpi.h>.
Коммуникаторы и группы.
Важными понятиями MPI являются группы ветвей и коммуникаторы. Группа ветвей представляет собой упорядоченное множество ветвей. Каждой ветви в группе назначается уникальный порядковый номер – целое число в диапазоне [0, N-1], где N – количество ветвей в группе.
При запуске приложения все его порожденные ветви образуют начальную группу. Библиотека MPI предоставляет функции, позволяющие создавать новые группы на основе существующих путем их разбиения, объединения, исключения ветвей из групп и т.п. операций.
С каждой группой ветвей MPI связан так называемый «коммуникационный контекст», или «коммуникационное поле», задающее множество всех возможных участников операций обмена данными и уникальным образом описывающее каждого участника. Кроме того, коммуникационный контекст может использоваться для хранения некоторых общих для всех ветвей данной группы данных. Для описания коммуникационного контекста в MPI служат объекты специального типа – коммуникаторы. Коммуникатор, или описатель коммуникационного контекста, присутствует в качестве параметра во всех вызовах MPI, связанных с обменом данными. Подчеркнем, что любая операция обмена данными может быть осуществлена только внутри определенного коммуникационного контекста, поэтому, например, сообщение, отправленное с помощью какого-либо коммуникатора, может быть получено только с помощью этого же самого коммуникатора.
Коммуникаторы MPI описываются специальным типом данных MPI_Comm. При запуске приложения сразу после инициализации среды выполнения MPI каждой ветви становятся доступны два предопределенных коммуникатора, обозначаемых константами 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);