|
Новые возможности конструктора заданий
Конструктор учебных заданий PT4TaskMaker входит в
состав программного комплекса Teacher Pack for PT4 и реализован в нескольких вариантах:
для языка Pascal (среды Delphi и Lazarus), C++ и С# (среда Visual Studio).
Кроме того, имеется вариант конструктора PT4TaskMaker для среды PascalABC.NET.
Для обеспечения возможности разработки заданий по параллельному
программированию в конструктор PT4TaskMaker для задачника версии 4.9 были добавлены
два перегруженных варианта процедуры CreateTask и новая процедура
SetProcess; кроме того, некоторые процедуры конструктора были
модифицированы.
При разработке заданий
по параллельному программированию не требуется использовать систему
MPICH.
Новый вариант процедуры CreateTask
procedure CreateTask(SubgroupName: string; var ProcessCount: integer);
procedure CreateTask(var ProcessCount: integer);
Данные перегруженные варианты процедуры CreateTask
предназначены для инициализации задания по параллельному
программированию. Параметр SubgroupName имеет
тот же смысл, что и для исходных вариантов процедуры: он определяет
заголовок подгруппы, в которую включается задание, если
разрабатываемую группу заданий целесообразно разбить на подгруппы.
Если параметр SubgroupName является пустой строкой или отсутствует, то
задание не связывается с какой-либо подгруппой.
В отличие от исходных вариантов процедуры CreateTask,
новые варианты содержат дополнительный параметр ProcessCount. Этот параметр определяет количество процессов, которые
будут использованы при инициализации задания в параллельном режиме.
Если параметр ProcessCount меньше или равен 1, то для
инициализации задания используется соответствующий вариант
процедуры CreateTask без данного параметра (при этом выходное значение
параметра ProcessCount будет равно 1). Далее при описании процедуры
CreateTask будем предполагать, что параметр ProcessCount имеет значение,
большее 1.
Если параметр ProcessCount превосходит 36, то в окне задачника
выводится сообщение об ошибке. Указанное максимально допустимое число
процессов представляется достаточным для реализации любого учебного
задания по параллельному программированию. К примеру, в заданиях
группы MPIBegin возможное число процессов не превосходит 13. Следует
заметить, что при использовании в параллельном приложении
существенно большего числа процессов заметно возрастает время
начальной загрузки приложения.
Для того чтобы в ходе тестирования параллельной программы с
решением задачи использовалось различное количество процессов,
рекомендуется при определении значения параметра ProcessCount
применять датчик случайных чисел. Вызывать перед этим процедуру
инициализации датчика Randomize не требуется, так как она
автоматически вызывается в процедуре CreateGroup, которая
инициализирует группу заданий. Если в задании налагается какое-либо
ограничение на количество процессов (например, количество процессов
всегда должно быть четным), то это ограничение также следует учитывать
при генерации параметра ProcessCount.
Обратите внимание на то, что параметр ProcessCount является одновременно
входным и выходным. Это связано с тем, что при выполнении
задания по параллельному программированию запускаются несколько экземпляров
программы учащегося, причем каждый из них выполняет определенную
роль в ходе инициализации параллельного задания. Напомним,
что первой запускается непараллельная программа, выполняющая роль
загрузчика. То значение параметра ProcessCount, которое будет в ней
получено, используется при запуске программы в параллельном режиме.
Экземпляры программы, запущенные в параллельном режиме, тоже
вызывают процедуру CreateTask, однако входное значение параметра
ProcessCount им не требуется, так как параллельный режим уже
запущен, и число процессов определено. Поэтому для процессов
параллельного приложения параметр ProcessCount используется как
выходной параметр, позволяющий определить фактическое
количество запущенных процессов.
Следует также учитывать «неравноправие» процессов
параллельного приложения при инициализации задания. Все необходимые
исходные и контрольные данные определяются в главном процессе
(процессе ранга 0), который затем автоматически пересылает каждому
подчиненному процессу предназначенные для него данные. Таким образом,
действия, связанные с инициализацией всех данных, входящих в задание,
достаточно выполнять только в главном процессе параллельного
приложения, тогда как в подчиненных процессах инициализация задания
может (и должна) состоять лишь в вызове процедуры CreateTask. Как,
однако, отличить главный процесс от подчиненного в процедуре,
определяющей учебное задание? Для этого также используется
возвращаемое значение параметра ProcessCount: для главного процесса он
возвращает действительное количество процессов параллельного
приложения, тогда как для подчиненных процессов всегда возвращается
значение 0. Нулевое значение параметра ProcessCount означает, что
никакие дополнительные действия, связанные с инициализацией
учебного задания, в данном процессе выполнять не требуется.
Отметим, что нулевое значение параметра ProcessCount возвращается
также в экземпляре непараллельной программы, выполняющей роль
загрузчика. В самом деле, единственная «обязанность» этой
программы состоит в определении числа процессов и использовании этого
числа в качестве параметра приложения MPIRun.exe, запускающего
параллельный вариант программы. Поэтому в программе-загрузчике, как и
в программе, выполняемой в качестве подчиненного процесса
параллельного приложения, действия по инициализации задания должны
состоять только в вызове процедуры CreateTask.
Наконец, следует упомянуть демонстрационный вариант
программы с учебным заданием, который всегда выполняется в
непараллельном режиме. При демонстрационном запуске программа
выступает не в роли загрузчика, а в роли обычной непараллельной
программы, в которой задание инициализируется и отображается в окне
задачника. В этой ситуации выходное значение параметра ProcessCount
всегда совпадает с его входным значением.
Все приведенные выше сведения, связанные с параметром ProcessCount,
удобно представить в табличном виде.
«Роль» программы
|
Входное значение параметра ProcessCount
| Выходное значение параметра ProcessCount
|
Непараллельная программа-загрузчик, обеспечивающая запуск параллельного варианта программы
|
Используется: определяет число процессов при запуске параллельного варианта программы
|
Всегда равно 0
|
Главный процесс параллельной программы (процесс ранга 0)
|
Не используется
|
Равно числу процессов в параллельной программе; используется при формировании входных и выходных данных
|
Подчиненный процесс параллельной программы
|
Не используется
|
Всегда равно 0
|
Непараллельная программа, обеспечивающая демонстрационный запуск учебного задания
|
Используется
|
Всегда равно входному значению; используется при формировании входных и выходных данных
|
Процедура SetProcess
procedure SetProcess(ProcessRank: integer);
Данная процедура устанавливает в качестве текущего
процесса параллельного приложения процесс ранга ProcessRank. Все
числовые исходные и контрольные данные связываются с текущим
процессом. До первого вызова данной процедуры текущим процессом
считается процесс ранга 0. Процедуру можно вызывать несколько раз с
одним и тем же параметром (например, первый раз процесс делается
текущим при определении связанных с ним исходных данных, а второй раз
при определении его контрольных данных).
Если какой-либо элемент исходных или контрольных данных связан с
процессом ранга ProcessRank, то ввести или, соответственно, вывести этот
элемент при выполнении задания можно будет только в этом процессе.
Порядок ввода и вывода элементов в каждом процессе определяется, как
обычно, порядком вызова соответствующих процедур групп Data и Result.
Для повышения наглядности следует всегда указывать в окне задачника
комментарий вида «Процесс N:» перед теми
исходными или результирующими данными, которые связаны с процессом
ранга N.
Параметр ProcessRank должен принимать значения в диапазоне от 0
до K 1, где K количество процессов,
возвращаемое параметром ProcessCount процедуры CreateTask. При
нарушении этого условия выводится сообщение об ошибке
«Параметр процедуры SetProcess находится вне диапазона
0..K1, где K количество процессов».
Особое сообщение об ошибке будет выведено при
попытке вызвать процедуру SetProcess при выполнении подчиненного
процесса параллельного приложения или непараллельной программы-загрузчика
(напомним, что в этих режимах работы программы процедура CreateTask
возвращает в параметре ProcessCount значение 0, свидетельствующее о
том, что выполнять дальнейшие действия по инициализации задания не
требуется). В этом случае текст сообщения будет следующим: «В
ситуации, когда параметр ProcessCount процедуры CreateTask вернул
значение 0, не выполнен немедленный выход из процедуры инициализации
задания».
Процедуру SetProcess можно вызывать и при формировании задания,
не связанного с параллельным программированием. В подобной ситуации
количество процессов всегда равно 1, допустимым значением параметра
ProcessRank является только 0, и вызов процедуры SetProcess(0) не
выполняет никаких действий.
Следует подчеркнуть, что с текущим процессом связываются только
числовые данные. Данные прочих простых типов (логические,
символьные, строковые, данные-указатели), а также все
«внешние» данные (файлы и динамические структуры)
остаются связанными с главным процессом параллельного приложения.
Это ограничение объясняется тем, что, во-первых, основной областью
применения параллельного программирования являются численные
методы, и, во-вторых, для освоения возможностей библиотеки MPI вполне
достаточно использования числовых данных (включая числовые массивы и
структуры). Пересылка между процессами строковых данных может
приводить к проблемам, обусловленным различиями в строковых
форматах для разных языков программирования, а использование в
заданиях по параллельному программированию файлов и динамических
структур данных (и связанных с ними указателей) не представляется
оправданным. Впрочем, все подобные данные допустимо использовать в
заданиях по параллельному программированию, однако их ввод и вывод
можно выполнять только в главном процессе.
Особенности использования других процедур
конструктора учебных заданий
Процедура SetRequiredDataCount
При выполнении заданий по параллельному программированию во
всех процессах параллельной программы должны быть введены все
связанные с ними исходные данные. Поэтому попытка вызова процедуры
SetRequiredDataCount(N) с параметром N, значение которого меньше
общего числа исходных данных, приведет в задании по параллельному
программированию к сообщению об ошибке.
Процедуры групп Data и Result
При разработке заданий по параллельному программированию может
оказаться недостаточно пяти экранных строк, отводимых в окне задачника
для раздела исходных или результирующих данных. В некоторых
ситуациях отмеченную проблему можно решить, размещая информацию в
виде нескольких столбцов данных. Однако если данные для каждого
процесса организованы сложным образом (например, представляют собой
двумерный массив-матрицу), их не удастся разместить на пяти экранных
строках даже при использовании нескольких столбцов. Для решения этой
проблемы в конструктор учебных заданий для задачника Programming Taskbook версии 4.9 была добавлена
возможность прокрутки содержимого областей исходных и
результирующих данных в окне задачника.
Заметим, что в предыдущей версии конструктора уже была реализована
прокрутка содержимого двоичных и текстовых файлов, а также (при
необходимости) прокрутка бинарных деревьев. Кроме того, прокрутку
можно было использовать в разделе формулировки задания; для этого
было достаточно при добавлении завершающего текста формулировки,
который не умещается в первых 5 экранных строках, указывать в качестве
параметра Y процедуры TaskText особое значение 0.
Подход, использованный в процедуре TaskText, нельзя
распространить на процедуры, отвечающие за формирование разделов
исходных и результирующих данных, поскольку в них на одной экранной
строке могут располагаться несколько элементов данных, а также
комментарии к ним. Поэтому в новой версии конструктора разрешено для
процедур групп Data и Result, в том числе DataComment и ResultComment,
указывать в качестве параметра Y значение, превышающее 5 (в
предыдущей версии конструктора использование такого значения привело
бы к сообщению об ошибке). Если значение параметра Y для некоторого
элемента раздела исходных данных превышает 5, то этот элемент
размещается в строке с указанным номером, а в разделе
исходных данных становится доступной прокрутка. Аналогичным
образом, прокрутка будет доступна в разделе результатов, если хотя бы
один элемент этого раздела помещен в него процедурой с параметром Y,
превышающим 5. Если оба раздела допускают прокрутку, то она
выполняется независимо. Прокрутку в любом разделе можно выполнять с
помощью клавиатуры или мыши; в последнем случае следует использовать
кнопки , расположенные справа от прокручиваемого раздела.
Если раздел данных содержит отдельный элемент,
допускающий прокрутку (например, двоичный файл), то команды
прокрутки этого элемента и раздела в целом могут конфликтовать друг с
другом. Кроме того, при отображении в прокручиваемом разделе
элементов, занимающих более одной экранной строки (например,
динамических структур), восприятие этих элементов может быть
затруднено. Чтобы избежать отмеченных проблем, в задании запрещено
использовать прокрутку раздела, если в нем уже имеется
«внешний» объект (файл или динамическая структура). Если
делается попытка вызвать какую-либо процедуру с параметром Y,
большим 5, для раздела, уже содержащего внешний объект, то выводится
сообщение об ошибке «При наличии внешних объектов режим
прокрутки для всего раздела недоступен». Если же в разделе,
уже имеющем элементы данных или комментарии, размещенные в
неотображаемых строках, делается попытка разместить внешний объект,
то выводится сообщение об ошибке «Раздел данных в режиме
прокрутки не может содержать внешние объекты».
Чтобы проиллюстрировать новую возможность задачника, связанную
с прокруткой разделов данных, мы использовали ее при реализации
третьего задания демонстрационной группы MPIDemo.
Разумеется, прокрутка разделов данных в новой версии конструктора
PT4TaskMaker доступна и для заданий, не связанных с параллельным
программированием. В частности, она может оказаться полезной в
заданиях, требующих использования в качестве исходных данных
нескольких двумерных массивов (например, в задании о нахождении
произведения матриц) или массива символьных строк.
|