Нижегородский государственный университет им. Н.И.Лобачевского
Опубликован: 02.10.2012 | Доступ: свободный | Студентов: 1753 / 198 | Длительность: 17:47:00
Специальности: Программист
Лекция 6:

Введение в использование инструментов для параллельного программирования на примере пакета Intel Parallel Studio

Проблемы производительности, определяемые при помощи профилирования.

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

Рассмотрим далее наиболее распространенные ошибки и то, как ITP может помочь разработчикам в их обнаружении и исправлении.

Распределение вычислительной нагрузки.

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

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

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

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

Синхронизация и производительность.

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

Рассмотрим основные вопросы, связанные с синхронизацией.

Выбор примитивов синхронизации

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

Рассмотрим, например, ситуацию, когда в приложении имеется некоторый целочисленный счетчик, являющийся глобальной переменной. При этом если мы в приложении используем каждый раз увеличение этого счетчика на единицу, то выбор такого объекта, как мьютекс, является нерациональным. Гораздо эффективнее использовать атомарную функцию ОС Windows interlockedincrement. Кроме того, при разработке модели синхронизации следует делать выбор в пользу примитивов пользовательского уровня (CriticalSection, например), поскольку они работают быстрее, так как не генерируют системных вызовов операционной системы.отрим основные вопросы, связанные с синхронизацией.

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

Синхронизация между потоками

Разработчикам следует придерживаться следующей рекомендации: производить синхронизацию между потоками как можно реже. То есть необходимо сделать потоки максимально независимыми, чтобы избежать ситуаций, когда они ожидают друг друга. Слишком частое обращение нескольких потоков к разделяемому ресурсу приводит к тому, что большое количество времени потоки простаивают, находясь в состоянии ожидания.

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

В данной ситуации ITP используется следующим образом. С его помощью определяются объекты, обращение к которым происходит наиболее часто. Затем анализируется, насколько затратно столь частое обращение к объекту. Если обнаруживается, что производительность существенно страдает, разработчику следует попытаться изменить архитектуру приложения. Хороший пример - замена глобальных переменных локальными.

Общий порядок работы с инструментом

Порядок работы с Intel® Thread Profiler включает в себя следующие шаги:

  • инструментация приложения;
  • профилирование приложения;
  • анализ полученной информации.

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

Далее мы приведем рассмотрение процессов инструментации и профилирования. О том, как эти процессы осуществляются в ITP, мы расскажем в разделе 3.6.3.

Инструментация приложения

Инструментация представляет собой встраивание в приложение дополнительных вызовов, при помощи которых профилировщик получает информацию о работе приложения. Эти вызовы могут быть добавлены как на уровне исходного кода приложения, так и в уже скомпилированное приложение. В связи с этим различают ин- струментацию исходных кодов (source instrumentation) и бинарную инструмента-цию (binary instrumentation). ITP поддерживает оба этих способа.

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

В связи с этим обычная процедура состоит в следующем: разработчик компилирует свое приложение со всеми необходимыми опциями, а затем использует бинарную инструментацию. Мы рассмотрим этот процесс подробнее в разделе 3.6.2.

Инструментация исходных кодов используется крайне редко. Создатели ITP рекомендуют ее лишь в двух ситуациях:

  • Бинарная инструментация недоступна. Это справедливо для систем, созданных для работы на архитектурах Intel® Itanium и Intel® EM64T.
  • Необходимо запустить инструментированное приложение вне Intel® Thread Profiler.

Далее под инструментацией мы будем понимать именно бинарную инстру-ментацию, поскольку именно с ней нам придется работать.

Профилирование приложения

После того как приложение было инструментировано, можно начинать профилирование. ITP осуществляет запуск приложения, в процессе которого собирает его трассу. В нее включается следующая статистическая информация:

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

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

При профилировании старайтесь следовать следующим советам:

  • Избегайте запускать другие приложения во время профилирования. Деятельность посторонних приложений (особенно потребляющих много ресурсов) может существенно исказить интересующую вас информацию
  • Производите профилирование несколько раз и для анализа выбирайте тот запуск, когда приложение отработало быстрее всего. Этот запуск соответствует ситуации, когда на ваше приложение было меньше всего воздействий, поэто-му эта информация ближе всего к "идеальному" профилю вашего приложения.

