Создание заготовки для параллельной программы
Процесс выполнения задания с применением задачника PT for MPI-2
начинается с создания заготовки проекта для выбранного задания. К этому
проекту уже будут подключены все необходимые библиотеки (связанные с
задачником и с выбранной системой MPICH), кроме того, основной файл
этого проекта будет содержать важные фрагменты кода, необходимые при
выполнении любой параллельной программы.
Для создания заготовки предназначен программный модуль PT4Load,
входящий в состав задачника. Проще всего вызвать этот модуль с
помощью ярлыка Load.lnk, который автоматически создается в рабочем
каталоге учащегося (по умолчанию рабочий каталог имеет имя PT4Work и
находится на диске C). После запуска модуля PT4Load на экране появится его окно:
Так выглядит окно, если текущей программной средой задачника
является среда Microsoft Visual Studio 2015 для языка C++. Для изменения
текущей среды достаточно выполнить в окне щелчок правой кнопкой
мыши (или нажать кнопку или клавишу Shift+F10) и выбрать из
появившегося контекстного меню новую среду (например «Code::Blocks
(C++)»; при этом в заголовке окна появится название выбранной среды).
Вид контекстного меню приведен на следующем рисунке:
Кроме списка доступных
сред контекстное меню содержит список доступных вариантов системы MPICH (с
указанием текущего варианта), позволяет выбрать язык интерфейса
(русский или английский), а также выполнить ряд дополнительных
действий по настройке рабочего каталога.
Обратите внимание на группы заданий, начинающиеся с префикса MPI
(MPI1Proc и т. д.). Они появятся в списке только после установки
задачника по параллельному программированию PT for MPI-2 и только в том случае,
если в качестве текущей среды программирования выбрана среда для языка C++.
Определимся с выбором среды программирования, после чего введем
в поле «Задание» текст MPI1Proc2 (заметим, что
полное имя группы вводить не
обязательно; достаточно ввести текст MPI1, однозначно определяющий
группу, после чего нажать пробел и указать номер задания 2). В результате
кнопка «Загрузка» станет доступной; кроме того, в нижней части окна
будет приведено краткое описание выбранной группы и количество
входящих в нее заданий:
Нажав кнопку «Загрузка» или клавишу Enter, мы создадим заготовку
для указанного задания, которая будет немедленно загружена в выбранную
программную среду.
Проект, созданный задачником для языка C++, всегда имеет имя ptprj;
это позволяет, в частности, существенно уменьшить количество файлов,
создаваемых в рабочем каталоге при выполнении большого количества
различных заданий. Он включает ряд файлов, основным из которых
является cpp-файл, имя которого совпадает с именем выполняемого
задания (в нашем случае MPI1Proc2.cpp). Этот файл автоматически
загружается в редактор кода среды программирования; именно в нем
необходимо ввести решение задачи. Приведем текст файла MPI1Proc2.cpp:
#include "pt4.h"
#include "mpi.h"
void Solve()
{
Task("MPI1Proc2");
int flag;
MPI_Initialized(&flag);
if (flag == 0)
return;
int rank, size;
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
}
В начале программы содержатся директивы подключения к ней
вспомогательных заголовочных файлов pt4.h и mpi.h. Затем
располагается функция Solve, которая должна содержать решение задачи.
При анализе файла MPI1Proc2.cpp возникает естественный вопрос: где
находится «стартовая» функция приложения (обычно имеющая имя main
или WinMain)? Данная функция размещается в другом файле проекта,
поскольку ее содержимое не требует редактирования. В ней производится
инициализация задачника, после чего происходит вызов функции Solve с
решением, при необходимости перехватываются исключения, которые
могут возникнуть при выполнении функции Solve, и в конце выполняются
завершающие действия, связанные с анализом полученного решения.
Программа-заготовка для заданий по параллельному
программированию содержит дополнительные операторы, отсутствующие
в заготовках для «непараллельных» заданий. Эти операторы должны
использоваться практически в любой параллельной MPI-программе,
поэтому, чтобы учащемуся не требовалось набирать их каждый раз заново,
они автоматически добавляются к программе при ее создании.
Обсудим содержимое функции Solve подробнее. Первым ее
оператором является оператор вызова функции Task, инициализирующей
требуемое задание. Этот оператор имеется в программах-заготовках
для всех заданий, в том числе и не связанных с параллельным
программированием. Функция Task реализована в ядре задачника
Programming Taskbook (динамической библиотеке) и доступна в программе
учащегося благодаря подключенному к ней заголовочному файлу pt4.h.
Помимо заголовочного файла pt4.h в рабочем каталоге учащегося должен
находиться файл pt4.cpp, содержащий определения функций, объявленных
в файле pt4.h.
Оставшиеся операторы функции Solve связаны с библиотекой MPI. Мы
уже отмечали, что задачник использует библиотеку MPI, входящую в
систему MPICH широко распространенную бесплатную программную
реализацию стандарта MPI для различных операционных систем, в том
числе и для Windows. Функции и константы библиотеки MPI доступны
программе благодаря подключенному к ней заголовочному файлу mpi.h.
Реализация функций из файла mpi.h содержится в объектном файле
mpich.lib, который требуется подключать к любому проекту на языках
С/C++, использующему библиотеку MPI. Однако в нашем случае это
подключение уже выполнено в ходе создания проекта-заготовки, поэтому
дополнительных действий, связанных с этим подключением, выполнять не
требуется.
Вызов функции MPI_Initialized позволяет определить,
инициализирован для программы параллельный режим или нет. Если
режим инициализирован, то выходной параметр функции принимает
значение, отличное от нуля; в противном случае параметр полагается
равным нулю. Следует отметить, что инициализация параллельного режима
выполняется функцией MPI_Init, которая в приведенном коде отсутствует.
Это объясняется тем, что за инициализацию отвечает сам задачник, и
выполняется она перед тем, как программа переходит к выполнению кода
учащегося. Однако такая инициализация выполняется задачником не
всегда. Например, если программа запущена в демо-режиме (для этого
достаточно при вызове функции Task дополнить имя задания символом
«?»: Task("MPI1Proc2?") ), задачник не выполняет инициализацию
параллельного режима, поскольку в нем нет необходимости. В этой
ситуации вызов в коде учащегося функций MPI (отличных от
MPI_Initialized) может привести к некорректной работе программы. Вызов
функции MPI_Initialized и следующий за ним условный оператор предназначены для того, чтобы
«пропустить» при выполнении программы все операторы, введенные
учащимся, если программа запущена не в параллельном режиме.
Два последних оператора программы позволяют определить две
характеристики, необходимые для нормальной работы любого процесса
любой содержательной параллельной программы: общее количество
процессов (функция MPI_Comm_size) и ранг текущего процесса (функция
MPI_Comm_rank). Текущим считается процесс, вызвавший данную
функцию. Требуемая характеристика возвращается во втором
параметре соответствующей функции; первым параметром является
коммуникатор, задающий набор процессов. Благодаря вызову этих
функций мы можем сразу использовать в нашей программе значения size
(общее число процессов в коммуникаторе MPI_COMM_WORLD) и rank
(ранг текущего процесса в коммуникаторе MPI_COMM_WORLD; значение
ранга обязательно лежит в диапазоне от 0 до size 1). Обратите внимание
на то, что вторые параметры этих функций являются указателями на соответствующие
переменные.
|