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

97

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

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

Винсент
источник
9
Начнем с того, что system_clock может быть нестабильным.
Джеймс МакНеллис
12
@CharlesSalvia Я не могу говорить о других платформах, но в system_clockWindows они не работают . В Windows системное время может быть изменено на любое произвольное значение любым достаточно привилегированным пользователем. Кроме того, служба синхронизации времени может при необходимости корректировать системное время назад. Я ожидаю, что большинство других платформ имеют аналогичные функции, позволяющие регулировать системное время.
Джеймс МакНеллис,
3
@Charles: Большинство POSIX-боксов, о которых я знаю, подвержены аналогичному воздействию, и их время изменится, если пользователь изменит время.
Билли Онил,
5
Видеоответ на этот вопрос: youtube.com/watch?v=P32hvk8b13M&t=48m44s
Говард Хиннант
1
@CharlesSalvia. По моему собственному опыту, анализируя временные данные, выводимые десятками систем сбора данных ПК, время на компьютере не стабильно. Linux, Windows и конкретные используемые системные вызовы неизвестны, но общим является частая отрицательная разница во времени между последующими значениями времени. Прямое время - это не норма.
Тайсон Хилмер

Ответы:

72

С 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. - конец примечания]

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

Это все, что в стандарте говорится об их различиях.

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

Билли Онил
источник
1
@Charles: Не забудьте указать в стандарте, где это так? Кажется, ясно указывает на обратное.
Билли Онил,
9
@Charles: Кроме того, время POSIX не является «постоянным» - если пользователь изменяет настройку времени на своем компьютере, время POSIX изменится. Если вы готовите яйцо и вам нужен таймер на 4 минуты, то вам нужно, чтобы он работал на 4 минуты, даже если текущее время изменилось. Если у вас есть таймер, установленный для встречи 5-го в 3, то вам абсолютно необходимо, чтобы этот таймер изменился, если местное время изменится. Отсюда разница между steady_clockи system_clockздесь.
Билли Онил,
1
@ 5gon: Ничто не требует, чтобы это system_clockбыло UTC.
Билли Онил
1
@CharlesSalvia Также обратите внимание, что, поскольку время POSIX привязано к UTC, а UTC имеет дополнительные секунды (см. En.wikipedia.org/wiki/Unix_time#Leap_seconds ). Это означает, что даже если время на машине никогда не регулируется, время C / POSIX может быть немонотонным.
Michael Schlottke-Lakemper
3
ОБНОВЛЕНИЕ (Visual Studio 2015). Реализация stable_clock изменилась [.....] stable_clock теперь основан на QueryPerformanceCounter (), а high_resolution_clock теперь является определением типа для stable_clock. Цитата из msdn.microsoft.com/en-us/library/hh874757.aspx
felix-b
47

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

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, похоже, здесь немного обманывает :-)

Если вам нужны часы с высокой точностью, я рекомендую сейчас написать свои собственные часы, которые соответствуют официальному интерфейсу часов C ++ 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и измените приведенный выше код.

Матеуш Пуш
источник
19
Реализация VC ++ 2012 была признана ошибкой сопровождающим стандартной библиотеки MS.
ildjarn 07
5
Для тех, кто заинтересован, это ссылка на ту ошибку
Бен Войт
1
Boost использует QueryPerformanceCounter, поэтому использование boost :: chrono является хорошим решением этой ошибки до выпуска Visual Studio 14
Мохамед Эль-Накиб,
И вот POSIX-вызовы, которые пересылаются в GCC 5.3.0: stackoverflow.com/a/36700301/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
19

Реализация 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_REALTIMEvs CLOCK_MONOTONIC: Разница между CLOCK_REALTIME и CLOCK_MONOTONIC?

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
2

Пожалуй, самое существенное отличие состоит в том, что отправной точкой std::chrono:system_clockявляется 1.1.1970, так называемая UNIX-эпоха. С другой стороны, std::chrono::steady_clockобычно это время загрузки вашего ПК, и это лучше всего подходит для измерения интервалов.

Сильвиу
источник