Różnica między std :: system_clock a std :: steady_clock?

97

Jaka jest różnica między std::system_clocki std::steady_clock? (Przykładowy przypadek ilustrujący różne wyniki / zachowania byłby świetny).

Jeśli moim celem jest precyzyjny pomiar czasu wykonywania funkcji (np. Benchmarku), jaki byłby najlepszy wybór pomiędzy std::system_clock, std::steady_clocka std::high_resolution_clock?

Vincent
źródło
9
Po pierwsze, zegar_systemowy może nie być stabilny.
James McNellis
12
@CharlesSalvia Nie mogę mówić w imieniu innych platform, ale system_clockw systemie Windows nie jest stabilny. W systemie Windows czas systemowy może zostać zmieniony na dowolną wartość przez dowolnego wystarczająco uprzywilejowanego użytkownika. Ponadto usługa synchronizacji czasu może w razie potrzeby cofnąć czas systemowy. Spodziewam się, że większość innych platform ma podobne funkcje, które umożliwiają dostosowanie czasu systemowego.
James McNellis
3
@Charles: Większość skrzynek POSIX, o których wiem, ma podobny wpływ i ich czas się zmieni, jeśli użytkownik zmieni czas.
Billy ONeal
5
Wideo odpowiedź na to pytanie: youtube.com/watch?v=P32hvk8b13M&t=48m44s
Howard Hinnant,
1
@CharlesSalvia. Z własnego doświadczenia, analizując wyjście taktowania z dziesiątek systemów akwizycji danych PC, czas z komputera nie jest stabilny. Linux, Windows i określone wywołania systemowe są nieznane, ale cechą wspólną są często ujemne różnice czasu między kolejnymi wartościami czasu. Czas w linii prostej nie jest normą.
Tyson Hilmer

Odpowiedzi:

72

Od N3376:

20.11.7.1 [time.clock.system] / 1:

Obiekty klasy system_clockreprezentują czas zegara ściennego z ogólnosystemowego zegara czasu rzeczywistego.

20.11.7.2 [time.clock.steady] / 1:

Obiekty klasy steady_clockreprezentują zegary, dla których wartości time_pointnigdy nie spadają wraz z upływem czasu fizycznego i dla których wartości time_pointpostępu w stałym tempie w stosunku do czasu rzeczywistego. Oznacza to, że zegar nie może być regulowany.

20.11.7.3 [time.clock.hires] / 1:

Obiekty klasy high_resolution_clockreprezentują zegary o najkrótszym takcie. high_resolution_clockmoże być synonimem system_clocklub steady_clock.

Na przykład na zegar systemowy może mieć wpływ coś w rodzaju czasu letniego, w którym to momencie rzeczywisty czas podany w pewnym momencie w przyszłości może w rzeczywistości należeć do przeszłości. (Np. W USA jesienią czas cofa się o godzinę, więc ta sama godzina jest przeżywana „dwa razy”). Jednak steady_clocktakie rzeczy nie mają na to wpływu.

Innym sposobem myślenia o „stabilnym” w tym przypadku są wymagania określone w tabeli 20.11.3 [time.clock.req] / 2:

W tabeli 59 C1i C2oznacz typy zegarów. t1i t2są wartościami zwracanymi przez miejsce, w C1::now()którym powrót wywołania t1ma miejsce przed powrotem wywołania t2i oba te wywołania występują wcześniej C1::time_point::max(). [Uwaga: oznacza to, C1że nie zawijano między t1a t2. —End note]

Wyrażenie: C1::is_steady
Zwraca: const bool
Semantyka operacyjna: truejeśli t1 <= t2jest zawsze prawdziwe, a czas między tyknięciami zegara jest stały, w przeciwnym razie false.

To wszystko, co standard ma na temat ich różnic.

Jeśli chcesz przeprowadzić test porównawczy, prawdopodobnie najlepszym rozwiązaniem będzie std::high_resolution_clock, ponieważ jest prawdopodobne, że Twoja platforma używa zegara o wysokiej rozdzielczości (np. W QueryPerformanceCountersystemie Windows) dla tego zegara. Jeśli jednak przeprowadzasz testy porównawcze, naprawdę powinieneś rozważyć użycie timerów specyficznych dla platformy do swojego testu porównawczego, ponieważ różne platformy radzą sobie z tym inaczej. Na przykład niektóre platformy mogą zapewniać pewne sposoby określania rzeczywistej liczby taktów zegara wymaganych przez program (niezależnie od innych procesów uruchomionych na tym samym procesorze). Jeszcze lepiej, zdobądź prawdziwy profiler i użyj go.

