C++ Cross-Plattform High-Resolution Timer

Lesezeit: 8 Minuten

C Cross Plattform High Resolution Timer
Amischer Programmierer

Ich möchte einen einfachen Timer-Mechanismus in C++ implementieren. Der Code sollte unter Windows und Linux funktionieren. Die Auflösung sollte so genau wie möglich sein (mindestens Millisekunden-Genauigkeit). Dies wird verwendet, um einfach den Zeitverlauf zu verfolgen, nicht um irgendeine Art von ereignisgesteuertem Design zu implementieren. Was ist das beste Werkzeug, um dies zu erreichen?

  • Sei genauer. Planen Sie einen Funktionsaufruf oder möchten Sie nach einer bestimmten Zeit eine Art Signal empfangen? Das sind beides “einfache” Timer-Anwendungen, aber sie sind sehr unterschiedlich implementiert. Beachten Sie die Verwendung von “einfach” in Anführungszeichen: Das Timing in Allzweckcomputern ist niemals “einfach”.

    – Jmucchiello

    28. September 2009 um 15:31 Uhr

  • C-Version stackoverflow.com/questions/361363/…

    – Ciro Santilli Путлер Капут 六四事

    18. März 2016 um 22:06 Uhr

C Cross Plattform High Resolution Timer
Howard Hinnant

Aktualisierte Antwort für eine alte Frage:

In C ++ 11 können Sie portabel zum Timer mit der höchsten Auflösung gelangen mit:

#include <iostream>
#include <chrono>
#include "chrono_io"

int main()
{
    typedef std::chrono::high_resolution_clock Clock;
    auto t1 = Clock::now();
    auto t2 = Clock::now();
    std::cout << t2-t1 << '\n';
}

Beispielausgabe:

74 nanoseconds

“chrono_io” ist eine Erweiterung zur Erleichterung von E/A-Problemen mit diesen neuen Typen und ist frei verfügbar Hier.

Es gibt auch eine Implementierung von <chrono> verfügbar in Boost (möglicherweise noch auf der Spitze des Kofferraums, nicht sicher, ob es veröffentlicht wurde).

Aktualisieren

Dies ist eine Antwort auf Bens Kommentar unten, der spätere Anrufe an std::chrono::high_resolution_clock dauern mehrere Millisekunden in VS11. Unten ist ein <chrono>-kompatible Problemumgehung. Es funktioniert jedoch nur auf Intel-Hardware, Sie müssen in die Inline-Assemblierung eintauchen (die Syntax dafür variiert je nach Compiler) und Sie müssen die Taktrate der Maschine fest in die Uhr einbinden:

#include <chrono>

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2800000000>          period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }

private:

    static
    unsigned
    get_clock_speed()
    {
        int mib[] = {CTL_HW, HW_CPU_FREQ};
        const std::size_t namelen = sizeof(mib)/sizeof(mib[0]);
        unsigned freq;
        size_t freq_len = sizeof(freq);
        if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0)
            return 0;
        return freq;
    }

    static
    bool
    check_invariants()
    {
        static_assert(1 == period::num, "period must be 1/freq");
        assert(get_clock_speed() == period::den);
        static_assert(std::is_same<rep, duration::rep>::value,
                      "rep and duration::rep must be the same type");
        static_assert(std::is_same<period, duration::period>::value,
                      "period and duration::period must be the same type");
        static_assert(std::is_same<duration, time_point::duration>::value,
                      "duration and time_point::duration must be the same type");
        return true;
    }

    static const bool invariants;
};

const bool clock::invariants = clock::check_invariants();

Es ist also nicht tragbar. Aber wenn Sie mit einer hochauflösenden Uhr auf Ihrer eigenen Intel-Hardware experimentieren möchten, wird es nicht feiner. Seien Sie jedoch vorgewarnt, die heutigen Taktraten können sich dynamisch ändern (sie sind nicht wirklich eine Kompilierzeitkonstante). Und mit einer Multiprozessormaschine können Sie sogar Zeitstempel von verschiedenen Prozessoren erhalten. Aber trotzdem funktionieren Experimente mit meiner Hardware recht gut. Wenn Sie mit der Millisekundenauflösung nicht weiterkommen, könnte dies eine Problemumgehung sein.

