Der Zeitgeber

Ein Zeitgeber besteht aus einem Startpunkt und einem Zeittakt. C++ hat mit std::chrono::system_clock, std::chrono::steady_clock und std::chrono::high_resolution_clock drei Zeitgeber im Angebot.

 

Der Zeitgeber

Bei drei verschiedenen Zeitgebern stellt sich natürlich die Frage. Worin unterscheiden sich die diese?

  • std::chrono::sytem_clock: Stellt die natürliche Zeit (wall-clock) dar. Er bietet die Hilfsfunktionen to_time_t und from_time_t an, um Zeitpunkte in Datumsausgaben zu konvertieren.
  • std::chrono::steady_clock: Gibt als einziger Zeitgeber die Garantie, dass er nicht neu gestellt werden kann. Damit ist std::chrono::steady_clock die ideale Uhr, um bis zu einem Zeitpunkt oder für eine Zeitdauer zu warten.
  • std::chrono::high_resolution_clock: Ist der Zeitgeber mit der höchsten Auflösung. Er kann ein Synonym für den Zeitgeber std::chrono::system_clock oder auch std::chrono::steady_clock sein.

Der C++-Standard gibt keine Garantie für die Genauigkeit, den Startzeitpunkt oder den gültigen Zeitbereich, den einen Zeitgeber darstellen kann. Der Startzeitpunkt von std::chrono:system_clock ist typischerweise der 1.1.1970, die sogenannte UNIX-Epoche. Für std::chrono::steady_clock ist es gerne der Bootzeitpunkt.

Genauigkeit und Stetigkeit

Natürlich ist es interessant, ob der Zeitgeber stetig ist und welche Genauigkeit er besitzt. Dies lässt sich direkt von den drei Zeitgebern abfragen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// clockProperties.cpp

#include <chrono>
#include <iomanip>
#include <iostream>

template <typename T>
void printRatio(){ 
    std::cout << "  precision: " << T::num << "/" << T::den << " second " << std::endl;
    typedef typename std::ratio_multiply<T,std::kilo>::type MillSec;
    typedef typename std::ratio_multiply<T,std::mega>::type MicroSec;
    std::cout << std::fixed;
    std::cout << "             " << static_cast<double>(MillSec::num)/MillSec::den << " milliseconds " << std::endl;
    std::cout << "             " << static_cast<double>(MicroSec::num)/MicroSec::den << " microseconds " << std::endl;
}

int main(){
    
    std::cout << std::boolalpha << std::endl;
    
    std::cout << "std::chrono::system_clock: " << std::endl;
    std::cout << "  is steady: " << std::chrono::system_clock::is_steady << std::endl;
    printRatio<std::chrono::system_clock::period>();
    
    std::cout << std::endl;
    
    std::cout << "std::chrono::steady_clock: " << std::endl;
    std::cout << "  is steady: " << std::chrono::steady_clock::is_steady << std::endl;
    printRatio<std::chrono::steady_clock::period>();
    
    std::cout << std::endl;
    
    std::cout << "std::chrono::high_resolution_clock: " << std::endl;
    std::cout << "  is steady: " << std::chrono::high_resolution_clock::is_steady << std::endl;
    printRatio<std::chrono::high_resolution_clock::period>();
    
    
    std::cout << std::endl;
    
}

 

In den Zeilen 22, 28 und 34 gebe ich für jeden Zeitgeber aus, ob dieser stetig ist. Deutlich anspruchsvoller wird meine Ausgabe in der Funktion printRatio (Zeile 7 - 15). Zuerst gebe ich die Genauigkeit des Zeitgebers in einer Bruchzahl aus, anschließend in einer Fließkommazahl. Dabei mache ich Gebrauch von dem Funktions-Template std::ratio_mulitply und den Konstanten std::kilo und std::mega um die Einheit der Fließkommazahl auf Millisekunden und Mikrosekunden anzupassen. Einen schönen Überblick zu Berechnungen zur Compilezeit mit std::ratio gibt in bewährter Manier cppreference.com.

Die Ausgabe unter Linux und Windows unterscheiden sich. So ist die std::chrono::system_clock unter Linux deutlich genauer, so ist std::chrono::high_resolution_clock unter Windows stetig.

 clockPropertiesclockPropertiesWin

Auch wenn der C++-Standard nicht definiert, welcher Zeitpunkt der Startpunkt der drei Zeitgeber ist, so lässt sich dieser rechnerisch bestimmen.

Epoche 

