|
Особенности работы задачника в параллельном режиме
Варианты запуска откомпилированной программы
Для успешной разработки новых заданий по параллельному
программированию необходимо четкое понимание механизма
функционирования задачника в параллельном режиме.
Прежде всего, следует учитывать, что откомпилированная программа
учащегося (exe-файл), предназначенная для выполнения задания по
параллельному программированию, может выступать в различных
качествах.
Программа, запускаемая непосредственно из среды
программирования, всегда выполняется как обычная
«непараллельная» программа. В частности, в непараллельном
режиме всегда выполняется демонстрационный запуск программы
(напомним, что для этого в конце имени задания, указываемого в качестве
параметра процедуры Task, надо добавить символ «?» или
«#»). При демонстрационном запуске не требуется
подключения системы MPICH, поэтому ознакомиться с содержанием задач
по параллельному программированию можно и на тех компьютерах, на
которых система MPICH не установлена.
Если запуск не является демонстрационным, то программа,
запущенная из среды программирования, выступает в роли
«загрузчика» своего параллельного варианта. Это позволяет
максимально упростить действия учащегося при тестировании
параллельной программы, поскольку требует от него только запуска
разработанной программы непосредственно из среды программирования.
Все прочие действия, связанные с нахождением файла MPIRun системы
MPICH и его запуском с требуемыми параметрами командной строки,
выполняются задачником автоматически. Кроме того, программа-
загрузчик обеспечивает случайный выбор количества процессов,
запускаемых для параллельного варианта программы.
Действия программы в режиме загрузчика
При своем запуске программа-загрузчик проверяет наличие
программы MPIRun.exe системы MPICH, создает в рабочем каталоге
учащегося пакетный файл $pt_run$.bat, содержащий команду для запуска
программы MPIRun.exe, и запускает созданный пакетный файл. Если
программа MPIRun не найдена или пакетный файл нельзя создать или
запустить, то выводится сообщение об ошибке, и выполнение программы-загрузчика
немедленно завершается. Если же пакетный файл успешно
запущен, то программа-загрузчик ожидает его завершения, после чего
выполняет необходимые завершающие действия и заканчивает работу.
Разумеется, программу MPIRun с требуемыми параметрами можно
было бы запускать и непосредственно из программы-загрузчика, однако
вариант с использованием пакетного файла обладает рядом преимуществ.
Прежде всего, запуск пакетного файла приводит к отображению на экране
стандартного консольного окна, в котором можно вывести информацию о
том, что выполнен запуск программы в параллельном режиме, а также
отобразить ту командную строку, которая обеспечивает запуск программы
MPIRun. Это позволяет сделать для учащегося более наглядным способ
запуска параллельной программы; кроме того, из параметров командной
строки учащийся может легко определить, сколько процессов
параллельной программы запущено. Еще более важной является
возможность стандартным образом (нажатием [Ctrl]+[C] или
[Ctrl]+[Break]) прервать выполнение пакетного файла в случае зависания
параллельной программы, что при выполнении заданий может
происходить достаточно часто. Заметим, что описание действий, требующихся
для прерывания выполнения пакетного файла,
приводится в его консольном окне:
Перед завершением своей работы программа-загрузчик вначале
удаляет из каталога учащегося вспомогательный пакетный файл, а затем
выгружает из памяти все процессы, связанные с задачником (за
исключением своего собственного процесса). Последнее действие
необходимо в ситуации, когда параллельная программа зависла, и
выполнение пакетного файла было прервано явным образом. Если не
удалить зависшие процессы из памяти, то станет невозможной
перекомпиляция исправленной программы, так как exe-файл программы
нельзя будет перезаписать из-за его блокировки этими процессами.
Осталось обсудить следующий вопрос: как запущенная программа определяет,
что она является загрузчиком? Казалось бы, для проверки этого достаточно
с помощью средств MPI определить количество процессов, связанное с
запущенным экземпляром программы: если число процессов равно 1,
значит, программа запущена не в параллельном режиме и, следовательно,
является загрузчиком, тогда как в противном случае программа выступает
в роли одного из процессов параллельной программы и не должна
выполнять действия загрузчика. Однако при таком способе проверки
появляется возможность запустить программу в параллельном режиме
без предварительного запуска программы-загрузчика.
Действительно, ничто не мешает учащемуся создать явным образом
пакетный файл с командой запуска программы MPIRun и, запустив этот
пакетный файл, выполнить программу, решающую задачу, в
параллельном режиме. Такой способ запуска является нежелательным
прежде всего потому, что в этой ситуации количество процессов
параллельной программы задает сам учащийся и, таким образом, задачник
лишается возможности протестировать предложенное решение при
различном числе процессов. Кроме того, в задании обычно предполагается,
что число параллельных процессов лежит в определенном диапазоне,
выход за границы которого может привести к ошибке
при инициализации этого задания.
Следовало реализовать такой способ проверки, который делал бы
невозможным выполнение параллельного варианта программы без
предварительного запуска программы-загрузчика. Для этого проще всего
было сделать так, чтобы экземпляр программы, выступающий в роли
загрузчика, при своем запуске оставлял какую-либо метку в
каталоге учащегося, а любой экземпляр запущенной программы проверял
наличие этой метки: если метка обнаружена, то этот экземпляр считается
одним из процессов параллельной программы, в противном случае он
считается загрузчиком. При завершении работы параллельного варианта
программы метка удаляется. Ясно, что метка должна быть защищена от
«подделки», поэтому в качестве такой метки нельзя
использовать, например, факт наличия в каталоге пакетного файла
$pt_run$.bat.
В задачнике реализован достаточно надежный способ установки
метки программой-загрузчиком. Благодаря ему учащийся не
сможет запустить параллельный вариант своей программы с
помощью «стороннего» пакетного файла: при таком запуске
первый из запущенных параллельных процессов не обнаружит в каталоге
метки загрузчика, поэтому он «решит», что сам является
загрузчиком, поместит свою метку в каталог, создаст свой пакетный файл
и запустит его на выполнение. При этом остальные параллельные
процессы из «стороннего» пакетного файла даже не будут
запущены, так как для их запуска программой MPIRun необходимо, чтобы
первый запущенный ею процесс в течение определенного времени после
запуска (по умолчанию 10 с) перешел в параллельный режим, а в случае
программы, выполняющей роль загрузчика, этого не произойдет.
Заканчивая обсуждение вопросов, связанных с программой-загрузчиком,
отметим, что при своем запуске данная программа дополнительно
проверяет наличие уже запущенных программ, связанных с задачником, и
при их обнаружении выводит сообщение «Перед запуском
программы в параллельном режиме необходимо закрыть все открытые
окна задачника» и завершает работу. Такая проверка
необходима из-за завершающих действий программы-загрузчика: если при
наличии работающих программ, в которых отображается окно задачника,
будет запущено параллельное приложение, то после его завершения
связанная с ним программа-загрузчик удалит из памяти все
процессы, связанные с задачником, в том числе и все ранее запущенные
программы, что может привести к нежелательным последствиям.
Действия программы в параллельном режиме
и обработка ошибок
Теперь опишем действия программы учащегося, запущенной в
параллельном режиме. При работе программы в параллельном режиме
выполняется несколько экземпляров этой программы (процессов), причем
процесс ранга 0 играет особую роль. Как и ранее, будем называть этот
процесс главным, а остальные процессы подчиненными. Именно в главном
процессе происходит инициализация задания, т. е. генерируются
наборы исходных и контрольных данных для всех процессов параллельной
программы. После генерации этих наборов выполняется пересылка в
каждый подчиненный процесс тех исходных и контрольных данных,
которые определены для этого процесса (для связывания элементов
данных с конкретным процессом при инициализации задания необходимо
использовать процедуру SetProcess см. ее описание в
разделе «Новые возможности конструктора заданий»).
После завершения пересылки данных каждый процесс выполняет
свою часть алгоритма решения задания, разработанного учащимся, в
частности, вводит исходные данные, подготовленные задачником, и
выводит результирующие данные (вывод данных осуществляется в
специальный выходной буфер в оперативной памяти). Затем все
подчиненные процессы пересылают главному процессу сведения о
результатах своей работы (включающие все данные из выходного буфера и
некоторую дополнительную информацию), причем пересылка этих
сведений выполняется с использованием особого коммуникатора,
определенного в задачнике и недоступного программе учащегося. Главный
процесс анализирует полученную информацию и отображает ее в окне
задачника. После закрытия окна задачника выполнение параллельной
программы завершается.
При выполнении подчиненных процессов могут возникнуть ошибки,
приводящие к их зависанию или аварийному завершению. В этом случае
главный процесс не сможет получить от этих процессов информацию о
результатах их работы. Если в течение определенного времени
(зависящего от общего числа подчиненных процессов) главный процесс не
получит информацию от некоторых подчиненных процессов, то он
выведет в окне задачника сообщение «MPI error. Процессы … не
отвечают» (где на месте многоточия
указываются ранги зависших процессов). В этой ситуации после закрытия
окна задачника потребуется явным образом прервать выполнение
зависших процессов параллельного приложения, нажав
несколько раз комбинацию клавиш [Ctrl]+[C] или [Ctrl]+[Break].
Прочие ошибки, связанные с функциями MPI, не приводят к выводу
особых сообщений в информационном разделе окна задачника, однако
сведения о них выводятся в разделе отладки, который в этом случае
автоматически отображается на экране (для перехвата ошибок системы
MPI и отмены их стандартной обработки, приводящей по умолчанию к
немедленному завершению всех процессов параллельного приложения, в
задачнике определяется специальный обработчик ошибок).
Все «обычные» ошибки времени выполнения (не
связанные с библиотекой MPI) подчиненные процессы также
перехватывают, после чего пересылают информацию о них главному
процессу, который в этом случае выводит сообщение «Run-time
error». Кроме того, подчиненные процессы могут распознавать
различные ошибки, связанные с неверным вводом-выводом (ввод
недостаточного числа исходных данных, попытка ввода лишних исходных
данных или исходных данных неверного типа; вывод недостаточного
числа результирующих данных, попытка вывода лишних результирующих
данных или результирующих данных неверного типа).
Сведения, полученные от подчиненных процессов, анализируются в
главном процессе в порядке убывания рангов (от процесса
K 1 до процесса 1, где K общее число
процессов). Если в различных подчиненных процессах возникли ошибки
различного типа, то в информационном разделе окна задачника выводится
информация о той ошибке, которая была обнаружена первой. При этом не
только выводится описание ошибки, но и перечисляются ранги
подчиненных процессов, в которых обнаружена данная ошибка.
Дополнительная информация о каждой ошибке, обнаруженной в
подчиненных процессах, приводится в разделе отладки окна задачника,
причем рядом с описанием ошибки указывается ранг процесса, в котором
она произошла.
В случае обнаружения ошибок в подчиненных процессах главный
процесс (процесс ранга 0) не проверяется на наличие ошибок.
Если ошибки в подчиненных процессах не обнаружены, то
выполняется завершающая проверка правильности решения в главном
процессе. При наличии ошибок ввода-вывода, ошибок времени
выполнения или в случае несоответствия результирующих данных
контрольным данным выводится соответствующее сообщение; если же
ошибки отсутствуют, а результаты совпадают с требуемыми
контрольными значениями, то данное испытание программы считается
успешным. Как и в обычном, «непараллельном» варианте
задачника, задание считается выполненным после определенного числа
успешных испытаний, проведенных подряд (для заданий группы MPIBegin
достаточно пяти успешных тестовых испытаний).
Заметим, что имеется особый тип ошибки, который не может быть
распознан подчиненным процессом: это отсутствие в процессе любых
операций ввода-вывода (поскольку отсутствие таких операций может
свидетельствовать не об ошибке, а лишь о том, что был выполнен
ознакомительный запуск параллельной программы без попытки решения
задачи). Такие ошибки могут быть пропущены на первом этапе анализа
решения; при этом может случиться, что первой будет обнаружена ошибка
в другом процессе, и именно информация о ней будет выведена на экране.
Однако в конечном итоге (при отсутствии других ошибок) главный
процесс выявит ошибки, которые связаны с отсутствием в некоторых
процессах операций ввода-вывода, и выведет сообщение о них в окне
задачника.
Возможна ситуация, когда из-за ошибок в реализации параллельного
алгоритма произойдет зависание главного процесса. Главный процесс можно
считать зависшим, если в течение 1520 с после
отображения консольного окна с информацией о запуске программы в
параллельном режиме на экране не появится окно задачника. В этом
случае необходимо явным образом прервать выполнение параллельной
программы, нажав несколько раз [Ctrl]+[C] или [Ctrl]+[Break].
В файл результатов в такой ситуации заносится информация о
том, что выполнение задания было прервано.
Завершая данный раздел, приведем схему, иллюстрирующую процесс
выполнения учебного задания по параллельному программированию:
Изменения в версии 4.11 базового варианта задачника, связанные с заданиями по параллельному программированию
В версиях задачника, предшествующих версии 4.11, в случае ошибочного решения, связанного с неправильным
вводом-выводом данных, сообщение об ошибке могло иметь вид «Ошибочное решение». В частности, такая
ситуация могла возникнуть, если в некотором подчиненном процессе не выполнялся требуемый ввод
данных (а в других процессах действия по вводу-выводу выполнялись). Это объяснялось тем, что сам
подчиненный процесс, в котором не выполнялись действия по вводу-выводу, мог «предположить»,
что производится ознакомительный запуск программы (в котором операции ввода-вывода
отсутствуют во всех процессах), и поэтому не посылал главному процессу сигнала об ошибке.
Главный же процесс, не получив сигнала об ошибке ни от одного из подчиненных процессов, мог
отреагировать на неверные результаты сообщением об ошибочном решении.
Подобная ситуация затрудняла отладку программы и противоречила правилу, согласно которому
прежде всего выявляются ошибки ввода-вывода, и только в случае отсутствия подобных ошибок
анализируются значения полученных результатов. Заметим, что в силу этого правила сообщение об
ошибочном решении может появиться только в случае, если оба индикатора ввода-вывода полностью
заполнены, и с ними не связаны особые ошибки ввода-вывода.
В версии 4.11 задачника отмеченный недочет исправлен. Теперь главный процесс не только принимает
сигналы об ошибках от подчиненных процессах, но и самостоятельно выявляет те из них, которые
сами подчиненные процессы выявить не могут. В частности, он распознает ошибки, связанные с
полным отсутствием операций ввода-вывода в некотором подчиненном процессе. Заметим, что в
версии 4.11 главный процесс получает от подчиненных процессов дополнительную информацию о
числе введенных и выведенных данных (это необходимо для правильного отображения
соответствующих индикаторов в окне задачника).
В версии 4.11 также изменен механизм отслеживания зависших процессов. Особенностью нового
варианта является то, что теперь допускается иметь открытые окна задачника в момент запуска
параллельного приложения. В предыдущей версии в подобной ситуации задачник
выводил сообщение об ошибке «Перед запуском программы в параллельном режиме необходимо
закрыть все открытые окна задачника». Благодаря отмене отмеченного ограничения можно при
запуске параллельного приложения оставлять открытым окно задачника, связанное с модулем
PT4Demo, или окна, относящиеся к заданиям, выполняемым в данное время в других программных
средах.
|