Diese Uhr hat eine Dauer in Bezug auf die Taktrate Ihrer CPU (wie Sie es gemeldet haben). Dh bei mir tickt diese Uhr alle 1/2.800.000.000 Sekunde einmal. Wenn Sie möchten, können Sie dies (zum Beispiel) in Nanosekunden umwandeln mit:

using std::chrono::nanoseconds;
using std::chrono::duration_cast;
auto t0 = clock::now();
auto t1 = clock::now();
nanoseconds ns = duration_cast<nanoseconds>(t1-t0);

Die Umwandlung kürzt Bruchteile eines CPU-Zyklus, um die Nanosekunde zu bilden. Andere Rundungsmodi sind möglich, aber das ist ein anderes Thema.

Für mich wird dies eine Dauer von nur 18 Takten zurückgeben, was auf 6 Nanosekunden verkürzt wird.

Ich habe der obigen Uhr einige “invariante Überprüfungen” hinzugefügt, von denen die wichtigste die Überprüfung der ist clock::period ist richtig für die Maschine. Auch dies ist kein portabler Code, aber wenn Sie diese Uhr verwenden, haben Sie sich bereits dazu verpflichtet. Das private get_clock_speed() Die hier gezeigte Funktion erhält die maximale CPU-Frequenz unter OS X, und das sollte die gleiche Zahl sein wie der konstante Nenner von clock::period.

Wenn Sie dies hinzufügen, sparen Sie ein wenig Debugging-Zeit, wenn Sie diesen Code auf Ihren neuen Computer portieren und vergessen, die zu aktualisieren clock::period an die Geschwindigkeit Ihrer neuen Maschine. Die gesamte Überprüfung erfolgt entweder zur Kompilierungszeit oder zur Programmstartzeit. Es hat also keinen Einfluss auf die Leistung von clock::now() im mindesten.

  • In Visual Studio 11 ist das kürzeste Nicht-Null-Intervall für high_resolution_clock beträgt leider mehrere Millisekunden.

    – Peter

    19. Januar 2012 um 22:50 Uhr

  • Es dauerte ein paar Sekunden, bis mir das einfiel … Millionen von Nanosekunden auf einer Plattform, auf der die Taktrate nur den Bruchteil einer Nanosekunde beträgt. Beeindruckend!!! Ich hatte gehofft, Plattformen zu sehen, auf denen Bruchteile einer Nanosekunde messbar wären. Ich fand meine Ergebnisse von mehreren zehn Nanosekunden nicht so beeindruckend.

    – Howard Hinnant

    20. Januar 2012 um 3:57 Uhr

  • Kennt jemand eine Möglichkeit, die CPU-Frequenz in der Kompilierzeit zu erhalten? Außerdem … kann die CPU-Frequenz heutzutage nicht in der Laufzeit variieren, mit Turbo-Modi und so weiter? Vielleicht macht das diesen Ansatz als praktikabel ungültig? Ich brauche aber einen anständigen Timer in VS11, ugh.

    – David

    18. Februar 2013 um 0:01 Uhr

  • @Dave: Ja, die CPU-Frequenz kann dynamisch variieren (ich habe dies in der Antwort angegeben). Meine Experimente, wenn ich dies verwende, sind normalerweise eine enge Schleife um etwas, das ich zu messen versuche. Eine solche enge Schleife, zumindest für meine Plattform, erhöht normalerweise die CPU-Frequenz auf ihr Maximum, und dieses Maximum ist normalerweise eine Kompilierzeitkonstante (aus der CPU-Spezifikation ablesbar). Für diese Art von Benchmarking kann dies also eine gültige Technik sein. Aber offensichtlich ist dies nichts für den allgemeinen Gebrauch. Es ist nichts, was ich Versand empfehlen würde. Nur etwas für Ermittlungszwecke.

    – Howard Hinnant

    18. Februar 2013 um 1:52 Uhr

  • Ich erhalte unter Windows mit VS2017 600-1200 Nanosekunden, und es scheint den Hochleistungs-Timer zu verwenden. Es scheint also, dass dieses Problem der 1-ms-Auflösung kein Problem mehr ist.

    – Programmtyp

    26. März 2017 um 11:47 Uhr

1646953225 232 C Cross Plattform High Resolution Timer
Josh Kelley

Für C++03:

