Материал: Лабораторная работа №6

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Лабораторная работа №6

Тестирование производительности, профилирование приложений”

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

Продолжительность работы - 4 часа.

Оглавление

Нагрузочное тестирование или тестирование производительности 2

Тестирование производительности (Performance testing) 2

Стрессовое тестирование (Stress Testing) 2

Объемное тестирование (Volume Testing) 2

Тестирование стабильности или надежности (Stability / Reliability Testing) 2

Определение целей тестирования производительности 3

Основные тесты производительности 3

Основные показатели (метрики) производительности 4

1. Потребление ресурсов центрального процессора (CPU, %) 4

2. Потребление оперативной памяти (Memory usage, Mb) 4

3. Потребление сетевых ресурсов 5

4. Работа с дисковой подсистемой (I/O Wait) 5

5. Время выполнения запроса (request response time, ms) 6

Профилирование 6

Профилировщик Visual Studio 6

1. Дискретный профилировщик 6

2. Инструментированный профилировщик 10

3. Профилировщик выделения памяти 11

4. Профилировщик конкуренции 13

Порядок выполнения лабораторной работы 15

Нагрузочное тестирование или тестирование производительности

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

Основные виды нагрузочного тестирования:

  • Тестирование производительности;

  • Стрессовое тестирование;

  • Объемное тестирование;

  • Тестирование стабильности или надежности.

Тестирование производительности (Performance testing)

Задачей тестирования производительности является определение масштабируемости приложения под нагрузкой, при этом происходит:

  • измерение времени выполнения выбранных операций при определенных интенсивностях выполнения этих операций;

  • определение количества пользователей, одновременно работающих с приложением;

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

  • исследование производительности на высоких, предельных, стрессовых нагрузках.

Стрессовое тестирование (Stress Testing)

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

Объемное тестирование (Volume Testing)

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

  • измерение времени выполнения выбранных операций при определенных интенсивностях выполнения этих операций;

  • может производиться определение количества пользователей, одновременно работающих с приложением.

Тестирование стабильности или надежности (Stability / Reliability Testing)

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

Определение целей тестирования производительности

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

  • определение "узких мест" системы (функций программно-аппаратного комплекса, обращение к которым приводит к наибольшему падению показателей производительности);

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

  • определение оптимального способа хранения файлов;

  • оценка и оптимизация схемы базы данных в контексте повышения производительности;

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

  • определение характера увеличения времени отклика системы при увеличении нагрузки;

  • определение максимального числа одновременно работающих пользователей, превышение которого делает использование системы невозможным;

  • определение влияния конфигурации системы на производительность;

  • оценка показателей масштабируемости системы;

  • оценка соответствия сетевой инфраструктуры требованиям производительности.

Основные тесты производительности

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

Тест на определение максимальных возможностей системы (capacity test) позволяет определить т.н. "точку насыщения системы" (system saturation point) – уровень нагрузки, при котором дальнейшее наращивание числа пользователей ведёт к увеличению времени отклика системы либо ухудшению стабильности системы, но не к увеличению в единицу времени количества полезных операций, обработанных системой. Данный тест направлен на оценку производительности системы как аппаратно-программного комплекса, поскольку учитывает доступные аппаратные ресурсы и эффективность их использования.

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

Низко-, средне- и высоконагруженная работа (low-, mid-, high-load tests) – позволяет оценить время отклика (response time) системы в некоторых заданных диапазонах нагрузки. Данная информация может быть использована при составлении перечня требований к условиям эксплуатации системы.

Тест на выживаемость (longevity test) показывает способность системы работать длительное время под высокой нагрузкой. Одной из наиболее опасных проблем, выявляемых данным тестом, является утечка памяти и иное снижение эффективности использования аппаратных ресурсов из-за накапливающихся со временем ошибок в работе приложения.

