Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Практикум по параллельному программированию с использованием электронного задачника Programming Taskbook for MPI
.pdf
91
Count означает, что никакие дополнительные действия, связанные с инициализацией учебного задания, в данном процессе выполнять не требуется.
Отметим, что нулевое значение параметра ProcessCount возвращается
также в экземпляре непараллельной программы, выполняющей роль загрузчика. В самом деле, единственная «обязанность» этой программы состоит в определении числа процессов и использовании этого числа в качестве параметра приложения MPIRun.exe, запускающего параллельный вариант программы. Поэтому в программе-загрузчике, как и в программе,
выполняемой в качестве подчиненного процесса параллельного приложения, действия по инициализации задания должны состоять только в вызове
процедуры CreateTask.
Наконец, следует упомянуть демонстрационный вариант программы с
учебным заданием, который всегда выполняется в непараллельном режиме. При демонстрационном запуске программа выступает не в роли загрузчика, а в роли обычной непараллельной программы, в которой задание
инициализируется и отображается в окне задачника. В этой ситуации выходное значение параметра ProcessCount всегда совпадает с его входным
значением.
Все приведенные выше сведения, связанные с параметром ProcessCount, представлены в таблице 1.
Таблица 1
Особенности использования параметра ProcessCount
«Роль» программы
Входное значение
параметра
ProcessCount
Выходное значение
параметра
ProcessCount
Непараллельная программа-загрузчик,
обеспечивающая запуск
параллельного варианта
программы
Используется: определяет число процессов при запуске
параллельного варианта программы
Всегда равно 0
Главный процесс параллельной программы
(процесс ранга 0)
Не используется Равно числу процессов
в параллельной программе; используется
при формировании
входных и выходных
данных
Подчиненный процесс
параллельной программы
Не используется Всегда равно 0
Непараллельная программа, обеспечивающая
демонстрационный
запуск учебного задания
Используется Всегда равно входному
значению; используется
при формировании
входных и выходных
данных

92 Часть II. Разработка новых учебных заданий
5.2. Процедура SetProcess
procedure SetProcess(ProcessRank: integer);
Данная процедура устанавливает в качестве текущего процесса параллельного приложения процесс ранга ProcessRank. Все числовые исходные и контрольные данные связываются с текущим процессом. До первого
вызова данной процедуры текущим процессом считается процесс ранга 0.
Процедуру можно вызывать несколько раз с одним и тем же параметром
(например, первый раз процесс делается текущим при определении связанных с ним исходных данных, а второй раз — при определении его контрольных данных).
Если какой-либо элемент исходных или контрольных данных связан с
процессом ранга ProcessRank, то ввести или, соответственно, вывести этот
элемент при выполнении задания можно будет только в этом процессе.
Порядок ввода и вывода элементов в каждом процессе определяется, как
обычно, порядком вызова соответствующих процедур групп Data и Result.
Для повышения наглядности следует всегда указывать в окне задачника
комментарий вида «Процесс N:» перед теми исходными или результирующими данными, которые связаны с процессом ранга N.
Параметр ProcessRank должен принимать значения в диапазоне от 0
до K – 1, где K — количество процессов, возвращаемое параметром
ProcessCount процедуры CreateTask. При нарушении этого условия выводится сообщение об ошибке «Параметр процедуры SetProcess находится
вне диапазона 0..K–1, где K — количество процессов». Особое сообщение
об ошибке будет выведено при попытке вызвать процедуру SetProcess при
выполнении подчиненного процесса параллельного приложения или непараллельной программы-загрузчика (напомним, что в этих режимах работы
программы процедура CreateTask возвращает в параметре ProcessCount
значение 0, свидетельствующее о том, что выполнять дальнейшие действия по инициализации задания не требуется). В этом случае текст сообщения будет следующим: «В ситуации, когда параметр ProcessCount проце-
дуры CreateTask вернул значение 0, не выполнен немедленный выход из
процедуры инициализации задания».
Процедуру SetProcess можно вызывать и при формировании задания,
не связанного с параллельным программированием. В подобной ситуации
количество процессов всегда равно 1, допустимым значением параметра
ProcessRank является только 0, и вызов процедуры SetProcess(0) не выполняет никаких действий.
Следует подчеркнуть, что с текущим процессом связываются только
числовые данные. Данные прочих простых типов (логические, символьные,
строковые, данные-указатели), а также все «внешние» данные (файлы и
динамические структуры) остаются связанными с главным процессом параллельного приложения. Это ограничение объясняется тем, что, вопервых, основной областью применения параллельного программирования
являются численные методы, и, во-вторых, для освоения возможностей

