--
Программа должна работать (Ну как же без этого!) И еще она должна работать быстро.
Производительность программного обеспечения (ПО) является важным аспектом в разработке любого программного продукта. Актуальность вопроса объясняется постоянно возрастающей сложностью и значимостью программных средств (под значимостью я понимаю рост количества пользователей ПО).
Как нельзя ожидать, что программа будет работать без ошибок с самого первого запуска, так нельзя ожидать того, что программа просто так начнет показывать высокую производительность. В соответствии с принципом Парето, только 20% программного кода будет оказывать существенное влияние на производительность приложения в целом. Эти 20% процентов зачастую называются bottleneck, бутылочным горлышком, или же просто узким местом.
--
Для того, чтобы выявить узкое место в приложении нужны две вещи:
- Требования к производительности. Причем требования должны быть составлены еще на этапе проектирования программного обеспечения.
- Сведения о работе приложения, иначе такие сведения называют значениями характеристик производительности. Такие значения можно получать из различных источников. Можно анализировать отчеты счетчиков производительности. Можно запускать профилировщики и искать проблемы. А можно просто собирать информацию о работе приложения под нагрузкой.
--
Требования должны быть сформулированы на этапе проектирования приложения и уточнены далее на этапе разработки и поддержки. Требования к производительности во многом зависят от назначения приложения и его архитектуры. Было бы глупо требовать от приложений таких как Web-база объема используемой оперативной памяти сравнимых с объемом, требуемых для мобильных приложений.
Рассмотрим примеры плохо сформулированных требований:
- Высокая отзывчивость при одновременном доступе нескольких пользователей.
- Низкий объем потребления памяти при небольшом количестве посетителей.
В данном случае не говорится о конкретных характеристиках и не оговариваются ограничения системы, при которых указываемые характеристики должны удовлетворять требованиям.
Веб-сервер | Время на обработку запроса не более 300 мс | Одновременно не более 300 запросов в секунду |
Веб-сервер | Объем используемой оперативной памяти не более 20 Гб | Одновременно открыто не более 5000 сеансов пользователей |
Клиентское ПО | Время открытия приложения не должно превышать 1500 мс | 8 Гб оперативной памяти |
Клиентское ПО | Нагрузка на ЦП в режиме простоя не должна превышать 1% | Процессор Intel Core i7 4790 3,6 ГГц или AMD FX-9590 4,7 ГГц |
--
Формулировка требований к производительности напрямую зависит от того, какие характеристики производительности мы будем учитывать. В отличие от требований, характеристики не привязаны к каким-то конкретным типам приложений.
|
---------------------|-------------------------------------------------- Нагрузка на CPU | % Использование памяти | Килобайты, мегабайты, гигабайты Время выполнения | Миллисекунды Сборка мусора | Продолжительность % от общего времени выполнения Попадания в кэш | Количество попаданий в секунду
Это весьма общие характеристики, и они характерны большинству приложений, будь то веб сервер или почтовый клиент.
--
Где же найти место вопросам производительности в процессе разработки ПО?
Во-первых, как я говорил раньше. На этапе проектирования приложения необходимо выяснить какие характеристики производительности хотелось бы отслеживать, какие значения этих характеристик хотелось бы получить и при каких условиях планируется эксплуатация разрабатываемого приложения.
На этапе разработки желательно как можно чаще производить замеры интересующих характеристик на прототипах, а также выполнять нагрузочное тестирование, для того, чтобы отслеживать процесс деградации производительности.
Перед каждым релизом обязательно производить полное нагрузочное тестирование.
Стоит отметить, что создание комплекса нагрузочных тестов, изолированного окружения и тщательный анализ результатов отнимает достаточно много времени, однако взамен вы получаете гарантию того, что снижение производительности не пройдет незамеченным.
--
Построение догадок и преждевременных выводов об узких местах в приложении - это самое худшее, что может сделать разработчик.
Однажды, когда я активно вел разработку над мобильным android приложением вместе с Димой Николенко, появилась проблема со стандартными библиотечными методами работы с http запросами. Если не ошибаюсь класс называется UrlConnection. У него какая-то жесть была с поддержкой многопоточных запросов на сервер. Решили найти альтернативу, на глаза попался Apache HttpClient с его ThreadSafeClientConnManager, ну я значит решил все, если есть сочетание Thread Safe значит все в порядке. А что оказалось в итоге, этот ThreadSafeClientConnManager плодил кучу ненужных потоков для каждого соединения из-за чего соответственно приложение тормозить стало.
В конце концов, нашли мы нормальную замену всему этому добру OkHttp называется, от команды Square. На этот раз проверили его работу профилировщиком, поставляемым Android Studio и благополучно пошли спать.
--
Вообще все что перечислено выше является разновидностями подходов к профилированию. Само слово профилирование означает одну из форм динамического анализа кода, которая измеряет какие-либо характеристики системы.
--
По поводу счетчиков производительности на самом деле можно сказать одно, их просто жуть как много, причем безо всяких проблем программист может написать свой счетчик производительности.
Правда есть ограничение, частота обновления информации у Performance Monitor не поднимается выше чем 1 раз/сек. Поэтому для замеров с частотой тысячи раз в секунду данный инструмент не подойдет.
--
Существует огромное множество различный профилировщиков. Профилирование времени является пожалуй одним из самых распространенных методов выявления узких мест в приложении. Особенно веб. Сегодня я хочу поговорить о двух представителях категории профилировщиков времени.
Первый (MiniProfiler) - это маленькая библиотека главной задачей которой является собрать как можно больше информации о времени выполнении каждого запроса в общем и sql в частности. Данная библиотека была написана командой StackExchange, а эти парни знают толк в производительности, я думаю, что большинство программистов так или иначе пользуются проектами StackExchange (если кто не знает StackOwerflow это один из их проектов).
--
MiniProfiler
Как вы думаете что в этом профиле не так? =) Рассказать об SQL N+1 проблеме
Данные логируются в БД и в последствии можно будет строить различные статистики.
--
dotTrace
Позволяет профилировать многопоточные приложения. Позволяет подключать таблицу символов, в результате чего появляется возможность соотносить какая стока кода дает проседание в производительности.
--
dotMemory
Уте́чка па́мяти (англ. memory leak) — процесс неконтролируемого уменьшения объёма свободной оперативной или виртуальной памяти компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные уже участки памяти, или с ошибками системных служб контроля памяти.
Трафик памяти (англ. memory traffic) - количество памяти выделяемое в единицу времени.
--
Microbenchmarking
Некоторые проблемы не могут быть решены с использованием стандартных методов профилирования. Например, необходимо объяснить свой выбор в использовании той или иной структуры данных. Так как профилировщики вносят свои, и причем достаточно весомые накладные расходы в работу приложения во время профилирования, то приходится производить замеры вручную. Это и называется микрохронометраж.
Но ручной хронометраж таит все в себе кучу подводных камней и необходимо постоянно учитывать внешние факторы, чтобы результат измерения был точным:
- Тестирование должно выполняться в среде, близкой по своим характеристикам окружению, в котором должно работать разрабатываемое приложение.
- Тестовые исходные данные по своей структуре должны быть близки фактическим данным.
- Время выполнения кода поддержки, используемого для настройки окружения, должно быть ничтожно мало, по сравнению со временем выполнения тестируемого кода.
- Тестируемый код должен выполняться достаточно долго, чтобы ослабить влияние случайных программных и аппаратных флуктуаций.
- Нужно учитывать различные оптимизации компилятора языка + JIT компилятора
Поэтому самому построить окружение для микрохронометража весьма сложно. И есть вариант - это использовать готовую библиотеку BenchmarkDotNet. О большей части проблем она уже позаботилась и нам по большей части остается, только провести тесты в приближенных условиях.
--
BenchmarkDotNet
Давайте попробуем выяснить что будет быстрее: обойти массив или двусвязный список.
--
Нагрузочное тестирование