Процесс отладки параллельных программ можно условно поделить на традиционную и автоматическую.
В традиционном подходе отлаживаемая программа запускается под управлением отладчика, который в любой момент может приостановить выполнение программы и выдать ее текущее состояние. Таким образом, пользователь может просматривать содержимое переменных и текущее положение в исходном коде, выполняя программу по шагам или расставляя контрольные точки. И на основании полученной информации, пользователь может определить причину некорректной работы программы. Следует заметить, что обычно при таком подходе параллельная работа моделируется программно, так как при достижении контрольной точки любой нитью все остальные нити так же должны остановиться, причем они могут находиться в это время в любом месте программы. Таким образом, при нескольких запусках, когда одна нить будет доходить до контрольной точки, то параллельные ей нити могут останавливаться каждый раз в разных местах, а следовательно значения переменных тоже будут разными, что в значительной степени увеличивает сложность обнаружения ошибок.
Одним из видов автоматической отладки является динамический контроль корректности. Автоматическая отладка позволяет только определить корректность самой программы, а не правильность работы реализованного в ней алгоритма. Т.е. автоматически будут найдены участки кода, которые создают ситуации несоответствующие стандарту языка или используемой технологии. Работа динамического контроля корректности заключается в слежении за состоянием отлаживаемой программы во время ее выполнения и обнаружении некорректных ситуаций.
Цель дипломной работы – разработать и реализовать алгоритм динамического контроля корректности использования директив OpenMP в программах, написанных на языке Fortran. И минимизировать количество потребляемых ресурсов при работе отладчика.
Задача инструмента, проводящего динамический контроль корректности OpenMP программы, состоит в том, чтобы находить в отлаживаемой программе некорректное использование директив, которое может привести к неправильному результату ее работы. Тем самым такой инструмент должен упростить и ускорить весь процесс отладки, благодаря нахождению самых распространенных и трудно обнаруживаемых ошибок. Из перечисленного во введении списка ошибок таковыми являются два вида: ошибки общей памяти и ошибки инициализации.
Ошибки общей памяти возникают, когда несколько нитей одновременно и независимо друг от друга работают с одной и той же областью памяти, причем хотя бы одна из нитей модифицирует содержимое этой области.
Ошибки инициализации возникают при чтении переменной, которой не присвоено никакое начальное значение. Если проблема обусловлена использованием директив OpenMP, то трудоемкость ее нахождения традиционным методом отладки сравнима с нахождением ошибки общей памяти. Кроме того, данный вид ошибки может не проявляться на одной машине, но приводить к неправильным результатам работы программы на других, в зависимости от использованных компиляторов и библиотек OpenMP.
Таким образом, требуется разработать и реализовать алгоритмы нахождения ошибок общей памяти и ошибок инициализации в OpenMP программах, написанных на языке Fortran 77.
На данный момент существуют несколько коммерческих инструментов, осуществляющих динамический контроль корректности OpenMP программы. Рассмотрим отладчики Intel Thread Checker[5] и Sun Thread Analyzer[4]. Оба инструмента способны находить ошибки общей памяти и взаимной блокировки в программах, использующих не только OpenMP, но и другие разновидности параллелизма с общей памятью, например, нити POSIX.
Intel Thread Checker имеет два режима работы: зависимый от числа нитей и независимый.
Для работы в первом отлаживаемую программу можно не перекомпилировать, если она компилировалась с включением отладочной информации. В этом режиме запуск программы должен осуществляться под управлением отладчика. Для обнаружения ошибок общей памяти необходимо запускать программу на более чем одной нити, при этом в случае нахождения ошибки будет выдано только положение в исходном коде, а имя переменной, с которой связана эта ошибка, останется неизвестным.
Независимый от числа нитей режим работы включается добавлением при компиляции программы с помощью компилятора Intel опции –tcheck. При этом в отлаживаемой программе должны отсутствовать вызовы функций работы с числом нитей, таких как omp_set_num_threads(), omp_get_num_threads(), omp_get_max_threads(), omp_get_thread_num(), и omp_get_num_procs(). В этом режиме программа должна быть запущена на одной нити. В процессе работы параллелизм будет смоделирован и, не смотря на то, что реально при работе программы ошибки не произошли, они будут найдены, как потенциальные. Так же этот режим позволяет получить более подробную информацию об ошибке.
Sun Thread Analyzer требует, чтобы программа была скомпилирована с ключом -xinstrument=datarace. Так же должна быть включена отладочная информация для того, чтобы отладчик мог выдать диагностику с привязкой к исходному коду. Для обнаружения ошибок, запуск программы должен быть произведен на нескольких нитях.
Сравнение приведенных отладчиков было проведено в Center for Computing and Communication RWTH Aachen University в Германии[2]. Intel Thread Checker и Sun Thread Analyzer запускались на нескольких программах с разными параметрами, для определения достоинств и недостатков этих инструментов. Наибольший интерес представляют полученные результаты замеров производительности отлаживаемых программ под управлением данных инструментов и без них, а так же количество потребляемой памяти. Отладчики тестировались на 3 примерах:
- Jacobi. Приближенное решение двумерного уравнения Пуассона методом Якоби.
- SMXV. Умножение матрицы на вектор в случае, когда в матрице имеется большое количество нулевых элементов.
- AIC. Вычисление интеграла адаптивным методом.
В Таблице 1 приведены параметры производительности и потребляемой памяти (в мегабайтах) при работе программы. При этом инструменты тестировались на разных машинах с использованием разных компиляторов:
Intel – запуск программы, скомпилированной компилятором Intel, без отладчика на 2 нитях.
Intel Thread Checker – запуск программы на 2 нитях под управлением этого инструмента в зависимом от числа нитей режиме, т.к. алгоритмы SMXV и AIC используют функции для работы с числом нитей.
Sun – выполнение программы на 2 нитях без режима отладки, с использованием компилятора фирмы Sun.
Sun Thread Analyzer – отладка программы на 2 нитях с помощью Sun Thread Analyzer.
Таблица 1: Характеристики выполнения программ
Jacobi | SMXV | AIC | ||||
MByte | MFLOP/s | MByte | MFLOP/s | MByte | время | |
Intel | 5 | 621 | 40 | 929 | 4 | 5,0 сек |
Intel Thread Checker | 115 | 0,9 | 1832 | 3,5 | 30 | 9,5 сек |
Sun | 5 | 600 | 50 | 550 | 2 | 8,4 сек |
Sun Thread Analyzer | 125 | 1,1 | 2020 | 0,8 | 17 | 8,5 сек |
Из приведенной таблицы видно, что замедление во время отладки программы может достигать сотен раз, в то время как потребление памяти так же увеличивается в десятки раз.
К тому же при проведении данного тестирования было замечено, что Intel Thread Checker работал на 4-х нитях с такой же производительностью, что и на 2-х.
Рассмотренные инструменты отладки, а именно Intel Thread Checker и Sun Thread Analyzer, с успехом могут быть использованы для отладки небольших программ. Однако, из-за значительного увеличения объема потребляемых ресурсов во время отладки они становятся не применимыми для задач, работающих с большими объемами данных и производящими длительные вычисления. Для таких приложений требуется подбирать специальные входные данные, чтобы минимизировать потребляемые ресурсы. В некоторых случаях такой метод может быть не применим, например, когда объем требуемой памяти не сильно зависит от входных данных, или если программа получает на вход данные непосредственно от другого приложения.
В этой главе описана схема работы отладчика, реализующего динамический контроль корректности OpenMP программы.
Изначально имеется исходный код Fortran программы со вставленными директивами OpenMP. Отладчик скомпилирован в объектный файл, и имеет интерфейс в виде набора функций, которые должны быть вызваны в процессе работы отлаживаемой программы, при наступлении соответствующих событий, например, при обращении к памяти. При помощи специальной программы – инструментатора, в исходный код вставляются вызовы интерфейсных функций отладчика. Далее полученный код должен быть скомпилирован вместе с объектным файлом отладчика в выполняемую программу, которую можно запускать на вычислительной системе с общей памятью. В результате работы этой программы будет выдана информация об обнаруженных ошибках. На рисунке 2 представлена описанная схема.
Рисунок 2: Общая схема работы отладчика.
Следует отметить, что при данной схеме работы будут найдены ошибки только в тех участках кода, для которых в процессе выполнения программы были выполнены вызовы функций отладчика. Из этого следует, что для исследования всего кода на наличие ошибок необходимо соответствующим образом подобрать входные данные. Но в любом случае недостижимый код проверен не будет из-за того, что вставленные функции отладчика также будут недостижимы.