93
библиотеки MPI вполне достаточно использования числовых данных
(включая числовые массивы и структуры). Пересылка между процессами
строковых данных может приводить к проблемам, обусловленным различиями в строковых форматах для разных языков программирования, а использование в заданиях по параллельному программированию файлов и
динамических структур данных (и связанных с ними указателей) не представляется оправданным. Впрочем, все подобные данные допустимо использовать в заданиях по параллельному программированию, однако их
ввод и вывод можно выполнять только в главном процессе.
5.3. Особенности использования других процедур
конструктора учебных заданий
Процедура SetRequiredDataCount
При выполнении заданий по параллельному программированию во
всех процессах параллельной программы должны быть введены все связанные с ними исходные данные. Поэтому попытка вызова процедуры
SetRequiredDataCount(N) с параметром N, значение которого меньше общего числа исходных данных, приведет в задании по параллельному программированию к сообщению об ошибке.
Процедуры групп Data и Result
При разработке заданий по параллельному программированию может
оказаться недостаточно пяти экранных строк, отводимых в окне задачника
для раздела исходных или результирующих данных. В некоторых ситуациях отмеченную проблему можно решить, размещая информацию в виде
нескольких столбцов данных. Однако если данные для каждого процесса
организованы сложным образом (например, представляют собой двумерный массив-матрицу), их не удастся разместить на пяти экранных строках
даже при использовании нескольких столбцов. Для решения этой проблемы в конструктор учебных заданий для задачника Programming Taskbook
версии 4.9 была добавлена возможность прокрутки содержимого областей
исходных и результирующих данных в окне задачника.
Заметим, что в предыдущей версии конструктора уже была реализована прокрутка содержимого двоичных и текстовых файлов, а также (при
необходимости) прокрутка бинарных деревьев. Кроме того, прокрутку
можно было использовать в разделе формулировки задания; для этого было достаточно при добавлении завершающего текста формулировки, который не умещается в первых 5 экранных строках, указывать в качестве параметра Y процедуры TaskText особое значение 0.
Подход, использованный в процедуре TaskText, нельзя распространить на процедуры, отвечающие за формирование разделов исходных и результирующих данных, поскольку в них на одной экранной строке могут
располагаться несколько элементов данных, а также комментарии к ним.

94 Часть II. Разработка новых учебных заданий
Поэтому в новой версии конструктора разрешено для процедур групп Data
и Result, в том числе DataComment и ResultComment, указывать в качестве
параметра Y значение, превышающее 5 (в предыдущей версии конструктора использование такого значения привело бы к сообщению об ошибке).
Если значение параметра Y для некоторого элемента раздела исходных
данных превышает 5, то этот элемент размещается в строке с указанным
номером, а в разделе исходных данных становится доступной прокрутка.
Аналогичным образом, прокрутка будет доступна в разделе результатов,
если хотя бы один элемент этого раздела помещен в него процедурой с параметром Y, превышающим 5. Если оба раздела допускают прокрутку, то
она выполняется независимо. Прокрутку в любом разделе можно выполнять с помощью клавиатуры или мыши; в последнем случае следует использовать кнопки
, расположенные справа от прокручиваемого разде-
ла.
Если раздел данных содержит отдельный элемент, допускающий прокрутку (например, двоичный файл), то команды прокрутки этого элемента
и раздела в целом могут конфликтовать друг с другом. Кроме того, при
отображении в прокручиваемом разделе элементов, занимающих более одной экранной строки (например, динамических структур), восприятие этих
элементов может быть затруднено. Чтобы избежать отмеченных проблем,
в задании запрещено использовать прокрутку раздела, если в нем уже имеется «внешний» объект (файл или динамическая структура). Если делается
попытка вызвать какую-либо процедуру с параметром Y, большим 5, для
раздела, уже содержащего внешний объект, то выводится сообщение об
ошибке «При наличии внешних объектов режим прокрутки для всего раз-
дела недоступен». Если же в разделе, уже имеющем элементы данных или
комментарии, размещенные в неотображаемых строках, делается попытка
разместить внешний объект, то выводится сообщение об ошибке «Раздел
данных в режиме прокрутки не может содержать внешние объекты».
Чтобы проиллюстрировать новую возможность задачника, связанную
с прокруткой разделов данных, мы использовали ее при реализации
третьего задания демонстрационной группы MPIDemo (см. п. 6.4).
Разумеется, прокрутка разделов данных в новой версии конструктора
PT4TaskMaker доступна и для заданий, не связанных с параллельным программированием. В частности, она может оказаться полезной в заданиях,
требующих использования в качестве исходных данных нескольких двумерных массивов (например, в задании о нахождении произведения матриц) или массива символьных строк.
6. Примеры разработки заданий по параллельному
программированию
В данном разделе приводятся примеры разработки заданий по параллельному программированию с использованием конструктора учебных за-

