- PVSM.RU - https://www.pvsm.ru -
Всем привет. Несколько месяцев назад мы вместе с victorzs [1] решили сделать простой и удобный профилировщик c++ кода (подразумевается профилирование времени исполнения участков кода, функций).
[2]
Скриншот профилирования примера из SDK CryEngine
Существующие решения нам не подходили по ряду причин. Нам нужен был качественный профайлер, умеющий делать следующее:
В результате тщательной проработки появился на свет профайлер [3], умеющий делать всё вышеперечисленное, и даже больше!
Если вы хотите знать, сколько времени работает ваш код, и иметь при этом объективные доказательства — прошу под кат, где я покажу, как использовать профилировщик.
<easy_profiler_release_dir>/include
<easy_profiler_release_dir>/bin
BUILD_WITH_EASY_PROFILER
#include <easy/profiler.h>
void foo() {
EASY_FUNCTION(profiler::colors::Magenta);// Начать блок с именем, совпадающим с именем функции
EASY_BLOCK("Calculating sum");// Блок с цветом по умолчанию
int sum = 0;
for (int i = 0; i < 10; ++i) {
EASY_BLOCK("Addition", profiler::colors::Red);// Блок будет закончен при выходе из области видимости
sum += i;
}
EASY_END_BLOCK; // Закончить блок (в данном случае блок с именем "Calculating sum"
EASY_BLOCK("Calculating multiplication", profiler::colors::Blue500);
int mul = 1;
for (int i = 1; i < 11; ++i)
mul *= i;
//на выходе из функции автоматически будут закрыты все открытые и незавершённые в этой функции блоки. В данном примере, автоматически закроются блоки с именами "Calculating multiplication" и "foo"
}
PATH
(в линуксе достаточно в LD_LIBRARY_PATH
) директорию <easy_profiler_release_dir>/bin
Добавленные блоки в режиме сбора статистики занимают минимально-возможное время (как мы этого добились — в дальнейших статьях о технической реализации). На машине с процессором Core i7-5930K 3.5GHz, 16 Gb RAM, Win7 Pro в приложении с 12 потоками средняя «стоимость» одного блока — порядка 10-15 наносекунд! Подобный результат достигнут и на Fedora 22 . Вот график замеров (по оси x — количество блоков, по y — наносекунд на блок):
Кроме того, видно, что зависимость линейная — количество блоков не влияет на временную характеристику.
Получение и анализ результатов происходит в программе с незамысловатым названием profiler_gui (в директории bin). Инициализация профилоровщика возможна двумя способами:
profiler::startListen();
Данная функция запускает поток, который слушает по порту 28077
(порт можно поменять параметром в функции profiler::startListen(portNumber)
) команды управления. Остановить прослушивание можно вызовом функции (хотя это совсем не обязательно):
profiler::stopListen();
Сбор блоков начинается после коннекта profiler_gui к профилируемому приложению и нажатия на кнопку «Capture» на тул-баре. После остановки профилирования (нажать на «Stop») собранная информация передается через сокет из профилируемого приложения в profiler_gui и сразу же сохраняется на диск в файл easy_profiler.cache. Можно также сохранить всю информацию в отдельный файл (при этом просто происходит перемещение файла easy_profiler.cache).
int main()
{
EASY_PROFILER_ENABLE;
/* do work*/
profiler::dumpBlocksToFile("test_profile.prof");
}
После этого сохранённые файлы можно открыть в программе profiler_gui
Для получения информации о переключении контекста в Windows необходимо запускать профилируемое приложение с правами администратора. В linux дело обстоит чуть сложнее: необходимо запускать с привилегиями суперпользователя скрипт, находящийся в директории scripts/context_switch_logger.stp
с параметрами. Данный скрипт интерпретируется программой systemtap [5]. В Fedora нужно выполнить команду:
#stap -o /tmp/cs_profiling_info.log scripts/context_switch_logger.stp name APPLICATION_NAME
Где APPLICATION_NAME
— имя профилируемого приложения, файл /tmp/cs_profiling_info.log
— файл, куда записывается информация о переключениях контекста. Привилегии суперпользователя необходимы потому, что информацию о переключении контекста возможно получить только в пространстве ядра.
Для демонстрации возможностей анализатора результатов попрофилируем простой пример из CryEngine. В самом CryEngine есть несколько профилировщиков и для их организации существуют макросы, в которые легко встроить любой профайлер.
После компиляции запускаем тестовый пример, запускаем программу profiler_gui, коннектимся к приложению (иконка: , рядом с ней можно ввести ip-адрес или имя хоста, на котором запущено профилируемое приложение). После удачного коннекта (иконка немного позеленеет: ) можно запускать сессию профилирования. После нажатия на кнопку начнётся сбор статистики в профилируемом приложении. Для завершения сессии профилирования нужно закрыть появившееся окошко.
На скриншоте представлен общий вид программы с результатом
В верхней части окна представлены запущенные потоки и сохранённые блоки, длительность которых можно оценить по горизонтальный шкале. Вертикально в рамках каждого блока показывается его иерархия.
В центральной части представлена диаграмма времён либо потока, либо выбранного блока. Здесь время исполнения блока оценивается по вертикали, по горизонтали — время выполнения программы. т.е. можно смотреть всплески длительности блоков и при необходимости более детально оценить проблему.
В нижней части представлено дерево выполнения блоков для выбранного участка с подробнейшей статистикой. Здесь можно сортировать по длительности, искать самые долгие блоки, оценивать количество вызовов того или иного блока. Выбор участка осуществляется в верхней части экрана зажатием правой кнопки мыши и выделением необходимого куска.
Краткую статистику по блоку можно посмотреть в верхней части экрана. После наведения курсора на блок — появляется всплывающее окошко с краткой сводкой:
В этой сводке информация по общей длительности суммарно всех блоков такого типа и сколько эта сумма составляет процентов от фрейма (самый верхний родитель для данного блока), от суммарного времени потока и от своего родителя. Во многих случаях это исчерпывающая информация.
Ещё одной очень удобной фичей является динамическое включение/отключение блоков. Для этого надо открыть диалог (иконка ) и в появившемся окне включить или отключить желаемые блоки. При следующей сессии профилирования эти настройки будут учтены.
Отключаем сбор информации для функции C3DEngine::GetWaterLevel
Итак, преимущества профилировщика:
— Скорость работы
— Минимальные затраты памяти
— Кроссплатформенность
— Удобное и функционально графическое представление
Единственным ограничением использования является необходимость сборки профилируемого приложения компилятором, поддерживающим стандарт c++11.
Данный профилировщик будет полезен как для разработчиков движков игр (как ИИ, так и 3D), так и для тех, кто использует уже готовые движки, да и для всех, кто заботится о производительности своего приложения. Данный профайлер используется нами в рамках разработки системы визуализации для авиационных и тактических тренажёров.
Спасибо за внимание! С удовольствием ждём обратной связи (вопросы, пожелания, баги, звёздочки на гитхабе, пулл-реквесты). В процессе разработки были решены кое-какие нестандартные задачи, о чём хочется написать отдельные статьи.
Автор: yse
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/proizvoditel-nost/239451
Ссылки в тексте:
[1] victorzs: https://habrahabr.ru/users/victorzs/
[2] Image: https://habrahabr.ru/post/318142/
[3] профайлер: https://github.com/yse/easy_profiler
[4] https://github.com/yse/easy_profiler/releases: https://github.com/yse/easy_profiler/releases
[5] systemtap: https://sourceware.org/systemtap/
[6] Источник: https://habrahabr.ru/post/318142/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.