Производительность приложений может оказаться решающим показателем для многих предприятий. Если уж на то пошло, то стоимость размещения серверного оборудования напрямую влияет на ваш чистый доход. Именно поэтому если вы будете использовать инструменты профилирования производительности для отладки запускаемого кода, вы сможете в итоге сэкономить ваши деньги.
На что стоит обратить внимание
«Узкое место» - это любая медленно работающая часть вашего приложения, которая замедляет остальную часть, которая работает быстрее, прямо как крышка на бутылке с водой или узкая дорога, которая мешает движению транспорта. Абсолютно любой код, который вы пишете, с большей долей вероятности имеет узкие места, и неважно, маленькие они или большие, вы в любом случае можете использовать инструменты профилирования производительности, чтобы их найти.
Все программы разные, но, как правило, страдают от одних и тех же проблем:
- Функции вызываются слишком часто (кэширование или планирование могут уменьшить количество вызовов).
- Код блокировки ввода-вывода, обычно сюда относится синхронный доступ к диску, но иногда и чрезмерное использования памяти.
- Большое количество циклов с затратными методами.
- Длительное время запуска, особенно в JIT-компилируемых языках.
- Нецелесообразное выделение памяти, особенно при работе программы сборки мусора.
- Области, которые выиграют от распараллеливания или асинхронного программирования.
При проверке вашего кода с помощью профилировщика вы сможете выявить любые из них. Даже если у вашего приложения нет какого-то серьезного заметного узкого места, в любом случае даже минимальный процент улучшения поспособствует тому, что ваше приложение будет работать быстрее и эффективнее. А увеличение скорости работы хотя бы на несколько процентов, может в перспективе привести к значительным результатам.
Кроме того, есть вероятность, что узким местом вашего приложения может оказаться не серверный код, а его расположение в вашей общей сети. Например, у вас есть API, который подключается к медленной базе данных. И здесь не имеет значения, насколько быстро работает ваш веб-сервер, если он всегда долго ждет результатов. Профилировщики производительности могут помочь решить проблемы только в рамках вашего кода, но никак не в общей сетевой структуре.
Как работает профилирование?
Инструменты профилирования производительности немного отличны от инструментов отладки. IDE использует такие инструменты отладки, как точки останова и инспектирование, для тестирования и решения проблем в процессе разработки. Профилировщики обычно работают, предполагая, что вы не знаете, в чем заключается проблема, и хотите профилировать код целиком, чтобы ее выявить. Профилировщик подключается к вашему приложению и с помощью высокоточного таймера отслеживает, какие функции работают дольше всего. Спустя какое-то время у вас будет достаточное количество данных, чтобы понять причину проблемы.
Большая часть профилировщиков представляют данные в виде стека, отсортированного по «потребителям» с наибольшим временем. Чаще всего в профилировщике используют Flame graph, который наглядно отображает разбиение всей истории вызовов программы.
Выбор определенного инструмента или метода зависит от того, для какого языка программирования или для какой среды выполнения кода вы профилируете, и от того, требуется ли вам профилирование приложения в производственных средах или нет, но в общих чертах идея одна и та же.
Так как каждый профилировщик должен быть интегрирован в исполняемый код, то вам необходимо будет загрузить профилировщик для языка, который использует ваше приложение. Некоторые из них проще в использовании, нежели другие, особенно это касается таких языков, как C# и Java. Профилировщик проще внедрить в приложение, которое использует именно эти языки, а не компилируемые.
Многие IDE имеют инструменты профилирования, которые встроены поверх стандартного набора инструментов отладки. Их вы также можете использовать. Например, Visual Studio может профилировать производительность и использование памяти во многих приложениях.
- Java – JProfiler, IDEA/Eclipse/Netbeans IDEs
- Python – cProfiler, Palanteer
- JavaScript – Chrome DevTools
- C# – dotTrace, Visual Studio IDE
- C, C++ – Orbit
Если вы уже примерно знаете, что может занимать времени больше, чем положено, то вы всегда можете воспользоваться библиотекой, которая имеет в своем арсенале секундомер, для того, чтобы провести оценку производительности.
Например, Benchmark.NET может проводить высокоточные тестирования различных функций и, как правило, используется для сравнения различных алгоритмов. Также можно использовать обычный класс Stopwatch для какого-то фрагмента кода, который вы хотите протестировать.
Использование профилировщика производительности
В этом руководстве мы покажем, как использовать dotTrace. Это полнофункциональный профилировщик производительности для приложений платформы .NET, в котором есть большая часть инструментов, которые есть и в других профилировщиках. Если вы профилируете код, написанный не на C#, то вам, скорее всего, потребуется другое приложение, но общий принцип остается тем же.
Открыв приложение, вы можете подключиться к запущенным .NET процессам или настроить свою собственную конфигурацию запуска для того, чтобы вы могли запускать приложение прямо из dotTrace. Запуск приложения непосредственно из профилировщика может оказаться довольно полезным, если вы пытаетесь устранить проблему, связанную с медленным запуском приложения.
Как только вы запустите приложение, профилировщик начнет собирать данные. Он может работать столько, сколько вы захотите, а для того, чтобы открыть анализ за накопленный период времени, вам нужно просто нажать «Get Snapshot and Wait».
Как только он откроется, вы увидите большое количество графиков рядом со стеком и деревом вызовов, которые, и, скорее всего, они будут нечитабельными. Если вы видите много всего, связанного с потоками, блокировками и ожиданием, то это, скорее всего, потому, что вам нужно оценить «основной поток».
Профилировщик перехватывает все потоки, которые часто используются для фоновых задач и которые простаивают в течение длительного периода времени. Несмотря на то, что это может говорить о проблемах с блокировкой ввода-вывода, здесь все куда более неоднозначно, чем вы можете себе представить, и это действительно зависит от того, какие действия выполняет поток.
dotTrace также имеет функцию фильтрации кода с точки зрения области, в которой он работает. Для этого используются фильтры «Subsystems» слева. Там можно найти системный код (system code), машинный код (native code) и другие запаздывающие области, такие как отражение (reflection), коллекции (collections), строки (string) и LINQ.
В главном окне вы можете найти Flame graph. Он показывает разбиение всего вашего приложения, начиная с «All Calls» (все вызовы) и разбивая дальше время, которое требуется на выполнение каждого уровня функций. Некоторые из них будут неразрешенными, а некоторые будут слишком малы, чтобы отобразить их на графике, но его можно увеличить до любого масштаба, чтобы увидеть любую функцию и более детальное разбиение стека вызовов.
Еще одна важная функция профилировщиков производительности – это дерево вызовов. Оно показывает вложенное разбиение самых быстрых функций, отсортированных по времени, которое необходимо для их выполнения. Здесь dotTrace также показывает процентное соотношение, то есть долю общего времени, которое данная функция и ее дочерние элементы тратят на свое выполнение.
Затраченное время ЦП не всегда является проблемой, особенно есть речь идет о таком языке, как C#, и если у него есть программы сборки мусора. dotTrace также отслеживает использование и распределение памяти, и его можно использовать для того, чтобы определить, что оказывает нежелательную нагрузку на вашу программу сборки мусора.