95
даний PT4TaskMaker. Основное внимание уделяется новым возможностям
конструктора, связанным с параллельным программированием, однако
кратко описываются и базовые возможности конструктора (подробное
описание этих возможностей содержится в [1]).
6.1. Создание новой группы учебных заданий
Любая группа заданий должна оформляться в виде динамической
библиотеки — dll-файла (другой вариант оформления группы заданий — в
виде модуля среды PascalABC.NET — описывается в п. 6.6). Будем разрабатывать библиотеку с новой группой заданий в среде Borland Delphi. Назовем новую группу MPIDemo (в качестве префикса имени группы, связанной с MPI-программированием, желательно указать текст «MPI», поскольку при наличии такого префикса в имени заданий модуль PT4Load
будет создавать для этих заданий специализированные проекты-заготовки,
предназначенные для выполнения задания в параллельном режиме — см.
п. 2.2). Следуя правилам именования dll-файлов с группами заданий (см.
[1]), дополним имя проекта префиксом PT4. Таким образом, имя нашего
проекта примет вид PT4MPIDemo.
Создаваемый нами текст библиотеки должен включать ряд стандартных элементов. Перечислим эти элементы:
• директива uses, подключающая модуль PT4TaskMaker;
• основная процедура группы заданий, определяющая задание по его
номеру (обычно она имеет имя InitTask); данная процедура должна
иметь один параметр целого типа и снабжаться модификатором
stdcall;
• процедура инициализации группы заданий, которая должна иметь
фиксированное имя inittaskgroup (все буквы строчные) и включаться в список exports процедур, экспортируемых данной библиотекой.
В процедуре inittaskgroup должна вызываться процедура CreateGroup,
в которой задаются настройки создаваемой группы: имя группы, ее описание, сведения об авторе, строковый ключ (последовательность произвольных символов), число заданий и имя основной процедуры группы.
По умолчанию группа считается доступной для всех языков программирования, поддерживаемых задачником Programming Taskbook. Поскольку задания по параллельному программированию в текущей версии задачника PT4 for MPI могут выполняться только для языков Паскаль и C++,
необходимо добавить в процедуру inittaskgroup фрагмент, позволяющий
сделать группу доступной только для указанных языков.
Кроме того, целесообразно явным образом установить для создаваемого проекта режимы проверки диапазона {$R+} и поддержки ANSI-строк
(«длинных строк») {$H+}.
С учетом всех перечисленных требований заготовка нашего проекта
примет следующий вид:

96 Часть II. Разработка новых учебных заданий
{$R+,H+}
library PT4MPIDemo;
uses PT4TaskMaker;
procedure InitTask(num: integer); stdcall;
begin
end;
procedure inittaskgroup;
begin
if CurrentLanguage and (lgPascal or lgCPP) = 0 then exit;
CreateGroup('MPIDemo',
'Примеры задач по параллельному MPI-программированию',
'М. Э. Абрамян, 2010', 'sddwertfghklbfdgfgd', 1, InitTask);
end;
exports inittaskgroup;
begin
end.
Условный оператор в процедуре inittaskgroup обеспечивает немедленный выход из этой процедуры (без выполнения инициализации группы заданий) в случае, если текущий язык не является Паскалем или C++.
Заметим, что почти такая же заготовка может использоваться при разработке библиотеки с новой группой заданий в среде Free Pascal Lazarus.
В текст заготовки следует внести лишь два добавления. Во-первых, надо
добавить к списку директив компилятора директиву, обеспечивающую режим совместимости с Delphi:
{$mode delphi}
Во-вторых, в список exports необходимо добавить процедуру activate
(описывать эту процедуру не требуется, так как она определена в модуле
PT4TaskMaker):
exports inittaskgroup, activate;
Кроме того, главный файл проекта, разрабатываемого в среде Lazarus,
должен иметь расширение lpr (а не dpr, как для среды Delphi).
Поскольку наш проект является библиотекой, а не исполняемым приложением, запустить его на выполнение нельзя. Для тестирования динамических библиотек и в среде Delphi, и в среде Lazarus можно использовать
главное приложение (host application), которое при запуске подключает
библиотеку и тем самым позволяет проверить правильность ее работы.
При тестировании библиотеки с учебными заданиями с качестве главного
приложения удобно использовать программный модуль PT4Demo.exe,
входящий в состав задачника Programming Taskbook и позволяющий отобразить на экране любое задание группы в демонстрационном режиме.

