Опубликован: 28.10.2009 | Доступ: свободный | Студентов: 515 / 40 | Оценка: 4.67 / 4.39 | Длительность: 20:33:00
Лекция 6:

Отладка параллельной программы

Аннотация: В лекции приводится назначение Intel® Thread Checker, характеризуются области его возможного применения, приводится информация, необходимая для подготовки пользовательского проекта и инструмента для анализа.

В предыдущих разделах рассматривались следующие вопросы построения кластера и создания программ для запуска на кластере:

  • аппаратной основы кластера;
  • системного обеспечения, необходимого для работы кластера;
  • оценка эффективности построенного кластера;
  • среды разработки параллельных программ.

Одним из основных этапов разработки программ является - отладка. Отладка параллельных программ является существенно более сложной задачей, чем отладка последовательных, поэтому в данном разделе уделено отдельное внимание отладки параллельных программ.

Ни для кого не секрет, наличие инструментов делает жизнь проще. Тезис этот находит подтверждение как в повседневной жизни - чистить одежду удобнее и эффективнее щеткой, чем руками, заворачивать гайки гораздо проще ключом, чем пальцами - так и в профессиональной деятельности - кто сейчас возьмется строить, ладно, даже не дом, пусть всего лишь баню, при помощи одного лишь топора. Естественно программисты - не исключение. Значение инструментов, облегчающих путь от анализа постановки задачи до получения решения, готового к внедрению, трудно переоценить. Данный документ представляет собой краткое описание инструмента отладки параллельных программ, разработанного корпорацией Intel и носящего название Intel® Thread Checker.

В первом разделе документа приводится назначение рассматриваемого инструмента, характеризуются области его возможного применения. Во втором дается краткая характеристика принципов работы Intel® Thread Checker. В третьем и четвертом разделах приводится информация, необходимая для подготовки пользовательского проекта и инструмента для анализа. Пятый раздел посвящен вопросам сбора и анализа данных, полученных в результате работы Intel® Thread Checker. В шестом возможности инструмента рассмотрены на простом примере, входящем в поставку Intel® Thread Checker.

Итак, приступим.

9.1. Назначение Intel Thread Checker

Процесс отладки в общем случае можно разбить на следующие шаги:

  • определение факта наличия ошибки;
  • поиск (локализация) ошибки;
  • выяснение причин ошибки;
  • определение способа устранения ошибки;
  • устранение ошибки.

Кажется, что на первом шаге никакой инструмент не требуется. Запускаем программу и либо на некоторых исходных данных получаем неверные результаты, либо обнаруживаем, что некоторая последовательность действий по использованию программы ведет к ее "падению" или "зависанию". Однако для параллельной программы даже этот очевидный шаг может иметь существенную сложность. На практике нередко встречаются ситуации, когда неработоспособность параллельной программы проявляется один раз на сотню и более запусков. Очевидно, в этом случае инструментальная поддержка лишней не будет.

Назначение Intel® Thread Checker (ITC) - поиск мест с возможным недетерминированным поведением многопоточной программы, написанной как на основе библиотеки потоков (Windows или POSIX threads), так и с использование технологии OpenMP. Соответственно ITC может быть использован как под операционными системами семейства Windows, так и под различными ОС семейства Linux. Принципы поиска ошибок рассмотрены ниже, здесь же укажем, что ITC неплохо справляется с задачей обнаружения факта ошибки в программе, даже если эта ошибка в текущем варианте исполнения программы и не проявила себя.

Второй шаг - поиск ошибки заключается в необходимости как можно более точной ее локализации, в идеале должна быть найдена переменная с неверным значением и/или строка кода, ведущая к краху программы. Типичный метод работы в этом пункте - использование режима трассировки в отладчике с наблюдением за состоянием переменных, регистров, стека вызова и т.д. "Плохая новость" - для многопоточных программ режим трассировки практически неприменим, поскольку автоматически меняет характер их выполнения, а значит, скрывает места, которые могут приводить к проблемам во время реальной работы. Кстати говоря, даже типичный способ локализации ошибки расстановкой операторов печати по тексту программы в этом случае нужно использовать с большой осторожностью - печать также вносит синхронизацию в выполнение программы.