Далее мы на конкретном примере познакомимся с графическим интерфейсом ITP и основными приемами работы с ним.

Пример использования Intel Thread Profiler

Целью настоящего раздела является начальное ознакомление с инструментом ITP и общими принципами работы с ним. Изучается процесс подготовки приложения к профилированию, графический интерфейс ITP и основные возможности по анализу производительности многопоточных приложений.

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

Изучение профилируемого приложения

Лабораторная работа проводится на учебном приложении, которое осуществляет факторизацию (разложение на простые множители) чисел из диапазона от 1 до N. Используется алгоритм, который основан на попытке деления факторизуе-мого числа на каждое из чисел меньше его. Если остаток от деления равен нулю, то очередной множитель запоминается, после чего производится повторная попытка деления на это же число. При нахождении каждого множителя факторизуе-мое число делится на него, и алгоритм завершает работу, когда частное от очередного деления становится равным единице. Заметим, что это малоэффективный алгоритм факторизации, поэтому мы не рекомендуем использовать его при решении практических задач. Такой алгоритм используется только в учебных целях -на его примере мы сможем изучить некоторые особенности оптимизации многопоточных приложений.

Откройте проект Factorization, последовательно выполняя следующие шаги:

  • запустите приложение Microsoft Visual Studio 2005;
  • в меню File выполните команду Open—Project/Solution;
  • в диалоговом окне Open Project выберите папку C:\ITPLab\Factorization;
  • дважды щелкните на файле Factorization.sln или, выбрав файл, выполните команду Open.

В окне Solution Explorer дважды щелкните на файле исходного кода Factorization.cpp (рис. 6.20). После этого в рабочей области Microsoft Visual Studio появится программный код, с которым нам предстоит работать.

Открытие файла Factorization.cpp.

Рис. 6.20. Открытие файла Factorization.cpp.

Приступим к изучению приложения. В начале файла Factorization.cpp объявлены две константы.

 
#define NUM_NUMBERS 100000
#define NUM_THREADS 2

Первая из них указывает количество чисел, которые будут факторизованы. В данном случае будет построено разложение для чисел от 1 до 100000. Вторая константа показывает, сколько потоков будет создано для решения этой задачи.

Также объявлен глобальный массив векторов divisors:

 vector<int> divisors[NUM_NUMBERS+1];

Он предназначен для хранения простых множителей каждого из чисел. Так, например, вектор divisors для NUM_NUMBERS=6 будет содержать два числа: 2 и 3.

В данной лабораторной работе нас будут интересовать только функции main и factorizationl.

Ознакомьтесь с кодом функции main. Он содержит объявления переменных, операции создания потоков и ожидания их завершения, а также вывод на экран, предназначенный для контроля правильности результатов.

После этого ознакомьтесь с функцией factorizationl, которая представляет собой рабочую функцию потока. В ней реализуется простейшая стратегия распределения нагрузки между потоками. А именно, первый поток строит разложение для первых NUM_NUMBERS/NUM_THREADS чисел, второй - для второго массива чисел такой же длины, и так далее. Пример распределения нагрузки между двумя потоками представлен на рис. 6.21.

Распределение нагрузки между потоками

Рис. 6.21. Распределение нагрузки между потоками

Скомпилируйте и запустите приложение:

  • В меню Build выберите команду Build Solution;
  • В меню Debug выберите команду Start Without Debugging.

Убедитесь в правильности работы приложения по выводу на консоли.

Дмитрий Остапенко
Дмитрий Остапенко

поддерживаю выше заданые вопросы

 

Павел Каширин
Павел Каширин

Скачал архив и незнаю как ничать изучать материал. Видео не воспроизводится (скачено очень много кодеков, различных плееров -- никакого эффекта. Максимум видно часть изображения без звука). При старте ReplayMeeting и Start в браузерах google chrome, ie возникает script error с невнятным описанием. В firefox ситуация еще интереснее. Выводится: 

Meet Now: Кукаева Светлана Александровна. 

Meeting Start Time: 09.10.2012, 16:58:04
Meeting Stop Time: 09.10.2012, 18:45:18
Recording Duration:01:47:14

Downloading...

Your Web browser is not configured to play Windows Media audio/video files.

Make sure the features are enabled and available.