necrotigr — Профилировщики для С/С++
Как я и обещал в комментарии к посту о тематической неделе программирования в Линукс, выкладываю небольшое сравнение профилировщиков для С/С++. Недавно по работе как раз понадобилось посмотреть скорость выполнения нескольких функций в одной программе, поэтому и решил выбрать себе какой-нибудь инструмент, чтоб и в будущем пригодился.
Этой мой первый пост на welinux, поэтому не судите строго :) Конструктивная нефанатичная критика, замечания и дополнения приветствуются :)
gprof
Самый распространенный, наверное, профилировщик для *nix-систем. В Арче входит в пакет binutils.
Использование:
Чтобы воспользоваться gprof, нужно скомпилировать программу с ключом -pg, например:
После этого запускаем программу как обычно, после отработки программы остаётся файл gmon.out
Запускаем профилировщик:
Он выдаст много-много букаф, поэтому советую сразу поставить на gprof алиас c ключом --brief, который удалит всяческие подробные пояснения. Останутся только 3 части:
- flat profile: сколько времени (в миллисекундах и в процентах от общего времени выполнения программы) было потрачено на выполнение каждой функции. Обратите внимание, что время здесь - процессорное, а не реальное, т.е. выполнение sleep(1000) фактические приравнивается к нулю :)
- call graph - сколько раз вызывалась функция и откуда именно (из какой функции). Штука очень полезная, но в табличном исполнении gprof совершенно непонятная.
- index by function name - соответствие имен функций их индексам для отображения в таблицах.
С помощью статистики от gprof легко можно заметить только явные отклонения: какая-нибудь функция вдруг выполняется раз в 1000 раз медленнее, чем остальные. Это неплохо, конечно, но по сравнению с другими утилитами - мало. И не дай Бог вам прогнать gprof'ом какую-нибудь программу на С++ c использованием STL - статистика будет совершенно нечитабельной, без последующей обработки cut'ом, sed'ом, perl'ом - не разберёшься.
callgrind
Эта утилита - часть известного инструмента valgrind, также довольно распространенного в никсах.
Использование:
Особых флагов компиляции не требует - запускать можно сразу.
Тоже советую сразу добавить алиас. Кстати говоря, для своей работы valgrind требует подмонтированной procfs, в Линуксе обычно она монтируется по умолчанию, во FreeBSD (по крайней мере, 5.3, под которой я его запускал - нет).
После отработки программы callgrind оставит файл callgrind.out.pid_запущенного_процесса. Анализировать этот файл можно двумя путями:
- в консоли с помощью callgrind_annotate
- через GUI с помощью KCacheGrind
Путь первый:
Без параметров эта утилита выдаст только список наиболее используемых в программе функций (в том числе системных) с указанием библиотек, что не очень информативно. Путём экспериментов выявил вот такой набор флагов:
Сперва идёт тот же самый список всех функций, только более подробный, затем - annotated source, где можно посмотреть, сколько раз выполнялась каждая строчка кода, а также какие функции эта строчка неявно вызывала.
Путь второй:
Открыть файл callgrind.out.pid с помощью KCacheGrind. И тут открывается куча возможностей для нефанатично настроенных против KDE пользователей :)
- с помощью комбобокса группировки можно посмотреть как статистику вызовов по всем без исключения функциям, так и только по пользовательским.
- время на выполнение каждой функции в процентах от выполнения всей программы (callgrind_annotate почему-то этой фичи не предоставляет )
- просмотр ассемблерного кода функций - если callgrind был запущен с параметром --dump-instr=yes
- исходник с построчным указанием числа выполнений каждой функции
- и, самая главная фича - красивый и наглядный график вызовов фунций
Кроме того, KCacheGrind нормально работает с C++-ными именами функций, по крайней мере, они вполне читабельно выглядят в графике вызовов.
.
oprofile
Честно говоря, не совсем понял назначения этой утилиты, а точнее - целого пакета утилит. Похоже, что его можно использовать для отладки различных функций ядра, но зачем мне это нужно - я не понял...
Использование:
Чтобы воспользоваться возможностями oprofile нужно запустить демона oprofiled от рута.
(последняя опция - т.к. у меня нет обычного образа ядра, только сжатое vmlinuz. Впрочем, на всех дистрибутивах сейчас именно сжатое ядро, насколько я знаю)
Это большой минус - дома-то ещё можно себе позволить, а вот на работе могут быть проблемы, связанные с корпоративной политикой безопасности. Проще говоря, без рутовского пароля ничего не отпрофилируешь.
Запустив демон, можно использовать утилиты opreport, opannotate и прочие, чтобы получить такие вот отчеты. Однако получить мне их так и не удалось, все программы ругались то на отстутствие прав, то на не найденную session dir. Я расстроился и дальше разбираться не стал - и так, на мой взгляд, не очень удобная в использовании штука.
google-perftools
Профилировщик, которым пользуются в Гугле. В принципе, это его главное достоинство, по остальным параметрами примерно аналогичен callgrind'у.
Использование:
Программа должна быть собрана с библиотекой libprofiler:
Можно также выделит код для профилирования функциями ProfilerStart() и ProfilerStop() (в Callgrind есть такие опции, но тут, имхо, проще)
И запускаем программу
env CPUPROFILE=/tmp/profile_test.prof ./profile_test
После отработки программы анализируем файл, указанный в CPUPROFILE с помощью утилиты pprof:
Утилита неплохая, может выдавать результат в различных форматах, в том числе и в формате callgrind. В общем - тот же callgrind с более удобным интерфейсом и большим числом форматов отчёта.
Резюме: лучшим из рассмотренных профилировщиков я считаю связку callgrind + KCacheGrind, преимуществами которой являются:
- детальная статистика
- читабельная статистика по С++
- наглядное представление отчетов
- распространенность
Этой мой первый пост на welinux, поэтому не судите строго :) Конструктивная нефанатичная критика, замечания и дополнения приветствуются :)
gprof
Самый распространенный, наверное, профилировщик для *nix-систем. В Арче входит в пакет binutils.
Использование:
Чтобы воспользоваться gprof, нужно скомпилировать программу с ключом -pg, например:
gcc -pg -o profile_test profile_test.c
После этого запускаем программу как обычно, после отработки программы остаётся файл gmon.out
Запускаем профилировщик:
gprof profile_test gmon.out
Он выдаст много-много букаф, поэтому советую сразу поставить на gprof алиас c ключом --brief, который удалит всяческие подробные пояснения. Останутся только 3 части:
- flat profile: сколько времени (в миллисекундах и в процентах от общего времени выполнения программы) было потрачено на выполнение каждой функции. Обратите внимание, что время здесь - процессорное, а не реальное, т.е. выполнение sleep(1000) фактические приравнивается к нулю :)
- call graph - сколько раз вызывалась функция и откуда именно (из какой функции). Штука очень полезная, но в табличном исполнении gprof совершенно непонятная.
- index by function name - соответствие имен функций их индексам для отображения в таблицах.
С помощью статистики от gprof легко можно заметить только явные отклонения: какая-нибудь функция вдруг выполняется раз в 1000 раз медленнее, чем остальные. Это неплохо, конечно, но по сравнению с другими утилитами - мало. И не дай Бог вам прогнать gprof'ом какую-нибудь программу на С++ c использованием STL - статистика будет совершенно нечитабельной, без последующей обработки cut'ом, sed'ом, perl'ом - не разберёшься.
callgrind
Эта утилита - часть известного инструмента valgrind, также довольно распространенного в никсах.
Использование:
Особых флагов компиляции не требует - запускать можно сразу.
valgrind --tool=callgrind profile_test
Тоже советую сразу добавить алиас. Кстати говоря, для своей работы valgrind требует подмонтированной procfs, в Линуксе обычно она монтируется по умолчанию, во FreeBSD (по крайней мере, 5.3, под которой я его запускал - нет).
После отработки программы callgrind оставит файл callgrind.out.pid_запущенного_процесса. Анализировать этот файл можно двумя путями:
- в консоли с помощью callgrind_annotate
- через GUI с помощью KCacheGrind
Путь первый:
callgrind_annotate callgrind.out.<pid>
Без параметров эта утилита выдаст только список наиболее используемых в программе функций (в том числе системных) с указанием библиотек, что не очень информативно. Путём экспериментов выявил вот такой набор флагов:
callgrind_annotate --auto=yes --context=3 --inclusive=no --tree=caller
Сперва идёт тот же самый список всех функций, только более подробный, затем - annotated source, где можно посмотреть, сколько раз выполнялась каждая строчка кода, а также какие функции эта строчка неявно вызывала.
Путь второй:
Открыть файл callgrind.out.pid с помощью KCacheGrind. И тут открывается куча возможностей для нефанатично настроенных против KDE пользователей :)
- с помощью комбобокса группировки можно посмотреть как статистику вызовов по всем без исключения функциям, так и только по пользовательским.
- время на выполнение каждой функции в процентах от выполнения всей программы (callgrind_annotate почему-то этой фичи не предоставляет )
- просмотр ассемблерного кода функций - если callgrind был запущен с параметром --dump-instr=yes
- исходник с построчным указанием числа выполнений каждой функции
- и, самая главная фича - красивый и наглядный график вызовов фунций
Кроме того, KCacheGrind нормально работает с C++-ными именами функций, по крайней мере, они вполне читабельно выглядят в графике вызовов.
.
oprofile
Честно говоря, не совсем понял назначения этой утилиты, а точнее - целого пакета утилит. Похоже, что его можно использовать для отладки различных функций ядра, но зачем мне это нужно - я не понял...
Использование:
Чтобы воспользоваться возможностями oprofile нужно запустить демона oprofiled от рута.
opcontrol --start --no-vmlinux
(последняя опция - т.к. у меня нет обычного образа ядра, только сжатое vmlinuz. Впрочем, на всех дистрибутивах сейчас именно сжатое ядро, насколько я знаю)
Это большой минус - дома-то ещё можно себе позволить, а вот на работе могут быть проблемы, связанные с корпоративной политикой безопасности. Проще говоря, без рутовского пароля ничего не отпрофилируешь.
Запустив демон, можно использовать утилиты opreport, opannotate и прочие, чтобы получить такие вот отчеты. Однако получить мне их так и не удалось, все программы ругались то на отстутствие прав, то на не найденную session dir. Я расстроился и дальше разбираться не стал - и так, на мой взгляд, не очень удобная в использовании штука.
google-perftools
Профилировщик, которым пользуются в Гугле. В принципе, это его главное достоинство, по остальным параметрами примерно аналогичен callgrind'у.
Использование:
Программа должна быть собрана с библиотекой libprofiler:
gcc -o profile_test profile_test.c -lprofiler
Можно также выделит код для профилирования функциями ProfilerStart() и ProfilerStop() (в Callgrind есть такие опции, но тут, имхо, проще)
И запускаем программу
env CPUPROFILE=/tmp/profile_test.prof ./profile_test
После отработки программы анализируем файл, указанный в CPUPROFILE с помощью утилиты pprof:
pprof ./profile_test /tmp/profile_test.prof
Утилита неплохая, может выдавать результат в различных форматах, в том числе и в формате callgrind. В общем - тот же callgrind с более удобным интерфейсом и большим числом форматов отчёта.
Резюме: лучшим из рассмотренных профилировщиков я считаю связку callgrind + KCacheGrind, преимуществами которой являются:
- детальная статистика
- читабельная статистика по С++
- наглядное представление отчетов
- распространенность