Что же делать? Быть может, наилучшее из возможных решение реализовано в ITC. ITC не есть привычный всем отладчик с режимами трассировки, наблюдения и т.д. ITC выполняет анализ программы сам, без участия программиста, причем анализируется не только выполненный "прогон" программы, а все возможные варианты ее выполнения. В результате выясняются и показываются программисту места в программе, в которых содержатся ошибки (с той или иной долей вероятности, в большинстве случаев близкой к 100%).

Шаг третий - выяснение причин ошибки. Задача здесь - понять, почему ошибка возникла. Отсюда во многих случаях автоматически вытекает способ ее устранения (задача шага четвертого). Можно, конечно, выяснить условия, ведущие к проявлению ошибки (некое сочетание значений переменных, например) и просто вставить в код заплатку именно для этого случая. Данный вариант мы здесь не рассматриваем. На этом шаге ITC помогает тем, что каждое найденное им проблемное место сопровождает комментарием, содержащим тип ошибки: гонка данных, несинхронизированный доступ к переменной, тупик и т.д.

В результате мы получаем место потенциальной ошибки, переменную, с которой связана проблема и описание ошибки. Остается лишь освоить типовые способы борьбы с типовыми ошибками и значительная их часть будет находиться и исправляться без грандиозных усилий.

Единственное в чем ITC совсем не может помочь - это шаг пятый. Устранять найденную ошибку все-таки придется программисту самостоятельно.

9.2. Возможности Intel Thread Checker

Согласно [9.2] ITC обнаруживает ошибки следующих видов: гонки данных ( data races ), тупики ( deadlocks ), потоки в состоянии ожидания ( stalled threads ), потерянные сигналы ( lost signals ), заброшенные замки ( abandoned locks ).

Приведем краткое описание каждого вида.

  • Гонки данных. Возникают, когда несколько потоков работают с разделяемыми данными и конечный результат зависит от соотношения скоростей потоков. Пусть, например, один поток выполняет над общей переменной x операцию x = x + 3, а второй поток - операцию x = x + 5. Данные операции для каждого потока фактически разбиваются на три отдельных подоперации: считать x из памяти, увеличить x, записать x в память. В зависимости от взаимного порядка выполнения потоками подопераций финальное значение переменной x может быть больше исходного на 3, 5 или 8. Гонка данных возможна и в случае, когда один поток пишет в переменную, а остальные только читают из нее.
  • Тупики. Взаимная блокировка потоков, ожидающих наступление некоторого события для продолжения работы. Типичный пример тупика, когда нулевой поток занял для использования ресурс 1 и ожидает предоставления ему ресурса 2, а первый поток занял ресурс 2 и ожидает предоставления ему ресурса 1.
  • Потоки в состоянии ожидания. Одно из состояний потока в многозадачной операционной системе - ожидание. Поток переходит в него, когда для продолжения выполнения ему требуется наступление некоторого внешнего события. Если пребывание потока в этом состоянии продолжается слишком долго, ITC рапортует об ошибке типа stalled thread. Интервал времени, по истечении которого выдается данная диагностика, может быть задан в настройках ITC.
  • Потерянные сигналы. Возникают, когда поток ожидает наступление некоторого события, произошедшего прежде, чем поток пришел в состояние готовности к его приему и обработке. В результате поток никогда не сможет выйти из состояния ожидания.
  • Заброшенные замки. Возникают в ситуации, когда поток захватил некоторый ресурс (критическую секцию, мьютекс) и был снят с выполнения по той или иной причине. В результате ресурс не может быть освобожден. Если он требуется другому потоку, это приведет к бесконечному ожиданию.

9.3. Принцип сбора информации

