Plattformübergreifender hochauflösender C ++ - Timer


73

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 Millisekundengenauigkeit). Dies wird verwendet, um einfach den Zeitablauf zu verfolgen und keine Art von ereignisgesteuertem Design zu implementieren. Was ist das beste Werkzeug, um dies zu erreichen?


3
Sei genauer. Planen Sie einen Funktionsaufruf oder möchten Sie nach einer bestimmten Zeit ein Signal empfangen? Dies sind beide "einfache" Timer-Anwendungen, die jedoch sehr unterschiedlich implementiert sind. Beachten Sie, dass die Verwendung von "einfach" in Anführungszeichen: Das Timing in Allzweckcomputern niemals "einfach" ist.
jmucchiello

Antworten:


41

Für C ++ 03 :

Boost.Timer funktioniert möglicherweise, hängt jedoch von der C-Funktion ab clockund hat möglicherweise nicht genügend Auflösung für Sie.

Boost.Date_Time enthält eine ptimeKlasse , die zuvor für Stack Overflow empfohlen wurde. Lesen Sie die Dokumentation zu microsec_clock::local_timeund microsec_clock::universal_time, beachten Sie jedoch die Einschränkung, 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 für betriebssystemspezifische APIs. Die Leistungsbibliothek verfügt über mehrere Klassen, die genau das tun, was Sie benötigen. (Um es plattformübergreifend zu machen, wählen Sie eine solche Klasse aus performance_counter, die sowohl im winstlals auch im unixstlNamespace vorhanden ist, und verwenden Sie dann den Namespace, der Ihrer Plattform entspricht.)

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

In der std::chronoBibliothek ist diese Funktionalität integriert. Weitere Informationen finden Sie in dieser Antwort von @HowardHinnant.


6
Da dies eine berühmte Frage / Antwort ist, könnte ein Update großartig sein. Insbesondere könnte dies auf standardmäßige und tragbare Weise unter Verwendung moderner C ++ - Funktionen wie <chrono>und <thread>? Wenn möglich, wie?
Manu343726

148

Aktualisierte Antwort auf 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 E / A - Probleme mit diesen neuen Typen zu erleichtern und ist frei verfügbar hier .

Es gibt auch eine Implementierung von <chrono>Boost (möglicherweise immer noch an der Spitze des Trunks, nicht sicher, ob es veröffentlicht wurde).

Aktualisieren

Dies ist eine Reaktion auf Bens Kommentar unten, dass nachfolgende Aufrufe std::chrono::high_resolution_clockin VS11 mehrere Millisekunden dauern sollen. Unten finden Sie eine <chrono>kompatible Problemumgehung. Es funktioniert jedoch nur auf Intel-Hardware. Sie müssen in die Inline-Assembly eintauchen (die Syntax variiert je nach Compiler) und die Taktrate der Maschine fest mit der Uhr verbinden:

#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. Wenn Sie jedoch mit einer hochauflösenden Uhr auf Ihrer eigenen Intel-Hardware experimentieren möchten, wird sie nicht feiner. Seien Sie gewarnt, die heutigen Taktraten können sich dynamisch ändern (sie sind nicht wirklich eine Konstante für die Kompilierungszeit). Und mit einer Multiprozessor-Maschine können Sie sogar Zeitstempel von verschiedenen Prozessoren erhalten. Trotzdem funktionieren Experimente mit meiner Hardware ziemlich gut. Wenn Sie mit der Millisekundenauflösung nicht weiterkommen, kann dies eine Problemumgehung sein.

Diese Uhr hat eine Dauer in Bezug auf die Taktrate Ihrer CPU (wie Sie es gemeldet haben). Dh für mich tickt diese Uhr einmal alle 1 / 2.800.000.000 Sekunden. 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 schneidet Bruchteile eines CPU-Zyklus ab, um die Nanosekunde zu bilden. Andere Rundungsmodi sind möglich, aber das ist ein anderes Thema.

Für mich ergibt dies eine Dauer von nur 18 Takt-Ticks, die auf 6 Nanosekunden verkürzt wird.

