Профилирование C++ кода на Linux: как найти узкие места?
std::chrono, но это превращает код в кашу и не даёт полной картины.
Вот упрощённый пример моего кода, который обрабатывает большой набор данных:
#include <vector>
#include <algorithm>
#include <iostream>
void processData(const std::vector<int>& data) {
std::vector<int> result;
for (size_t i = 0; i < data.size(); ++i) {
if (data[i] % 2 == 0) {
result.push_back(data[i] * data[i]);
}
}
// Дальше сложные вычисления...
std::sort(result.begin(), result.end());
for (auto& v : result) {
v = v % 1000;
}
}
int main() {
std::vector<int> data(1000000);
std::generate(data.begin(), data.end(), std::rand);
processData(data);
return 0;
}
Я использую GCC и собираю с флагами -O2 -march=native. Проблема в том, что я не знаю, какие именно части кода (аллокации в векторе, сортировка, или что-то ещё) жрут больше всего времени. Слышал про профилировщики, но не знаю, с какого начать и как их правильно использовать. Подскажите, пожалуйста, надёжный способ профилирования C++ на Linux для начинающих.
<<Используем perf и gprof для профилирования C++ на Linux
Проблема ручных замеров — они искажают результат и не показывают полной картины. Для профилирования C++ кода на Linux есть два отличных инструмента: perf (современный, низкоуровневый) и gprof (классический, но требует перекомпиляции). Рекомендую начать с perf — он встроен в ядро и не требует специальной сборки.
Шаг 1: Базовая профилировка с perf
Установите perf (на Ubuntu: sudo apt install linux-tools-common linux-tools-generic). Скомпилируйте программу с отладочными символами и оптимизацией:
g++ -g -O2 -o myapp myapp.cpp
Запустите профилирование:
sudo perf record ./myapp
sudo perf report
Вы увидите таблицу, где функции отсортированы по проценту времени выполнения. Например, вы можете обнаружить, что std::sort занимает 40% времени, а push_back — ещё 30% из-за реаллокаций.
Шаг 2: Анализ с perf top и аннотацией
Для быстрого просмотра активных функций во время работы используйте:
sudo perf top -p $(pidof myapp)
Чтобы увидеть, какие строки кода самые "горячие", добавьте флаг -l:
sudo perf record -g ./myapp
sudo perf annotate
Это покажет ассемблер с подсветкой инструкций, которые занимают больше всего циклов CPU. Для вашего примера вы увидите, что push_back вызывает реаллокацию вектора, а std::sort — это O(n log n) операция.
Шаг 3: Оптимизация на основе perf
После профилировки вы можете:
- Заменить вектор на
std::vector<int> result;с предварительнымreserve()— это уберёт лишние реаллокации. - Использовать
std::sortс пользовательским компаратором или перейти наstd::partial_sort, если нужны только топ-N элементов. - Применить
std::transformвместо цикла для векторизации.
Пример оптимизированного кода:
void processData(const std::vector<int>& data) {
std::vector<int> result;
result.reserve(data.size()); // Убираем реаллокации
for (auto v : data) {
if (v % 2 == 0) result.push_back(v * v);
}
std::sort(result.begin(), result.end());
for (auto& v : result) v = v % 1000;
}
Альтернатива: gprof (если нет доступа к perf)
Скомпилируйте с -pg:
g++ -g -O2 -pg -o myapp myapp.cpp
./myapp
gprof myapp gmon.out
Результат — текстовый отчёт с вызовами функций и временем. Минус: gprof добавляет оверхед и не работает с многопоточностью.
Советы для новичков
- Всегда компилируйте с
-gдля читаемых имён функций. - Используйте
-O2(или-O3) — профилировка без оптимизации бессмысленна. - Для многопоточных приложений используйте
perf record -e sched:sched_switchдля учёта переключений контекста. - Попробуйте Valgrind --tool=callgrind для детального анализа кэша и инструкций (но он медленный).
В вашем случае perf покажет, что основное время уходит на сортировку и реаллокацию. Исправьте их — и программа ускорится в 2-3 раза.
<<Профилирование с помощью Google Performance Tools (gperftools)
Если perf кажется слишком низкоуровневым, попробуйте gperftools от Google. Он предоставляет CPU-профилировщик и хип-профилировщик (для анализа памяти). Установка:
sudo apt install google-perftools libgoogle-perftools-dev
Скомпилируйте программу с линковкой библиотеки:
g++ -g -O2 -o myapp myapp.cpp -lprofiler
Запустите с переменной окружения:
CPUPROFILE=myapp.prof ./myapp
pprof --text myapp myapp.prof
Вы получите текстовый отчёт с процентами времени по функциям. Для визуализации используйте pprof --pdf (нужен Graphviz). Плюс gperftools — он не требует прав root и даёт наглядные графики вызовов.
Для вашего примера gperftools покажет, что std::sort и push_back (с реаллокациями) — главные "пожиратели" времени. Рекомендую также запустить heapprofiler (HEAPPROFILE=myapp.heap) для анализа утечек и частоты аллокаций.
Минус: gperftools добавляет небольшой оверхед (около 10-20%), но для большинства задач это приемлемо.
Ответов пока нет.
Войдите чтобы оставить ответ