Анализ программы, выполняемый ITC, основан на процедуре инструментации. Инструментация - вставка обращений к библиотеке ITC для записи действий, потенциально способных привести к ошибкам: работа с памятью, вызовы операций синхронизации и работа с потоками [9.2]. Может выполняться автоматически на уровне исполняемого модуля (а также dll-библиотеки) и/или по указанию программиста на уровне исходного кода. Для достоверности получаемых результатов крайне желательно, чтобы во время сборки анализируемой программы была выключена оптимизация (сборка в конфигурации debug не обязательна).

В процессе анализа контролируется:

  • доступ к памяти;
  • операции синхронизации;
  • операции создания потоков.

Необходимо отметить, что неисполняемые участки (не вызываемые функции, ветки условных переходов и т.д.) никак не проверяются, то есть под анализ не подпадают.

9.4. Подготовка программы для анализа

Использование отладчика Intel® Thread Checker возможно в двух режимах:

  • Бинарная инструментация программы - осуществляется автоматически в момент запуска Активности ( Activity ) в проекте ITC. Рекомендуется в случае, если отсутствует доступ к исходным кодам или невозможна повторная сборка программы с нужными ITC настройками.
  • Компиляторная инструментация - при сборке анализируемой программы необходимо указать ключ компилятора /Qtcheck. Позволяет ITC предоставить информацию о найденных ошибках с указанием имен переменных, с которыми эти ошибки связаны.

    Сборка приложения для работы с ITC предполагает установку следующих опций проекта (или настроек в make-файле):

  • Компиляция потоко-безопасного кода: -MT[d], -MD[d]. Данные опции автоматически устанавливаются при сборке в конфигурации debug. При сборке в конфигурации release указанные опции необходимо устанавливать вручную.
  • Использование debug опций: -Z[i,I,7], -Od. Замечание аналогичное предыдущему пункту.
  • Связывание с ключом /fixed:no. Необходимо указывать явно.

Дополнительно необходимо отметить, что при использовании ключа /Qtcheck в среде разработки (IDE) требуется указать путь к библиотекам ITC. Обычно этот путь имеет вид C:\Program Files\Intel\VTune\Analyzer\Lib\.

9.5. Создание проекта в Intel Thread Checker

Работа в ITC выполняется в рамках проекта. Для его создания используется команда меню File->New Project. В главном окне мастера настройки проекта (см. рис. 9.1) необходимо выполнить всего лишь два действия. Первое - указать исполняемый файл ( Launch an application ). Второе (необязательное) - указать аргументы командной строки.

Мастер настройки проекта в Intel Thread Checker (версия 3.0)

Рис. 9.1. Мастер настройки проекта в Intel Thread Checker (версия 3.0)

Рекомендуется при анализе программы, с одной стороны, использовать типичные размеры обрабатываемых данных, с другой, задавать их так, чтобы программа "убиралась" в оперативную память с учетом накладных расходов ITC, которые могут быть довольно значительными.

9.6. Сбор и анализ данных

После запуска в проекте ITC активности (при создании проекта это происходит автоматически) начинается инструментация исполняемого модуля, указанного для анализа, и используемых им динамических библиотек. Затем модуль запускается и начинается процесс анализа. По завершении ITC формирует окно с информацией о найденных ошибках и подозрительных местах. Возможный его вид указан на рис. 9.2.

В случае повторного запуска активности необходимо использовать один из следующих вариантов:

  1. выбрать пункт меню Activity \to Run,
  2. нажать F5,
  3. нажать кнопкуна панели инструментов.
Результат анализа - список диагностики

увеличить изображение
Рис. 9.2. Результат анализа - список диагностики

По каждой диагностике, выданной ITC, в случае если сборка выполнялась с приведенными в разделе 4 настройками, может быть получена дополнительная информация (см. раздел 7), вид которой показан на рис. 9.3.

Результат анализа - диагностика в исходном коде

увеличить изображение
Рис. 9.3. Результат анализа - диагностика в исходном коде