Ich habe der obigen Uhr eine "invariante Überprüfung" hinzugefügt, von der die wichtigste die Überprüfung ist, ob die clock::periodfür die Maschine korrekt ist. Auch dies ist kein portabler Code, aber wenn Sie diese Uhr verwenden, haben Sie sich bereits dazu verpflichtet. Die get_clock_speed()hier gezeigte private Funktion erhält unter OS X die maximale CPU-Frequenz, und diese sollte dieselbe Zahl sein wie der konstante Nenner von clock::period.

Wenn Sie dies hinzufügen, sparen Sie ein wenig Zeit beim Debuggen, wenn Sie diesen Code auf Ihren neuen Computer portieren und vergessen, den Code clock::periodauf die Geschwindigkeit Ihres neuen Computers zu aktualisieren . Die gesamte Überprüfung erfolgt entweder zur Kompilierungszeit oder zum Start des Programms. Es hat also keinen Einfluss auf die Leistung von clock::now().


2
In Visual Studio 11 high_resolution_clockbeträgt das kürzeste Intervall ungleich Null leider mehrere Millisekunden.
Petter

5
Das hat für mich ein paar Sekunden gedauert ... Millionen von Nanosekunden auf einer Plattform, auf der die Taktrate einen Bruchteil einer Nanosekunde beträgt. Beeindruckend!!! Ich hatte gehofft, Plattformen zu sehen, auf denen Bruchteile einer Nanosekunde messbar sind. Ich fand meine Ergebnisse von einigen zehn Nanosekunden nicht so beeindruckend.
Howard Hinnant

1
Ist jemandem eine Möglichkeit bekannt, die CPU-Frequenz in der Kompilierungszeit zu ermitteln? Auch ... kann die CPU-Frequenz heutzutage nicht in der Laufzeit variieren, mit Turbomodi und so weiter? Vielleicht macht das diesen Ansatz ungültig? Ich brauche allerdings einen anständigen Timer in VS11, ugh.
David

3
@ 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. Solch eine enge Schleife, zumindest für meine Plattform, erhöht normalerweise die CPU-Frequenz auf ihr Maximum, und dieses Maximum ist typischerweise eine Kompilierungszeitkonstante (Ablesen der CPU-Spezifikation). Für diese Art von Benchmarking kann dies eine gültige Technik sein. Aber offensichtlich ist dies nicht etwas für den allgemeinen Gebrauch. Ich würde den Versand nicht empfehlen. Nur etwas zu Untersuchungszwecken.
Howard Hinnant

7
Ich erhalte 600-1200 Nanosekunden unter Windows mit VS2017, und es scheint, als würde der Hochleistungstimer verwendet. Es scheint also, dass dieses Problem mit einer Auflösung von 1 ms kein Problem mehr ist.
Programmdude

6

Die STLSoft-Bibliotheken von Matthew Wilson bieten verschiedene Timertypen mit kongruenten Schnittstellen, sodass Sie Plug-and-Play- fähig sind . Zu den Angeboten gehören Timer, die kostengünstig, aber niedrig aufgelöst sind, und Timer, die hochauflösend, aber kostenintensiv sind. Es gibt auch solche zum Messen der Vorfadenzeiten und zum Messen der Prozesszeiten sowie alle, die die verstrichenen Zeiten messen.

Es gibt einen ausführlichen Artikel in Dr. Dobbs von vor einigen Jahren, der jedoch nur die Windows- Artikel behandelt, die im WinSTL-Teilprojekt definiert sind. STLSoft bietet auch UNIX-Timer im UNIXSTL-Unterprojekt, und Sie können den "PlatformSTL" 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


Link ist übrigens tot.
Tek

5

Die StlSoft Open Source-Bibliothek bietet sowohl auf Windows- als auch auf Linux-Plattformen einen recht guten Timer . Wenn Sie möchten, dass es selbst implementiert wird, schauen Sie sich einfach die Quellen an.



4

Ich habe gesehen, dass dies einige Male als Closed-Source-Inhouse-Lösungen implementiert wurde. Alle haben auf #ifdefLösungen für native Windows-Hi-Res-Timer einerseits und Linux-Kernel-Timer struct timeval(siehe man timeradd) andererseits zurückgegriffen.

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