Тест "часа пик" (rush hour test) позволяет оценить реакцию системы на резкое изменение нагрузки. Во время теста проверяется способность системы выдержать скачкообразное увеличение нагрузки во время "часа пик", а также способность системы вернуться к изначальным показателям производительности после завершения "часа пик" (восстановления исходных показателей нагрузки на систему). Такой тест позволяет выявить проблемы с синхронизацией выполнения отдельных участков кода, а также проблемы с управлением всеми видами межкомпонентного взаимодействия (в т.ч. сетевых и локальных соединений) на всех уровнях системы.

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

Основные показатели (метрики) производительности

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

  1. Потребление ресурсов центрального процессора (cpu, %)

Метрика, показывающая, сколько времени из заданного определённого интервала было потрачено процессором на вычисления для выбранного процесса. В современных системах важным фактором является способность процесса работать в нескольких потоках, для того, чтобы процессор мог производить вычисления параллельно. Анализ истории потребления ресурсов процессора может объяснять влияние на общую производительность системы потоков обрабатываемых данных, конфигурации приложения и операционной системы, мультипоточности вычислений, и других факторов.

  1. Потребление оперативной памяти (Memory usage, Mb)

Метрика, показывающая количество памяти, использованной приложением. Использованная память может делиться на три категории:

Virtual — объём виртуального адресного пространства, которое использует процессор. Этот объём не обязательно подразумевает использование соответствующего дискового пространства или оперативной памяти. Виртуальное пространство конечно и процесс может быть ограничен в возможности загружать необходимые библиотеки.

Private — объём адресного пространства, занятого процессом и не разделяемого с другими процессами.

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

При работе приложения память заполняется ссылками на объекты, которые, в случае неиспользования, могут быть очищены специальным автоматическим процессом, называемым «сборщиком мусора» (англ. Garbage Collector). Время затрачиваемое процессором на очистку памяти таким способом может быть значительным, в случае, когда процесс занял всю доступную память (в Java — так называемый «постоянный Full GC») или когда процессу выделены большие объёмы памяти, нуждающиеся в очистке. На время, требующееся для очистки памяти, доступ процесса к страницам выделенной памяти может быть заблокирован, что может повлиять на конечное время обработки этим процессом данных.

  1. Потребление сетевых ресурсов

Эта метрика не связана непосредственно с производительностью приложения, однако её показатели могут указывать на пределы производительности системы в целом.

Пример:

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

Нагрузочное тестирование показало, что эффективно сервер может предоставлять данные только 4 пользователям одновременно, так как мультимедиа-поток имеет битрейт в 500 килобит. Очевидно, что предоставление этого потока 5 пользователям одновременно невозможно в силу превышения пропускной способности сетевого канала, а значит, система не удовлетворяет заданным требованиям производительности, хотя при этом потребление ей ресурсов процессора и памяти может быть невысоким.

  1. Работа с дисковой подсистемой (I/o Wait)

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

  1. Время выполнения запроса (request response time, ms)

Время выполнения запроса приложением остаётся одним из самых главных показателей производительности системы или приложения. Это время может быть измерено на серверной стороне, как показатель времени, которое требуется серверной части для обработки запроса; так и на клиентской, как показатель полного времени, которое требуется на сериализацию / десериализацию, пересылку и обработку запроса. Надо заметить, что не каждое приложение для тестирования производительности может измерить оба этих времени.

Профилирование

Профилирование — это сбор характеристик программы, таких как время выполнения отдельных ее фрагментов (функций, строк).

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

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

Инструменты мониторинга могут быть поделены на две главные группы — встраиваемые блоки и отдельные независимые программы.

Для первой категории существуют такие инфраструктуры как ETW (Event Tracing for Windows) и счетчики производительности. Для второй – библиотеки, которые встраиваются в ваш код, например, New Relic, и полностью готовые системы мониторинга, которые не требуют вмешательства программистов.

Профилировщик Visual Studio

  1. Дискретный профилировщик

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