Mit Hilfe der Methode time_since_epoch gibt jeder Zeitpunkt zurück, wie viele Ticks seit dem Startpunkt (Epoche) seines Zeitgebers vergangen sind. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// now.cpp

#include <chrono>
#include <iomanip>
#include <iostream>

template <typename T>
void durationSinceEpoch(T dur){
    std::cout << "     Counts since epoch:  " << dur.count() << std::endl;
    typedef std::chrono::duration<double, std::ratio<60>> MyMinuteTick;
    MyMinuteTick myMinute(dur);
    std::cout << std::fixed;
    std::cout << "     Minutes since epoch: "<< myMinute.count() << std::endl;
    typedef std::chrono::duration<double, std::ratio<60*60*24*365>> MyYearTick;
    MyYearTick myYear(dur);
    std::cout << "     Years since epoch:   " << myYear.count() << std::endl;
}
    
int main(){
    
    std::cout << std::endl;
    
    std::chrono::system_clock::time_point timeNowSysClock = std::chrono::system_clock::now(); 
    std::chrono::system_clock::duration timeDurSysClock= timeNowSysClock.time_since_epoch();
    std::cout << "std::chrono::system_clock: " << std::endl;
    durationSinceEpoch(timeDurSysClock);
    
    std::cout << std::endl;
     
    auto timeNowStClock = std::chrono::steady_clock::now(); 
    auto timeDurStClock= timeNowStClock.time_since_epoch();
    std::cout << "std::chrono::steady_clock: " << std::endl;
    durationSinceEpoch(timeDurStClock);
    
    std::cout << std::endl;
    
    auto timeNowHiRes = std::chrono::high_resolution_clock::now(); 
    auto timeDurHiResClock= timeNowHiRes.time_since_epoch();
    std::cout << "std::chrono::high_resolution_clock: " << std::endl;
    durationSinceEpoch(timeDurHiResClock);
    
    std::cout << std::endl;

}

 

Die Variablen timeDurSysClock (Zeile 24), timeNowStClock (Zeile 31) und timeNowHiResClock (Zeile 38) halte für jeden Zeitgeber die Zeitdauer seit seinem Startpunkt. Verwende ich keine automatische Typableitung mit auto, so sind die Typen für den Zeitpunkt und die Zeitdauer in den Zeilen 23 und 24 sehr umständlich zu schreiben. Die Darstellung der Zeitdauer in verschiedenen Auflösungen findet in dem Funktions-Template durationSinceEpoch (Zeile 7 - 17) statt. Zuerst gebe ich die Anzahl der Ticks (Zeile 9), dann die Minuten (Zeile 13) und zum Schluss die Jahre (Zeile 16) seit der Epoche des spezifischen Zeitgebers aus. Der Einfachheit halber besteht für mich jedes Jahr aus genau 365 Tagen.

Unter Linux und Windows unterscheiden sich die Ausgaben des Programms.

nownowWin

Um die Zahlen richtig zu interpretieren, muss ich natürlich anmerken, dass ich meinen Linux Rechner vor ca. fünf Stunden (305 Minuten) angeschaltet habe. Im Gegensatz dazu ist mein Windows Rechner schon gut sechs Stunden (391 Minuten) am Laufen.

Unter Linux besitzen sowohl std::chrono::system_clock als auch std::chrono::high_resolution_clock die UNIX-Epoche als Startzeitpunkt. Hingegen ist der Bootzeitpunt der Startzeitpunkt von std::chrono::steady_clock. Unter Windows ist für den Zeitgeber std::chrono::high_resolution_clock im Gegensatz zu Linux der Bootzeitpunkt der Startpunkt. 

Wie geht's weiter?

Hier hört die Geschichte der neuen Zeitbibliothek insbesondere für die Komponenten Zeitpunkt und die Zeitdauer noch nicht auf. Erlauben die Komponenten es doch, einen Thread bis zu einem Zeitpunkt oder für eine Zeitdauer schlafen zu legen. Die Details folgen im nächsten Artikel.

 

 

 

 

 

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Hole dir dein E-Book. Unterstütze meinen Blog.

 

Tags: time

Mentoring

Stay Informed about my Mentoring

 

Rezensionen

Tutorial

Besucher

Heute 436

Gestern 1608

Woche 5925

Monat 24712

Insgesamt 3877426

Aktuell sind 36 Gäste und keine Mitglieder online

Kubik-Rubik Joomla! Extensions

Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode

Neuste Kommentare