Ich habe mich für diesen Weg entschieden. Ihr Link war tot, also habe ich einen kommentiert, der noch funktioniert: songho.ca/misc/timer/timer.html
Patrick

Ahh, nichts wie ein Kommentar zu einer acht Jahre alten Frage :) Ich hatte in der Zwischenzeit viel Glück mit der CCTZ- Bibliothek von Google, die auf einigen neueren C ++ 11-Redewendungen aufbaut.
Dirk Eddelbuettel

4

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


3

SDL2 verfügt über einen hervorragenden plattformübergreifenden hochauflösenden Timer. Wenn Sie jedoch Sub-Millisekunden - Genauigkeit benötigen, schrieb ich eine sehr kleine Cross-Plattform - Timer Bibliothek hier . Es ist sowohl mit C ++ 03 als auch mit C ++ 11 / höheren Versionen von C ++ kompatibel.





0

Wenn Sie das Qt-Framework im Projekt verwenden, ist die beste Lösung wahrscheinlich die Verwendung von QElapsedTimer.


0

Spät zur Party hier, aber ich arbeite in einer alten Codebasis, die noch nicht auf C ++ 11 aktualisiert werden kann. Niemand in unserem Team ist sehr erfahren in C ++, daher erweist sich das Hinzufügen einer Bibliothek wie STL als schwierig (zusätzlich zu den potenziellen Bedenken, die andere in Bezug auf Bereitstellungsprobleme geäußert haben). Ich brauchte wirklich einen extrem einfachen plattformübergreifenden Timer, der ohne die Standard-Systembibliotheken von Bare-Bones auskommen kann. Folgendes habe ich gefunden:

http://www.songho.ca/misc/timer/timer.html

Reposting der gesamten Quelle hier, damit sie nicht verloren geht, wenn die Site jemals stirbt:

    //////////////////////////////////////////////////////////////////////////////
// Timer.cpp
// =========
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn (song.ahn@gmail.com) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#include "Timer.h"
#include <stdlib.h>

///////////////////////////////////////////////////////////////////////////////
// constructor
///////////////////////////////////////////////////////////////////////////////
Timer::Timer()
{
#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceFrequency(&frequency);
    startCount.QuadPart = 0;
    endCount.QuadPart = 0;
#else
    startCount.tv_sec = startCount.tv_usec = 0;
    endCount.tv_sec = endCount.tv_usec = 0;
#endif

    stopped = 0;
    startTimeInMicroSec = 0;
    endTimeInMicroSec = 0;
}



///////////////////////////////////////////////////////////////////////////////
// distructor
///////////////////////////////////////////////////////////////////////////////
Timer::~Timer()
{
}



///////////////////////////////////////////////////////////////////////////////
// start timer.
// startCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::start()
{
    stopped = 0; // reset stop flag
#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&startCount);
#else
    gettimeofday(&startCount, NULL);
#endif
}



///////////////////////////////////////////////////////////////////////////////
// stop the timer.
// endCount will be set at this point.
///////////////////////////////////////////////////////////////////////////////
void Timer::stop()
{
    stopped = 1; // set timer stopped flag

#if defined(WIN32) || defined(_WIN32)
    QueryPerformanceCounter(&endCount);
#else
    gettimeofday(&endCount, NULL);
#endif
}



///////////////////////////////////////////////////////////////////////////////
// compute elapsed time in micro-second resolution.
// other getElapsedTime will call this first, then convert to correspond resolution.
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMicroSec()
{
#if defined(WIN32) || defined(_WIN32)
    if(!stopped)
        QueryPerformanceCounter(&endCount);

    startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart);
    endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart);
#else
    if(!stopped)
        gettimeofday(&endCount, NULL);

    startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec;
    endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec;
#endif

    return endTimeInMicroSec - startTimeInMicroSec;
}



///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInMilliSec()
{
    return this->getElapsedTimeInMicroSec() * 0.001;
}



///////////////////////////////////////////////////////////////////////////////
// divide elapsedTimeInMicroSec by 1000000
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTimeInSec()
{
    return this->getElapsedTimeInMicroSec() * 0.000001;
}



