Профилируем использование памяти в программах на C/C++ при помощи Heaptrack
7 ноября 2016
Рассмотрим типичную задачу. Есть программа на C или C++ с исходниками. Известно, что при выполнении определенных условий программа начинает отжирать слишком много памяти. Нужно понять, почему это происходит, и по возможности исправить. Инструменты, которые мы рассматривали до этого, например, в заметке Профилирование кода на C/C++ в Linux и FreeBSD, для этого явно не подходят. Спрашивается, что же тогда делать? На помощь приходит Heaptrack!
Примечание: Heaptrack является аналогом Valgrind Massif, только, в отличие от него, работает намного быстрее. См также заметку Поиск ошибок работы с памятью в C/C++ при помощи Valgrind.
Для установки Heaptrack я воспользовался этим AUR (см заметку Управление пакетами в Arch Linux с помощью ABS и pacman). Также нам понадобятся Massif Visualizer и его зависимость KDiagram, которые также доступны в AUR. Если вы используете дистрибутив, отличный от Arch Linux, уверен, для него в каком-то виде данные пакеты тоже есть. В крайнем случае, можно посмотреть файлы PKGBUILD у Арча и собрать все из исходников таким же образом. Насколько мне известно, в настоящее время Heaptrack работает только под Linux. Поэтому, если вы используете другую ОС… ну что ж, хреново быть вами :)
Пользоваться Heaptrack очень просто. Для примера натравим его на программку из Не унылого поста о списках и деревьях поиска в языке C:
# или: heaptrack -p PID
heaptrack_print --print-leaks \
--print-histogram histogram.data \
--print-massif massif.data \
--print-flamegraph flamegraph.data \
--file ./heaptrack.test_rbtree.22023.gz > report.txt
Ключ --print-leaks
включает вывод информации о потенциальных утечках памяти (отключено по умолчанию), --print-histogram
включает запись статистики по размерам выделенной памяти, а --print-massif
создает файл, пригодный для просмотра в Massif Visualizer. Что делат ключ --print-flamegraph
попробуйте угадать сами :) Файл flamegraph.data будет использован чуть ниже.
Полный пример отчета можно посмотреть здесь. Наиболее интересная его часть:
4.98MB peak memory consumed over 31111 calls from
tree_allocfunc
at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:37
in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
1.24MB consumed over 7777 calls from:
rb_insert
in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
left_right_walk_test
at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:166
in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
run_tests
at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:430
in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
main
at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:458
in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
Вполне похоже на правду! Действительно, почти вся память должна выделяться во время вставки в RB-дерево.
Если текстовый отчет вам не кажется слишком наглядным, попробуйте запустить Massif Visualizer:
Выглядит он приблизительно так (кликабельно, PNG 1366x747, 224 Кб):
Кроме того, можно построить флеймграф:
./FlameGraph/flamegraph.pl flamegraph.data > flamegraph.svg
На красивые флеймграфы мы смотрели уже тысячу раз, так что картинку здесь не привожу.
Собственно, это все! А чем вы профилируете использование оперативной памяти?
Метки: C/C++, Linux, Оптимизация.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.