Разница между std:: system_clock и std:: stable_clock?

В чем разница между std::system_clock и std::steady_clock? (Примерный пример, иллюстрирующий разные результаты/поведение, будет большим).

Если моя цель - точно измерить время выполнения функций (например, контрольный показатель), какой лучший выбор между std::system_clock, std::steady_clock и std::high_resolution_clock?

Ответы

Ответ 1

От N3376:

20.11.7.1 [time.clock.system]/1:

Объекты класса system_clock представляют собой часы настенных часов из общесистемных часов реального времени.

20.11.7.2 [time.clock.steady]/1:

Объекты класса steady_clock представляют собой часы, для которых значения time_point никогда не уменьшаются по мере продвижения физического времени и для которых значения time_point продвигаются с постоянной скоростью относительно реального времени. То есть, часы могут не регулироваться.

20.11.7.3 [time.clock.hires]/1:

Объекты класса high_resolution_clock представляют часы с самым коротким тиковым периодом. high_resolution_clock может быть синонимом для system_clock или steady_clock.

Например, на системные часы может повлиять что-то вроде летнего времени, и в этот момент фактическое время, указанное в какой-то момент в будущем, может на самом деле быть временем в прошлом. (Например, в США в осеннее время отходит один час, поэтому один и тот же час переживается "дважды"). Однако steady_clock не может быть затронуто такими вещами.

Другой способ мышления об "устойчивом" в этом случае - в требованиях, определенных в таблице 20.11.3 [time.clock.req]/2:

В таблице 59 C1 и C2 обозначены типы часов. t1 и t2 - значения, возвращаемые C1::now() где возврат вызова t1 происходит до вызова t2 и оба этих вызова происходят до C1::time_point::max(). [Примечание: это означает, что C1 не обертывается между t1 и t2. -End note]

Выражение: C1::is_steady
Возврат: const bool
Операционная семантика: true если t1 <= t2 всегда истинно, а время между часами - постоянным, в противном случае - false.

То, что весь стандарт имеет на свои различия.

Если вы хотите провести бенчмаркинг, лучшим вариантом будет, вероятно, std::high_resolution_clock, потому что, вероятно, ваша платформа использует таймер с высоким разрешением (например, QueryPerformanceCounter для Windows) для этих часов. Однако, если вы проводите бенчмаркинг, вам следует подумать о том, чтобы использовать определенные для платформы таймеры для своего теста, потому что разные платформы обрабатывают это по-другому. Например, некоторые платформы могут дать вам некоторые способы определения фактического количества тактов синхронизации, которые требуется программе (независимо от других процессов, работающих на одном процессоре). Еще лучше, возьмитесь за реальный профилировщик и используйте это.

Ответ 2

Билли предоставил отличный ответ на основе стандарта ISO С++, с которым я полностью согласен. Однако есть и другая сторона истории - настоящая жизнь. Похоже, что сейчас практически нет разницы между этими часами в реализации популярных компиляторов:

gcc 4.8:

#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
   ...
#else
  typedef system_clock steady_clock;
#endif
  typedef system_clock high_resolution_clock;

Visual Studio 2012:

class steady_clock : public system_clock
{   // wraps monotonic clock
public:
  static const bool is_monotonic = true;    // retained
  static const bool is_steady = true;
};

typedef system_clock high_resolution_clock;

В случае gcc вы можете проверить, имеете ли вы дело с постоянными часами, просто проверив is_steady и ведите себя соответственно. Однако VS2012, кажется, немного обманывает здесь: -)

Если вам нужны часы с высокой точностью, я рекомендую сейчас писать собственные часы, которые соответствуют официальному интерфейсу часов С++ 11 и ждут, пока реализация не догонит. Это будет гораздо лучший подход, чем использование специфичного для ОС API непосредственно в вашем коде. Для Windows вы можете сделать это следующим образом:

// Self-made Windows QueryPerformanceCounter based C++11 API compatible clock
struct qpc_clock {
  typedef std::chrono::nanoseconds                       duration;      // nanoseconds resolution
  typedef duration::rep                                  rep;
  typedef duration::period                               period;
  typedef std::chrono::time_point<qpc_clock, duration>   time_point;
  static bool is_steady;                                                // = true
  static time_point now()
  {
    if(!is_inited) {
      init();
      is_inited = true;
    }
    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);
    return time_point(duration(static_cast<rep>((double)counter.QuadPart / frequency.QuadPart *
                                                period::den / period::num)));
  }

private:
  static bool is_inited;                                                // = false
  static LARGE_INTEGER frequency;
  static void init()
  {
    if(QueryPerformanceFrequency(&frequency) == 0)
      throw std::logic_error("QueryPerformanceCounter not supported: " + std::to_string(GetLastError()));
  }
};

Для Linux это еще проще. Просто прочитайте страницу руководства clock_gettime и измените код выше.

Ответ 3

Внедрение GCC 5.3.0

C++ stdlib находится внутри источника GCC:

  • high_resolution_clock - это псевдоним для system_clock
  • system_clock к первому из следующего, которое доступно:
    • clock_gettime(CLOCK_REALTIME,...)
    • gettimeofday
    • time
  • steady_clock к первому из следующего, которое доступно:
    • clock_gettime(CLOCK_MONOTONIC,...)
    • system_clock

Затем CLOCK_REALTIME vs CLOCK_MONOTONIC объясняется в: Разница между CLOCK_REALTIME и CLOCK_MONOTONIC?