97
С помощью параметров командной строки программу PT4Demo можно настроить на немедленное отображение определенного задания требуемой
группы. Не приводя здесь все возможные варианты этих параметров
(см. [1]), укажем те, которые являются наиболее удобными для тестирования нашей библиотеки:
-gMPIDemo -n999
Первый из параметров определяет используемую группу заданий
(MPIDemo), а второй обеспечивает отображение на экране последнего из
заданий этой группы.
Для определения главного приложения и его параметров в среде Delphi надо выполнить команду меню «Run | Parameters...», перейти в появившемся окне на вкладку «Locals» и указать полное имя главного приложения в поле «Host Application», а требуемые параметры — в поле
«Parameters» (в нашем случае при стандартном размещении задачника
Programming Taskbook имя главного приложения будет следующим:
C:\Program Files\PT4\PT4Demo.exe). Поле «Working Directory» следует ос-
тавить пустым; при этом рабочим каталогом будет считаться тот каталог,
который содержит откомпилированную библиотеку с группой заданий.
Аналогичное окно настроек предусмотрено и в среде Lazarus; для его отображения надо выполнить команду меню «Run | Run Parameters...».
После выполнения указанных действий мы уже можем протестировать заготовку нашей библиотеки. При нажатии [F9] на экране должно
появиться окно задачника с сообщением об ошибке: «Задание MPIDemo1
не реализовано для текущего языка программирования». Это соответствует
действительности, так как пока мы не реализовали ни одного задания.
Примечание. Если окно задачника не появилось, то это означает, что
в каталоге, в котором разрабатывается библиотека с новой группой
заданий, в качестве текущего выбран язык программирования, отлич-
ный от Паскаля и C++. В этом случае надо выбрать в качестве теку-
щей для данного каталога какую-либо из доступных сред Паскаля или
С++, используя для этого программу PT4Setup, входящую в состав за-
дачника Programming Taskbook.
6.2. Разработка простого задания: MPIDemo1
Первое задание, которое мы реализуем, не требует для своего решения пересылки данных.
MPIDemo1. В каждом из процессов, входящих в коммуникатор
MPI_COMM_WORLD, прочесть одно вещественное число и вывести его
противоположное значение.
Тем не менее при реализации этого задания придется использовать все
новые средства конструктора заданий, связанные с параллельным программированием, — «параллельный» вариант процедуры CreateTask и
процедуру SetProcess.

98 Часть II. Разработка новых учебных заданий
Оформим реализацию задания в виде вспомогательной процедуры
MPIDemo1:
procedure MPIDemo1;
var
i, count, y: integer;
d: real;
begin
count := Random(3) + 3;
CreateTask('Процессы и их ранги', count);
if count = 0 then exit;
TaskText('В каждом из процессов, входящих в коммуникатор ' +
'MPI\_COMM\_WORLD,', 0, 2);
TaskText('прочесть одно вещественное число и вывести его ' +
'противоположное значение.', 0, 4);
if count = 5 then
y := 1
else
y := 2;
for i := 0 to count - 1 do
begin
SetProcess(i);
d := Random * 199.99 - 100;
DataR('Процесс ' + Chr(i + 48) + ': ', d, 0, y + i, 6);
ResultR('Процесс ' + Chr(i + 48) + ': ', -d, 0, y + i, 6);
end;
end;
Необходимо также откорректировать процедуру InitTask, включив в
нее вызов процедуры MPIDemo1 (в дальнейшем процедура InitTask будет
дополняться вызовами процедур, реализующих другие задания нашей
группы):
procedure InitTask(num: integer); stdcall;
begin
case num of
1: MPIDemo1;
end;
end;
Теперь при запуске нашего проекта на экране появится окно задачника, подобное приведенному на рисунке 28 (количество процессов может
быть другим).

