Різниця між std :: system_clock і std :: steady_clock?


97

У чому різниця між std::system_clockі std::steady_clock? (Чудовим є приклад, який ілюструє різні результати / поведінку).

Якщо моєю метою є точно виміряти час виконання функцій (наприклад, орієнтир), що було б найкращим вибором між std::system_clock, std::steady_clockі std::high_resolution_clock?


9
Почнемо з того, що системний_ годинник може бути нестабільним.
James McNellis

12
@CharlesSalvia Я не можу говорити про інші платформи, але system_clockце не стабільно в Windows. У Windows будь-який достатньо привілейований користувач може змінити системний час на будь-яке довільне значення. Крім того, служба синхронізації часу може за потреби відрегулювати час системи назад. Я сподіваюся, що більшість інших платформ мають подібні функції, які дозволяють регулювати системний час.
James McNellis

3
@Charles: Більшість ящиків POSIX, про які я знаю, зазнають подібного впливу, і їхній час буде змінюватися, якщо користувач змінить час.
Billy ONeal


1
@CharlesSalvia. Згідно з моїм власним досвідом аналізу часу виведення з десятків систем збору даних ПК, час від комп'ютера не є стабільним. Linux, Windows та конкретні використовувані системні дзвінки невідомі, але спільність - це часті негативні різниці в часі між наступними значеннями часу. Прямий час - це не норма.
Тайсон Хілмер,

Відповіді:


71

З 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
Операційна семантика: trueякщо t1 <= t2це завжди істина, а час між годинниковими тиками постійний, інакше false.

Це все, що має стандарт щодо їх відмінностей.

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


1
@Charles: Хочете вказати в стандарті, де це так? Здається, це чітко вказує на протилежне.
Billy ONeal

9
@Charles: Крім того, час POSIX не є "стабільним" - якщо користувач змінить установку часу на своєму комп'ютері, час POSIX зміниться. Якщо ви готуєте яйце, і вам потрібен таймер, який триває 4 хвилини, тоді він повинен тривати 4 хв, навіть якщо поточний час змінено. Якщо у вас встановлений таймер для зустрічі 5-го о 3-ї, то вам точно потрібен цей таймер, щоб змінити місцевий час. Звідси різниця між steady_clockі system_clockтут.
Billy ONeal

1
@ 5gon: Ніщо не вимагає system_clockUTC.
Billy ONeal

1
@CharlesSalvia Зверніть також увагу, що оскільки час POSIX прив’язаний до UTC, а UTC має високосні секунди (див. En.wikipedia.org/wiki/Unix_time#Leap_seconds ). Це означає, що навіть якщо час на машині ніколи не регулюється, час C / POSIX може бути немонотонним.
Michael Schlottke-Lakemper

3
ОНОВЛЕННЯ (Visual Studio 2015) Реалізація steady_clock змінилася [.....] steady_clock тепер базується на QueryPerformanceCounter (), а high_resolution_clock тепер є typedef для steady_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


1
Boost використовує QueryPerformanceCounter, тому використання boost :: chrono - хороший спосіб вирішити цю помилку до випуску Visual Studio 14
Мохамед Ель-Накіб,

А ось такий POSIX називає ті вперед , на GCC 5.3.0: stackoverflow.com/a/36700301/895245
Чіро Сантіллі郝海东冠状病六四事件法轮功

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як правило, час завантаження вашого ПК, і це найбільш підходить для вимірювання інтервалів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.