Сбор информации с использованием профилировщика Visual Studio обходится достаточно недорого, и если интервал следования событий достаточно широк (по умолчанию он составляет 10 000 000 тактов часов CPU), накладные расходы в общем времени выполнения приложения будут составлять менее 5%. Кроме того, дискретный подход к сбору информации позволяет подключиться к выполняющемуся процессу, собрать данные в течение некоторого времени и затем отключиться от процесса, чтобы проанализировать их. Из-за этой особенности, выявление проблем производительности рекомендуется начинать с поиска участков программы, оказывающих самую большую нагрузку на процессор - методов, на выполнение которых тратится больше всего процессорного времени.

По окончании сеанса сбора информации, профилировщик генерирует сводные таблицы, где каждый метод характеризуется двумя значениями: числом исключительных попаданий (exclusive samples), когда в момент отбора очередной порции данных на CPU выполнялся данный метод, и числом включительных попаданий (inclusive samples), когда выполнялся данный метод или вызванный им. Методы с большим количеством исключительных попаданий оказывают самую большую нагрузку на процессор; методы с большим количеством включительных попаданий не используют процессор непосредственно, но вызывают другие методы, суммарно занимающие значительную часть процессорного времени. (Например, в однопоточных приложениях метод Main() будет иметь 100% включительных попаданий.)

Запуск дискретного профелировщика:

  1. В Visual Studio выберите пункт меню Analyze -> Launch Performance Wizard (Анализ --> Запустить мастер производительности).

  2. На первой странице мастера отметьте радиокнопку CPU sampling (Выборка циклов ЦП (рекомендуется)) и щелкните на кнопке Next.

Рисунок 1. Выбор способа профилирования

  1. Если проект для профилирования загружен в текущем решении, щелкните на радиокнопке One or more available projects (Один или несколько доступных проектов) и выберите проект из списка. В противном случае щелкните на радиокнопке An executable (.EXE file) (Исполняемый файл). Щелкните на кнопке Next.

  2. Если на предыдущем шаге вы выбрали параметр An executable (.EXE file), укажите профилировщику путь к выполняемому файлу и аргументы командной строки, если они необходимы, затем щелкните на кнопке Next. (Если у вас нет собственного подходящего приложения, используйте JackCompiler.exe из исходников.)

  3. Оставьте флажок Launch profiling after the wizard finishes (Запустить профилирование после завершения работы мастера) отмеченным и щелкните на кнопке Finish (Готово).

  4. Если среда разработки Visual Studio была запущена не с привилегиями администратора, вам будет предложено повысить привилегии профилировщика.

  5. Когда приложение завершит выполнение, откроется отчет профилирования. Используйте раскрывающийся список Current View (Текущее представление) для навигации между представлениями, отображающими информацию, собранную в процессе профилирования.

По окончании сеанса профилирования можно также воспользоваться обозревателем производительности - Performance Explorer, чтобы запустить его, выберите пункт меню Analyze -> Windows -> Performance Explorer (Анализ --> Окна --> Обозреватель производительности). С его помощью можно изменить параметры профилирования (например, выбрать другой интервал опроса или установить другой критерий), изменить целевой файл и сравнить результаты разных сеансов профилирования.

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

Рисунок 2. Результаты дискретного профилирования из представления Summary

На следующем рисунке показан подробный отчет, где перечислено несколько методов, на выполнение которых затрачено больше всего процессорного времени (имеющих наибольшее количество исключительных попаданий):

Рисунок 3. Результаты дискретного профилирования из представления Functions

Как видно, функция System.String.Concat имеет, как минимум, в два раза больше попаданий, чем любая другая функция. Если выполнить двойной щелчок на методе в списке, откроется окно детализации со строками исходного кода в приложении, в которых обнаружено наибольшее число попаданий:

Рисунок 4. Результаты дискретного профилирования из представления Function Details

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

Помимо количества исключительных/включительных попаданий для каждого метода, профилировщик Visual Studio предлагает массу другой ценной информации. Например, представление Call Tree (Дерево вызовов) отображает иерархию вызовов методов в приложении, представление Lines (Строки) отображает информацию о попаданиях на уровне строк, а представление Modules (Модули) группирует методы по сборкам, и может помочь быстро выяснить, в каком направлении двигаться в поисках узких мест.