Billy ONeal
źródło
1
@Charles: Chcesz wskazać w standardzie, gdzie tak jest? Wydaje się, że jasno wskazuje na coś przeciwnego.
Billy ONeal
9
@Charles: Poza tym czas POSIX nie jest "stały" - jeśli użytkownik zmieni ustawienie czasu na swoim komputerze, czas POSIX się zmieni. Jeśli gotujesz jajko i potrzebujesz minutnika, który trwa 4 minuty, potrzebujesz go na 4 minuty, nawet jeśli bieżący czas zostanie zmieniony. Jeśli masz ustawiony licznik czasu na spotkanie piątego o godzinie 3, to absolutnie potrzebujesz go do zmiany, jeśli zmieni się czas lokalny. Stąd różnica między steady_clocki system_clocktutaj.
Billy ONeal
1
@ 5gon: Nic nie wymaga system_clockczasu UTC.
Billy ONeal
1
@CharlesSalvia Należy również pamiętać, że ponieważ czas POSIX jest powiązany z UTC, a UTC ma sekundy przestępne (por. En.wikipedia.org/wiki/Unix_time#Leap_seconds ). Oznacza to, że nawet jeśli czas na maszynie nigdy nie jest regulowany, czas C / POSIX może nie być monotoniczny.
Michael Schlottke-Lakemper
3
UPDATE (Visual Studio 2015) Implementacja steady_clock zmieniła się [.....] steady_clock jest teraz oparta na QueryPerformanceCounter (), a high_resolution_clock jest teraz wartością typu dla steady_clock. Cytat z msdn.microsoft.com/en-us/library/hh874757.aspx
felix-b,
47

Billy udzielił świetnej odpowiedzi opartej na standardzie ISO C ++, z którym w pełni się zgadzam. Jest jednak druga strona tej historii - prawdziwe życie. Wygląda na to, że w tej chwili tak naprawdę nie ma różnicy między tymi zegarami w implementacji popularnych kompilatorów:

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;

W przypadku gcc możesz sprawdzić, czy masz do czynienia z zegarem stałym, po prostu sprawdzając is_steadyi odpowiednio się zachowując. Jednak VS2012 wydaje się tutaj trochę oszukiwać :-)

Jeśli potrzebujesz zegara o wysokiej precyzji, polecam na razie napisać własny zegar zgodny z oficjalnym interfejsem zegara C ++ 11 i poczekać, aż implementacje nadrobią zaległości. Będzie to znacznie lepsze podejście niż używanie interfejsu API specyficznego dla systemu operacyjnego bezpośrednio w kodzie. W systemie Windows możesz to zrobić w ten sposób:

// 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()));
  }
};

W przypadku Linuksa jest to jeszcze łatwiejsze. Po prostu przeczytaj stronę podręcznika man clock_gettimei zmodyfikuj powyższy kod.

Mateusz Pusz
źródło
19
Implementacja VC ++ 2012 została uznana za błąd przez opiekuna standardowej biblioteki MS.
ildjarn
5
Dla zainteresowanych link do tego błędu
Ben Voigt,
1
Boost używa QueryPerformanceCounter, więc użycie boost :: chrono jest dobrym obejściem tego błędu do czasu wydania Visual Studio 14
Mohamed El-Nakib
A oto wywołania POSIX do przekazywania w GCC 5.3.0: stackoverflow.com/a/36700301/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功 Kwietnia
19

Wdrożenie GCC 5.3.0

C ++ stdlib znajduje się w źródle GCC:

  • high_resolution_clock jest aliasem dla system_clock
  • system_clock przekierowuje do pierwszej z dostępnych opcji:
    • clock_gettime(CLOCK_REALTIME, ...)
    • gettimeofday
    • time
  • steady_clock przekierowuje do pierwszej z dostępnych opcji:
    • clock_gettime(CLOCK_MONOTONIC, ...)
    • system_clock

Następnie CLOCK_REALTIMEvs CLOCK_MONOTONICjest wyjaśnione na: Różnica między CLOCK_REALTIME i CLOCK_MONOTONIC?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
2

Być może najbardziej znaczącą różnicą jest fakt, że punktem wyjścia std::chrono:system_clockjest 1.1.1970, tzw. Epoka UNIX. Z drugiej strony, std::chrono::steady_clockzwykle dla czasu uruchamiania komputera i najlepiej nadaje się do mierzenia odstępów czasu.

Silviu
źródło