Boost.Timer könnte funktionieren, hängt aber von der C-Funktion ab clock und hat daher möglicherweise keine ausreichend gute Auflösung für Sie.

Boost.Date_Time enthält a ptime Klasse das wurde schon einmal auf Stack Overflow empfohlen. Siehe seine Dokumente auf microsec_clock::local_time und microsec_clock::universal_timebeachten Sie jedoch den Vorbehalt, dass “Win32-Systeme über diese API häufig keine Mikrosekundenauflösung erreichen”.

STLsoft bietet unter anderem dünne plattformübergreifende (Windows und Linux/Unix) C++-Wrapper um betriebssystemspezifische APIs. Es ist Leistungsbibliothek hat mehrere Klassen, die tun würden, was Sie brauchen. (Um es plattformübergreifend zu machen, wählen Sie eine Klasse wie performance_counter das existiert sowohl in der winstl und unixstl Namespaces, und verwenden Sie dann den Namespace, der Ihrer Plattform entspricht.)

Für C++11 und höher:

Die std::chrono Die Bibliothek hat diese Funktionalität integriert. Weitere Informationen finden Sie in dieser Antwort von @HowardHinnant.

  • Da dies eine berühmte Frage/Antwort ist, könnte ein Update großartig sein. Insbesondere könnte dies auf eine standardisierte und portable Weise unter Verwendung moderner C++-Funktionen erreicht werden, wie z <chrono> und <thread>? Wenn möglich, wie?

    – Manu343726

    1. Mai 2014 um 18:59 Uhr


Matthäus Wilson‘S STLSoft-Bibliotheken bieten mehrere Timer-Typen mit kongruenten Schnittstellen für Plug-and-Play. Zu den Angeboten gehören Timer, die kostengünstig, aber mit niedriger Auflösung sind, und solche, die mit hoher Auflösung, aber hohen Kosten verbunden sind. Es gibt auch solche zum Messen von Pre-Thread-Zeiten und zum Messen von Per-Process-Zeiten sowie alle, die verstrichene Zeiten messen.

Es gibt eine erschöpfende Artikel darüber in Dr. Dobb’s von vor einigen Jahren, obwohl es nur die Windows-Dateien abdeckt, die im WinSTL-Unterprojekt definiert sind. STLSoft bietet auch UNIX-Timer im UNIXSTL-Unterprojekt, und Sie können den “PlatformSTL”-Timer verwenden, der den UNIX- oder Windows-Timer enthält, wie in:

#include <platformstl/performance/performance_counter.hpp>
#include <iostream>

int main()
{
    platformstl::performance_counter c;

    c.start();
    for(int i = 0; i < 1000000000; ++i);
    c.stop();

    std::cout << "time (s): " << c.get_seconds() << std::endl;
    std::cout << "time (ms): " << c.get_milliseconds() << std::endl;
    std::cout << "time (us): " << c.get_microseconds() << std::endl;
}

HTH

Die StlSoft Open-Source-Bibliothek bietet eine ziemlich guter Timer sowohl auf Windows- als auch auf Linux-Plattformen. Wenn Sie es selbst implementieren möchten, schauen Sie sich einfach deren Quellen an.

Die ACE-Bibliothek verfügt auch über tragbare hochauflösende Timer.

Doxygen für hochauflösende Timer:
http://www.dre.vanderbilt.edu/Doxygen/5.7.2/html/ace/a00244.html

1646953226 439 C Cross Plattform High Resolution Timer
Dirk Edelbüttel

Ich habe dies einige Male als Closed-Source-Inhouse-Lösungen umgesetzt gesehen …. auf die alle zurückgegriffen haben #ifdef Lösungen rund um native Windows-Hi-Res-Timer einerseits und Linux-Kernel-Timer verwenden struct timeval (sehen man timeradd) andererseits.

Sie können dies abstrahieren, und einige Open-Source-Projekte haben dies getan – das letzte, das ich mir angesehen habe, war das CoinTimer der CoinOR-Klasse aber es gibt sicher noch mehr davon.

1646953227 848 C Cross Plattform High Resolution Timer
Maciek

Ich empfehle die boost::posix_time-Bibliothek dafür sehr. Es unterstützt Timer in verschiedenen Auflösungen bis hinunter zu Mikrosekunden, glaube ich

989220cookie-checkC++ Cross-Plattform High-Resolution Timer

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy