Вплоть до версии C++11 не существовало стандартного способа достаточно точного измерения времени, в течение которого выполняется тот или другой участок кода на C++. Программист был вынужден использовать сторонние библиотеки, такие как Boost или POCO, или использовать непереносимые методы работы со временем, предлагаемые конкретной операционной системой. С появлением C++11 ситуация сильно изменилась в лучшую сторону.

Заголовочный файл <chrono> из стандартной библиотеки предоставляет три варианта таймеров:

  • system_clock — таймер реального времени, используемый операционной системой;
  • high_resolution_clock — таймер с минимально возможным периодом, который только доступен на конкретной ОС;
  • steady_clock — монотонный таймер, который гарантированно не может быть изменен никаким образом.

Для замеров производительности участков кода нам более всего подойдет третий вариант — steady_clock, поскольку этот таймер не может быть изменен системой, что гарантирует, что разница между двумя последовательными моментами времени всегда будет положительной.
В дополнение к этому стандартная библиотека предоставляет набор функций, позволяющих сделать некоторые проверки касательно точности таймеров и действительно ли конкретный таймер является монотонным и неизменяемым. Поскольку реализация конкретного таймера является платформозависимой, перед его использованием никогда не помешает сделать некоторые проверки:

#include <chrono>
#include <iostream>

int main(int argc, char *argv[])
{
  using namespace std;
  using namespace std::chrono;
  cout << "system_clock:" << endl;
  cout << "  numerator: " << system_clock::period::num << endl;
  cout << "  denominator: " << system_clock::period::den << endl;
  cout << "  is_steady: " << boolalpha << system_clock::is_steady << endl;
  cout << endl;
  cout << "high_resolution_clock:" << endl;
  cout << "  numerator: " << high_resolution_clock::period::num << endl;
  cout << "  denominator: " << high_resolution_clock::period::den << endl;
  cout << "  is_steady: " << boolalpha << high_resolution_clock::is_steady << endl;
  cout << endl;
  cout << "steady_clock:" << endl;
  cout << "  numerator: " << steady_clock::period::num << endl;
  cout << "  denominator: " << steady_clock::period::den << endl;
  cout << "  is_steady: " << boolalpha << steady_clock::is_steady << endl;

  return 0;
}

Результат запуска этого кода, скомпилированного gcc-5.4 на Linux:

system_clock:
  numerator: 1
  denominator: 1000000000
  is_steady: false

high_resolution_clock:
  numerator: 1
  denominator: 1000000000
  is_steady: false

steady_clock:
  numerator: 1
  denominator: 1000000000
  is_steady: true

Как видим, на этой платформе все таймеры имеют точность в наносекундах, и steady_clock является монотонным.

То же самое на clang-4.2 и MacOS:

system_clock:
  numerator: 1
  denominator: 1000000
  is_steady: false

high_resolution_clock:
  numerator: 1
  denominator: 1000000000
  is_steady: true

steady_clock:
  numerator: 1
  denominator: 1000000000
  is_steady: true

А здесь ситуация немного другая — только system_clock не будет гарантировать нам правильных измерений, к тому же имеет меньшую точность.

Чтобы произвести нужные нам замеры для определения времени, потраченного на выполнение какого-либо участка кода, можно сделать примерно так, как показано ниже:

#include <chrono>
#include <iostream>

int main(int argc, char *argv[])
{
  using namespace std;
  using namespace std::chrono;

  steady_clock::time_point start = steady_clock::now();

  //Здесь начинается измеряемый участок кода
  for(u_int32_t i=0; i<0xFFFFFFFF; i++)
  {
    u_int32_t j = i;
  }
  //Здесь заканчивается измеряемый участок кода

  steady_clock::time_point end = steady_clock::now();
  steady_clock::duration dur = end - start;

  cout << "timing in milliseconds: " << duration<double, milli>(dur).count() << " ms" << endl;
  cout << "timing in nanoseconds: " << duration<double, nano>(dur).count() << " ns" << endl;

  return 0;
}

Как видно, нужно взять текущее время на начало и конец прохождения тестирукмого участка и найти разницу между ними.  Результат можно представить в единицах, которые наиболее удобны для каждого случая.  Вывод нашей скомпилированной программы в милисекундах и наносекундах:

timing in milliseconds: 0.000255 ms
timing in nanoseconds: 255 ns

В подобных ситуациях, конечно же, всегда хочется сделать как можно более качественный инструмент, чтобы его использование не привносило слишком больших усилий и неудобств. И с таким инструментом в виде специального небольшого класса можно ознакомиться в статье на сайте QuantPro.ru. Насколько удобно им пользоваться, можно увидеть ниже:

#include <chrono>
#include <iostream>

class TimingUtil
{
public:
  TimingUtil(std::string const& message)
    : start_(std::chrono::steady_clock::now()),
      message_(message)
  {
  }
  ~TimingUtil()
  {
    std::chrono::steady_clock::time_point finish = std::chrono::steady_clock::now();
    std::cout << message_ << " took "
              << std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start_).count()
              << " ns." << std::endl;
  }
private:
  std::chrono::steady_clock::time_point start_;
  std::string message_;
};

int main(int argc, char *argv[])
{
  TimingUtil t("My test");

  for(u_int32_t i=0; i<0xFFFFFFFF; i++)
  {
    u_int32_t j = i;
  }

  return 0;
}

В конструкторе класса можно задать имя теста, которое будет выведено на экран вместе с измеренным временем.  Результат нашего теста будет таким:

My test took 151 ns.

Данный класс хорош тем, что использует парадигму RAII, что позволяет измерять время выполнения кода в пределах области видимости, избавляя от необходимости беспокоиться о том, что замеряемый участок кода может вернуть управление раньше или выбросит исключение. Измерение будет произведено в любом случае при уничтожении класса в момент выхода за пределы области видимости.