← На главную

Профилируем использование памяти в программах на C/C++ при помощи Heaptrack

Рассмотрим типичную задачу. Есть программа на 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 ./test_rbtree # или: 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 будет использован чуть ниже.

Полный пример отчета можно посмотреть здесь. Наиболее интересная его часть:

PEAK MEMORY CONSUMERS 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:

massif-visualizer massif.data

Выглядит он приблизительно так (кликабельно, PNG 1366x747, 224 Кб):

Massif Visualizer

Кроме того, можно построить флеймграф:

git clone https://github.com/brendangregg/FlameGraph ./FlameGraph/flamegraph.pl flamegraph.data > flamegraph.svg

На красивые флеймграфы мы смотрели уже тысячу раз, так что картинку здесь не привожу.

Собственно, это все! А чем вы профилируете использование оперативной памяти?