///////////////////////////////////////////////////////////////////////////////
// same as getElapsedTimeInSec()
///////////////////////////////////////////////////////////////////////////////
double Timer::getElapsedTime()
{
    return this->getElapsedTimeInSec();
}

und die Header-Datei:

//////////////////////////////////////////////////////////////////////////////
// Timer.h
// =======
// High Resolution Timer.
// This timer is able to measure the elapsed time with 1 micro-second accuracy
// in both Windows, Linux and Unix system 
//
//  AUTHOR: Song Ho Ahn (song.ahn@gmail.com) - http://www.songho.ca/misc/timer/timer.html
// CREATED: 2003-01-13
// UPDATED: 2017-03-30
//
// Copyright (c) 2003 Song Ho Ahn
//////////////////////////////////////////////////////////////////////////////

#ifndef TIMER_H_DEF
#define TIMER_H_DEF

#if defined(WIN32) || defined(_WIN32)   // Windows system specific
#include <windows.h>
#else          // Unix based system specific
#include <sys/time.h>
#endif


class Timer
{
public:
    Timer();                                    // default constructor
    ~Timer();                                   // default destructor

    void   start();                             // start timer
    void   stop();                              // stop the timer
    double getElapsedTime();                    // get elapsed time in second
    double getElapsedTimeInSec();               // get elapsed time in second (same as getElapsedTime)
    double getElapsedTimeInMilliSec();          // get elapsed time in milli-second
    double getElapsedTimeInMicroSec();          // get elapsed time in micro-second


protected:


private:
    double startTimeInMicroSec;                 // starting time in micro-second
    double endTimeInMicroSec;                   // ending time in micro-second
    int    stopped;                             // stop flag 
#if defined(WIN32) || defined(_WIN32)
    LARGE_INTEGER frequency;                    // ticks per second
    LARGE_INTEGER startCount;                   //
    LARGE_INTEGER endCount;                     //
#else
    timeval startCount;                         //
    timeval endCount;                           //
#endif
};

#endif // TIMER_H_DEF

0

Ich fand das vielversprechend und äußerst unkompliziert, nicht sicher, ob es irgendwelche Nachteile gibt:

https://gist.github.com/ForeverZer0/0a4f80fc02b96e19380ebb7a3debbee5

/* ----------------------------------------------------------------------- */
/*
Easy embeddable cross-platform high resolution timer function. For each 
platform we select the high resolution timer. You can call the 'ns()' 
function in your file after embedding this. 
*/
#include <stdint.h>
#if defined(__linux)
#  define HAVE_POSIX_TIMER
#  include <time.h>
#  ifdef CLOCK_MONOTONIC
#     define CLOCKID CLOCK_MONOTONIC
#  else
#     define CLOCKID CLOCK_REALTIME
#  endif
#elif defined(__APPLE__)
#  define HAVE_MACH_TIMER
#  include <mach/mach_time.h>
#elif defined(_WIN32)
#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>
#endif
static uint64_t ns() {
static uint64_t is_init = 0;
#if defined(__APPLE__)
    static mach_timebase_info_data_t info;
    if (0 == is_init) {
        mach_timebase_info(&info);
        is_init = 1;
    }
    uint64_t now;
    now = mach_absolute_time();
    now *= info.numer;
    now /= info.denom;
    return now;
#elif defined(__linux)
    static struct timespec linux_rate;
    if (0 == is_init) {
        clock_getres(CLOCKID, &linux_rate);
        is_init = 1;
    }
    uint64_t now;
    struct timespec spec;
    clock_gettime(CLOCKID, &spec);
    now = spec.tv_sec * 1.0e9 + spec.tv_nsec;
    return now;
#elif defined(_WIN32)
    static LARGE_INTEGER win_frequency;
    if (0 == is_init) {
        QueryPerformanceFrequency(&win_frequency);
        is_init = 1;
    }
    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);
    return (uint64_t) ((1e9 * now.QuadPart)  / win_frequency.QuadPart);
#endif
}
/* ----------------------------------------------------------------------- */-------------------------------- */
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.