|
Создание заготовки для параллельной программы
Процесс выполнения задания с применением задачника Programming
Taskbook обычно начинается с создания проекта-заготовки для выбранного
задания. Особенно удобно использовать такую заготовку для заданий по
параллельному программированию, поскольку в нее уже будут входить
важные фрагменты кода, необходимые при выполнении любой
параллельной программы. Для создания заготовки воспользуемся
программным модулем PT4Load, входящим в состав задачника. Вызвать
этот модуль можно с помощью ярлыка Load.lnk, который
автоматически создается в рабочем каталоге учащегося.
При этом на экране появится окно модуля PT4Load.
Так выглядит окно, если текущей программной средой задачника
является среда Free Pascal Lazarus. Для изменения текущей среды
достаточно выполнить в окне щелчок правой кнопкой мыши и выбрать из
появившегося контекстного меню новую среду.
Обратите внимание на группы MPIBegin и MPIDebug, указанные в списке
доступных групп заданий. Их наличие означает, что к базовому варианту задачника
Programming Taskbook подключено его расширение: задачник по параллельному
программированию Programming Taskbook for MPI. Заметим, что если выбрать
программную среду, не связанную с языками Pascal или C++ (например,
Microsoft Visual C# любой версии), то группы MPIBegin и MPIDebug
в списке будут отсутствовать.
Определимся с выбором среды программирования, после чего введем
в поле «Задание» текст MPIBegin1 . В результате кнопка
«Загрузка» станет доступной и, нажав ее (или клавишу
[Enter]), мы создадим заготовку для указанного задания, которая
будет немедленно загружена в выбранную программную среду.
В случае использования языка Pascal для среды Lazarus в нее будет
загружен файл MPIBegin1.lpr, содержащий следующий текст:
[Pascal]
program MPIBegin1;
uses PT4, MPI;
var
flag, size, rank: integer;
begin
Task('MPIBegin1');
MPI_Initialized(flag);
if flag = 0 then exit;
MPI_Comm_size(MPI_COMM_WORLD, size);
MPI_Comm_rank(MPI_COMM_WORLD, rank);
end.
Файл с таким же содержанием будет создан и при использовании среды Borland Delphi;
изменится только расширение файла: dpr.
Если же используется язык C++ для среды Visual Studio или Code::Blocks, то будет
создан и загружен в эту среду файл MPIBegin1.cpp, содержащий следующий текст:
[C++]
#include "pt4.h"
#include "mpi.h"
void Solve()
{
Task("MPIBegin1");
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);
}
Программа-заготовка для заданий по параллельному
программированию содержит дополнительные операторы, отсутствующие
в заготовках для «непараллельных» заданий. Эти операторы
должны использоваться практически в любой параллельной MPI-программе,
поэтому, чтобы не вынуждать учащегося набирать их каждый
раз заново, они автоматически добавляются к программе при ее создании.
Обсудим операторы программы-заготовки подробнее. Первым
оператором является оператор вызова процедуры Task,
инициализирующей требуемое задание. Этот оператор
имеется в программах-заготовках для всех заданий, в том числе и не
связанных с параллельным программированием. Заметим, что процедура
Task реализована в ядре задачника Programming Taskbook (динамической
библиотеке) и доступна из программы учащегося благодаря
подключенному к ней модулю PT4.pas (для Pascal) или заголовочному
файлу pt4.h (для C++). Помимо заголовочного файла pt4.h в рабочем
каталоге учащегося должен находиться файл pt4.cpp, содержащий
определения функций, объявленных в файле pt4.h.
Оставшиеся операторы связаны с библиотекой MPI. Задачник
использует библиотеку MPI, входящую в систему MPICH широко
распространенную бесплатную программную реализацию стандарта MPI
для различных операционных систем, в том числе и для Windows.
Функции и константы библиотеки MPI доступны программе благодаря
подключенному к ней модулю MPI.pas (для Pascal) или заголовочному
файлу mpi.h (для C++). Отметим, что реализация функций из файла mpi.h
содержится в файле mpich.lib, который требуется явным образом
подключить к любому проекту на языках С/C++, использующему
библиотеку MPI. Однако в нашем случае это подключение уже
выполнено в ходе создания проекта-заготовки, поэтому
дополнительных действий, связанных с этим подключением, выполнять не
требуется.
Вызов функции MPI_Initialized позволяет определить,
инициализирован для программы параллельный режим или нет. Если
режим инициализирован, то выходной параметр функции принимает
значение, отличное от нуля; в противном случае параметр полагается
равным нулю. Следует отметить, что инициализация параллельного
режима выполняется функцией MPI_Init, которая в приведенном коде
отсутствует. Это объясняется тем, что за инициализацию отвечает сам
задачник, и выполняется она перед тем как программа переходит к
выполнению кода учащегося. Однако такая инициализация выполняется
задачником не всегда. Например, если программа запущена в демо-режиме
(для этого достаточно при вызове процедуры Task дополнить имя задания
символом «?»: Task("MPIBegin1?") ), задачник не выполняет
инициализацию параллельного режима, поскольку в нем нет
необходимости. В этой ситуации вызов в коде учащегося функций MPI
(отличных от MPI_Initialized) может привести к некорректной работе
программы. Вызов функции MPI_Initialized и следующий за ним условный
оператор позволяют «пропустить» при выполнении
программы все операторы, введенные учащимся, если программа запущена
не в параллельном режиме.
Два последних оператора программы позволяют определить две
характеристики, необходимые для нормальной работы любого процесса
любой содержательной параллельной программы: общее количество
процессов (функция MPI_Comm_size) и ранг текущего процесса (функция
MPI_Comm_rank). Текущим считается процесс, вызвавший данную
функцию. Требуемая характеристика возвращается во втором (выходном)
параметре соответствующей функции; первым параметром является
коммуникатор, задающий набор процессов. Благодаря вызову этих
функций мы можем сразу использовать в нашей программе значения size
(общее число процессов в коммуникаторе MPI_COMM_WORLD) и rank
(ранг текущего процесса в коммуникаторе MPI_COMM_WORLD;
значение ранга обязательно лежит в диапазоне от 0 до
size 1). Обратите внимание на то, что в варианте для
языка C++ выходные параметры являются указателями на
соответствующие переменные, тогда как в варианте для языка Pascal эти
параметры являются самими переменными, передаваемыми по ссылке (так
называемые var-параметры).
|