Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Практикум по параллельному программированию с использованием электронного задачника Programming Taskbook for MPI

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
91
Count означает, что никакие дополнительные действия, связанные с ини­циализацией учебного задания, в данном процессе выполнять не требуется.
Отметим, что нулевое значение параметра ProcessCount возвращается также в экземпляре непараллельной программы, выполняющей роль за­грузчика. В самом деле, единственная «обязанность» этой программы со­стоит в определении числа процессов и использовании этого числа в каче­стве параметра приложения MPIRun.exe, запускающего параллельный ва­риант программы. Поэтому в программе-загрузчике, как и в программе, выполняемой в качестве подчиненного процесса параллельного приложе­ния, действия по инициализации задания должны состоять только в вызове процедуры CreateTask.
Наконец, следует упомянуть демонстрационный вариант программы с учебным заданием, который всегда выполняется в непараллельном режи­ме. При демонстрационном запуске программа выступает не в роли за­грузчика, а в роли обычной непараллельной программы, в которой задание инициализируется и отображается в окне задачника. В этой ситуации вы­ходное значение параметра ProcessCount всегда совпадает с его входным значением.
Все приведенные выше сведения, связанные с параметром Process­Count, представлены в таблице 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), а второй обеспечивает отображение на экране последнего из
заданий этой группы.
Для определения главного приложения и его параметров в среде Del­phi надо выполнить команду меню «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 текущим
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]