$ mpirun -np 16 /trap/search_n_rescue
// Листинг 9.5. Реализация MPI-задачами простого
// поиска и имитации спасения поврежденных
// объектов
template<T> bool travel(vehicle *Transport,
set<T> Location, T Object)
{
//.. .
Transport->startEngine(); Transport->moveForward(XDegrees); Transport->turnLeft(YDegrees); //.. .
if (Location.find(Transport->location() == Object){ // . .. rescue()
}
//.. .
}
int main(int argc, char *argv[])
326 Глава 9. Реализация моделей SPMD и MPMD с помощью шаблонов..
{
//...
int Tag = 2; int WorldSize; int TaskRank; MPI_Status Status; MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &TaskRank); MPI_Comm_size(MPI_COMM_WORLD, &WorldSize); //. . .
switch(TaskRank) {
case 1: {
//. . .
car * Car;
set<streets> SearchSpace
travel<streets>(Car, SearchSpace,Street); //.. .
}
break;
case 2:
{
//.. .
helicopter *BlueThunder; set<air_space> NationalAirSpace; travel<air_space>(BlueThunder,
NationalAirSpace,
AirSpace);
//.. .
}
//case n: //. . .
}
}
Программа search_n_rescue будет запущена в 16 процессах, причем все процессы потенциально могут выполняться на различных процессорах, а все процессоры — находиться на различных компьютерах. Несмотря на то что все процессы выполняют один и тот же код, их действия могут радикально различаться (как и данные, с которыми они работают). Шаблоны и полиморфизм позволяют отличать одну MPI-задалу от другой (а значит, и данные, которые они будут использовать). Обратите внимание на то, что в листи н ге 9.5 МРI - процесс, у которого TaskRank = 1, будет использовать объект класca Car и контейнер, содержа щ ий streets - объекты . MPI-процесс, у которо г о TaskRank = 2, будет использовать объект класса helicopter и аir_spасе объекты. Обе задачи вызывают шаблонную функцию travel (). Поскольку шаблоннал функция travel () манилулирует указателями на класс vehicle, она может воспользоваться преимущест-вами полиморфизма и выполнять операции с потомками класса vehicle. Это означает, что, хотя все MPI-задачи вызывают одну и гу же функцию travel (), действия, вы полняемые этой функцией, рааличны. Обратите внимание на то, что в функции travel () нет инструкций case или if, которые бы пытались идентифицировать тип vehicle -объекта, с которым она работает. Конкретный vehicle -объект определяется типом, на который используется указатель. Это MPI-приложение может работать потенциально с 16 различными транспортными средствами, каждое из которых характеризуется собственным типом мобильности и областью поиска. Существуют и другие методы, которые можно использовать для реализации модели MPMD в среде MPI, но полиморфический подход обычно требует меньшего объема кода.
Основные два типа полиморфизма, которые мы здесь демонстрируем, — это полиморфизм динамического связывания, по д держиваемый наследованием и виртуальными методами, и параметрический полиморфизм, по д держиваемый шаблонами. Функция travel () в листин г е 9.5 использует оба типа полиморфизма. Полиморфизм, основанный на наследовании, характеризует параметр vehicle *Transport, а параметрический полиморфизм — параметры set<T> и T Object. Параметрический полиморфизм представляет собой механизм, благодаря которому один и тот же код используется для различных типов, передаваемых в качестве параметров. Различные типы полиморфизма, которые позволяют упростить МРТзадачи и сократить код, необходимый для реализации MPI-программы, перечислены в табл. 9.2.
Таблица 9.2. Различные типы полиморфизма, которые можно использовать для упрощения МРI-задач
Типы полиморфизма
Механизмы
Описание
Динамический
Наследование и виртуальные методы
Вся информация, необходимая для определения того, какие виртуальные методы будет вызывать функция, неизвестна до выполнения программы
Параметрический
Шаблоны
Механизм, в котором один и тот же код используется для различных типов, которые передаются как параметры
Введение MPMD-модели c помощью функций -объектов
Функции-объекты используются в стандартных алгоритмах для реализации горизонтального полиморфизма. Полиморфизм, реализованный с помощью передачи параметра vehicle *Transport в листинге 9.5, является вертикальным, поскольку для функционирования необходимо, чтобы классы были связаны наследованием. При горизонтальном полиморфизме классы связаны не наследованием, а интерфейсом. Все функции-объекты определяют операторную функцию operator (). Функции-объекты позволяют разрабатывать MPI-задачи с использованием некоторой общей формы.
// Функция-объект class some_class{ //.. .
operator(); //
};
template<class T> T mpiTask(T X) {
//
Т Result; Result = X() //. . .
}
Шаблонная функция mpiTask () будет работать с любым типом T, который имеет соответствующим образом определенную функцию operator ().
//. . .
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &TaskRank); MPI_Comm_size(MPI_COMM_WORLD, &WorldSize); //. . .
if(TaskRank == 0){ //. . .
user_defined_type M; mpiTask(M); //.. .
}
if(TaskRank == N){ //.. .
some_other_userdefined_type N; mpiTask (N) ;
}
//----
Этот горизонтальный полиморфизм не имеет отношения к наследованию или виртуальным функциям. Поэтому, если наша MPI-задача получит свой ранг, а затем объявит тип объекта, в котором определена функция operator (), то при вызове функции mpiTask () ее поведение будет продиктовано содержимым метода operator (). Тогда, несмотря на идентичность всех процессов, запу щ енных посредством сценария mpirun, полиморфизм шаблонов и функций-объектов позволит всем MPI-задачам выполнять различную работу над различными данными.
Как упростить взаимодействие между MPI-задачами
Помимо упрощения и сокращения размеров кода МРТзадачи с помощью полиморфизма и шаблонов, мы можем также упростить взаимодействие между MPI-задачами, воспользовавшись преимуществами перегрузки операторов. Функции MPI_Send () и MPI_Recv () имеют следующий формат:
MPI_Send(Buffer, Count, MPI_LONG, TaskRank, Tag, Comm);
MPI_Recv(Buffer,Count,MPI_INT, TaskRank, Tag, Comm, &Status);
При вызове этих функций необходимо, чтобы пользователь указал тип применяемых здесь данных и буфер, предназначенный для хранения посылаемых или принимаемых данных. Спецификация типа посылаемых или принимаемых данных может иметь довольно громоздкий вид и чревата последующими ошибками при передаче неверного типа. В табл. 9.3 приведены прототипы MPI-функций отправки и приема данных и их краткое описание.
Таблица 9.3 Прототипы MPI-функций отправки и приема данных
ФункцииОписание
#include «mpi.h»
int MPI_Send (void *Buffer,int Count, MPI_Datatype Туре, int Destination, int MessageTag, MPI_Comm Comm) ; Выполняет базовую отправку данных
int MPI_Send_init (void *Buffer,int Count, MPI_Datatype Type, int Destination, int MessageTag, MPI_Comm Comm, MPI_Request *Request); Инициализирует дескриптор для стандартной отправки данных