99
Рис. 28. Демонстрационный запуск задания MPIDemo1
Прокомментируем процедуру MPIDemo1.
Первый оператор процедуры определяет с помощью датчика случайных чисел количество процессов, используемое при текущем запуске задания (предварительного вызова процедуры Randomize не требуется, так как
она уже была вызвана при выполнении процедуры CreateGroup). Возможное число процессов будет лежать в диапазоне от 3 до 5; такой диапазон
выбран для того, чтобы данные для каждого процесса можно было выводить в окне задачника в один столбец, не используя режим прокрутки. Задавать для простых заданий большее число процессов не следует, так как
это лишь увеличит время работы параллельной программы. Необходимо
только, чтобы при различных запусках программы количество процессов
изменялось, пусть и в небольшом диапазоне.
Следующий оператор инициализирует задание, вызывая процедуру
CreateTask. Благодаря указанию количества процессов count в качестве
второго параметра процедуры, задание будет выполняться в параллельном
режиме.
Особое внимание надо обратить на следующий условный оператор:
if count = 0 then exit;
Для того чтобы понять его смысл, необходимо вспомнить механизм
выполнения задания в параллельном режиме (см. п. 4 и 5.1). Экземпляр
программы, запущенной непосредственно из интегрированной среды, является непараллельной программой-загрузчиком, которая определяет требуемое количество процессов и запускает параллельное приложение. После этого никакие действия, связанные с настройкой задания, ей выполнять
не требуется. При выполнении параллельного приложения запускаются
несколько экземпляров исходной программы (по одному экземпляру для
каждого процесса), однако только в главном процессе (процессе ранга 0)
определяются все данные, связанные с заданием; прочие процессы эти

100 Часть II. Разработка новых учебных заданий
данные не формируют, а лишь получают от главного процесса. Таким образом, часть процедуры MPIDemo1, следующую за вызовом процедуры
CreateTask, требуется выполнить только для главного процесса параллельного приложения, причем именно возвращаемое значение параметра count
позволяет проверить, что данный экземпляр программы выполняется в
главном процессе: в этом (и только в этом) случае параметр count отличен
от нуля и, кроме того, равен числу запущенных процессов параллельного
приложения. Для непараллельной программы-загрузчика и программ, запущенных в качестве подчиненных процессов параллельного приложения,
параметр count вернет значение 0, означающее, что дальнейшие действия,
определенные в процедуре MPIDemo1, выполнять не требуется. Входное
значение параметра count в процессах параллельного приложения не используется; оно учитывается только в программе-загрузчике при запуске
программы в параллельном режиме, а также при демо-запуске задания.
Программа, выполняющая демо-запуск задания, является непараллельной, как и программа-загрузчик (именно в таком непараллельном режиме работает, в частности, программный модуль PT4Demo, используемый нами для предварительного тестирования создаваемых заданий).
Только при демо-запуске задания можно гарантировать, что входное значение параметра count будет равно его выходному значению.
Следует заметить, что отсутствие оператора
if count = 0 then exit;
не скажется на предварительном тестировании задания, так как при запуске задания в демо-режиме возвращаемое значение параметра count никогда
не будет равно 0. Однако при попытке запуска этого задания в параллельном режиме в случае отсутствия данного оператора будет выведено сообщение об ошибке: «В ситуации, когда параметр ProcessCount процедуры
CreateTask вернул значение 0, не выполнен немедленный выход из процедуры инициализации задания».
Рассмотрим оставшиеся операторы процедуры MPIDemo1.
С помощью процедур TaskText задается формулировка задания (обратите внимание на то, что перед символом подчеркивания указывается символ «\»; это связано с тем, что «обычный» символ подчеркивания в формулировках заданий означает переход в режим нижних индексов).
Далее задается значение вспомогательной переменной y, которая
обеспечивает вертикальное центрирование текста в разделах исходных и
результирующих данных: если число процессов равно 3 или 4, то вывод
данных начинается со второй строки раздела, а если число равно 5, то с
первой.
Наконец, с помощью процедур DataR и ResultR в цикле определяются
исходные и результирующие данные. В начале каждой итерации цикла с
помощью процедуры SetProcess определяется текущий процесс задания (в
качестве параметра процедуры указывается ранг этого процесса). Все последующие данные, определяемые в задании, связываются с текущим процессом. Напомним, что до первого вызова процедуры SetProcess текущим
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
