Профилирование проекта С++ с точки зрения времени выполнения

Мне нужна помощь с точки зрения профилирования существующего кода с точки зрения времени выполнения. Цель состоит в том, чтобы ускорить его.

Мне был предоставлен код, который ранее работал. Он полностью написан на С++ с концепциями OO. Для него есть интерфейс, основанный на графическом интерфейсе, и при выборе определенной опции выполняется выбор фрагмента кода. (В рамках проекта около 11 классов).

Я хочу иметь возможность нажимать параметр GUI и запускать код и генерировать карту ресурсов, например:

Functions of Class 1 = 20% of execution time
Functions of Class 2 = 60% of execution time
Functions of Class 3 = 10% of execution time
Functions of Class 4 = 10% of execution time

Таким образом, я знаю, какой класс занимает больше всего времени, а затем знает, над чем работать и улучшаться. Однако я понятия не имею, как это сделать. У меня есть только базовые знания на C++.

Я прочитал этот пост: найти время выполнения С++, однако, поскольку программа не является последовательной. Один класс вызывает другого, и это вызывает другое, я не знаю, как системные часы/тики будут реализованы?

Я читал о программе, такой как Valgrind, Zoom, Poor Man Profiler и т.д., но, честно говоря, понятия не имею, как интегрировать ее с кодом. Есть ли более простой метод?

Я также прочитал этот метод: Как я могу создать код на С++, работающий в Linux?, однако я не вижу, как я мог получить информацию с пин-точками относительно на основе класса (класс 1, класс 2 и т.д.)

Может ли кто-нибудь проконсультироваться с новичком?

Ответы

Ответ 1

Valgrind (subtool callgrind) довольно прост в использовании. Вам просто нужно убедиться, что в вашу программу скомпилирована/увязана достаточная информация об отладке, так что callgrind может найти имена различных функций, которые вызываются. Затем вместо прямого вызова вашей программы передайте его (и его аргументы) в качестве параметров valgrind, например:

valgrind --tool=callgrind --trace-children=yes <myprogram> <myprogram_args>

(- trace-children существует, если ваш реальный исполняемый файл скрывается за некоторым слоем или слоями сценариев оболочки)

Обратите внимание, что ваша программа будет работать намного медленнее (например, на 100x медленнее), поскольку отслеживается каждая точка входа в функцию.

Существуют различные инструменты для изучения вывода callgrind, в частности kcachegrind/qcachegrid.

В качестве альтернативы вы можете измерить тики системных тактовых импульсов для небольшого количества высокоуровневых функций (так что вы видите "время, которое выполняет функция X и все под ним" ), и прогресс вниз по вашему коду, когда вы находите горячие точки.

Что-то вроде этого (концептуально, должно быть правильно организовано в заголовки/источники):

struct FunctionTimer {
  FunctionTimer(char const * name) : mName(name), mStartTime(clock()) { }
  ~FunctionTimer() { mFunctionTimes[mName] += clock() - mStartTime; }

  static void report()
  {
    ... iterate through mFunctionTimes, printing out
    the names and accumulated ticks ...
  }

  std::string mName;
  clock_t mStartTime;

  static std::map<std::string, clock_t> mFunctionTimes;
};

...

void myfunc()
{
  FunctionTimer ft("myfunc");
  ... code of myfunc ...
}

...

int main(int argc, char* argv[])
{
  ... do stuff ...
  FunctionTimer::report();
}

Ответ 2

Уродливое решение состоит в том, чтобы запускать и останавливать таймеры вокруг каждой интересующей функции, добавляя время к некоторой глобальной переменной после каждого вызова. Затем, в конце основного, вы просто сравниваете переменные для вычисления процента времени.

Это, однако, может стать очень грубым, особенно если есть много функций. Если вы знакомы с аспектом, ориентированным на аспект С++, вы можете временно использовать его, потому что аспект позволит вам более легко разместить этот шаблонный код вокруг всех ваших функций.