webentwicklung-frage-antwort-db.com.de

C++ - plattformübergreifender hochauflösender Timer

Ich suche nach einem einfachen Timermechanismus in C++. 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 Zeitverlauf zu verfolgen, und keine ereignisgesteuerten Designs zu implementieren. Was ist das beste Werkzeug, um dies zu erreichen?

68

Für C++ 03:

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

Boost.Date_Time enthält eine ptime-Klasse , die zuvor für Stack Overflow empfohlen wurde. Siehe die Dokumentation zu microsec_clock::local_time und microsec_clock::universal_time. Beachten Sie jedoch die Einschränkung, dass "Win32-Systeme über diese API oft keine Auflösung im Mikrosekundenbereich erzielen."

STLsoft bietet unter anderem schlanke, plattformübergreifende (Windows und Linux/Unix) C++ - Wrapper für betriebssystemspezifische APIs. Seine Performance-Bibliothek hat mehrere Klassen, die das tun, was Sie brauchen. (Um es plattformübergreifend zu machen, wählen Sie eine Klasse wie performance_counter aus, die in den Namespaces winstl und unixstl vorhanden ist, und verwenden Sie dann den für Ihre Plattform passenden Namespace.)

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

Die std::chrono-Bibliothek verfügt über diese Funktion. Siehe diese Antwort von @HowardHinnant für Details.

38
Josh Kelley

Aktualisierte Antwort für eine alte Frage:

In C++ 11 können Sie den Timer mit der höchsten Auflösung portabel erreichen 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 here .

Es gibt auch eine Implementierung von <chrono>, die in boost verfügbar ist (möglicherweise noch auf dem Tip-of-Trunk, nicht sicher, ob sie veröffentlicht wurde).

Update

Dies ist eine Antwort auf Bens Kommentar, dass nachfolgende Aufrufe von std::chrono::high_resolution_clock in VS11 mehrere Millisekunden dauern. Nachfolgend finden Sie eine <chrono>-kompatible Problemumgehung. Es funktioniert jedoch nur auf Intel-Hardware, Sie müssen in die Inline-Assembly einsteigen (die Syntax dazu variiert je nach Compiler) und Sie müssen die Taktfrequenz der Maschine fest in der Uhr verankern:

#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 es nicht feiner. Die heutigen Taktgeschwindigkeiten sind zwar vorgewarnt, können sich jedoch dynamisch ändern (sie sind keine Konstante für die Kompilierzeit). Und mit einer Multiprozessor-Maschine können Sie sogar Zeitstempel von verschiedenen Prozessoren erhalten. Experimente auf meiner Hardware funktionieren jedoch recht gut. Wenn Sie mit Millisekunden-Auflösung nicht weiterkommen, könnte dies eine Problemumgehung sein.

Diese Uhr hat eine Dauer in Bezug auf die Taktrate Ihrer CPU (wie Sie sie gemeldet haben). Das heißt Für mich tickt diese Uhr einmal alle 1/2.800.000.000 Sekunden. Wenn Sie möchten, können Sie dies beispielsweise in Nanosekunden konvertieren:

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 verkürzt Fraktionen 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 bis zu 18 Takten zurückgeben, die auf 6 Nanosekunden verkürzt wird.

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

Wenn Sie dies hinzufügen, sparen Sie etwas Zeit beim Debuggen, wenn Sie diesen Code auf Ihren neuen Computer portieren und vergessen, den clock::period auf die Geschwindigkeit Ihres neuen Computers zu aktualisieren. Alle Prüfungen werden entweder zur Kompilierzeit oder zur Programmstartzeit durchgeführt. Daher wird die Leistung von clock::now() nicht im geringsten beeinträchtigt.

137
Howard Hinnant

Matthew Wilson 's STLSoft-Bibliotheken bieten verschiedene Timer-Typen mit kongruenten Schnittstellen, die Plug-and-Play ermöglichen. Zu den Angeboten gehören Timer, die kostengünstig sind, aber eine niedrige Auflösung haben, und solche, die hochauflösend sind, aber hohe Kosten verursachen. Es gibt auch solche zum Messen von Vor-Thread-Zeiten und zum Messen von Prozesszeiten sowie für alle, die verstrichene Zeiten messen.

Es gibt einen ausführlichen Artikel, der in Dr. Dobb's vor einigen Jahren darüber berichtet, obwohl er nur die Windows-Artikel abdeckt, die im WinSTL-Unterprojekt definiert sind. STLSoft stellt auch UNIX-Timer im UNIXSTL-Unterprojekt bereit, und Sie können die "PlatformSTL" verwenden, die je nach Bedarf die UNIX- oder Windows-Version 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

6
dcw

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

5
Malte Clasen

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

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

5
Dan

Dies ist nicht die beste Antwort, aber hier sind einige Gespräche auf einer Spieleentwicklungs-Site über hochauflösende Timer:

  1. http://www.gamedev.net/topic/374327-timing-iseverything/
  2. http://www.gamedev.net/topic/471804-high-resolution-timer/
  3. http://www.gamedev.net/topic/40600-high-resolution-timing-in-games/

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

4
Maciek

SDL2 hat einen ausgezeichneten plattformübergreifenden hochauflösenden Timer. Wenn Sie jedoch eine Genauigkeit unter einer Millisekunde benötigen, habe ich eine sehr kleine plattformübergreifende Zeitgeberbibliothek geschrieben - hier ..__, die sowohl mit C++ 03 als auch mit C++ 11/höheren Versionen von C++ kompatibel ist.

3
metamorphosis

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

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

3

STLSoft hat eine Performance Library , die eine Reihe von Zeitgeberklassen enthält, von denen einige sowohl für UNIX als auch für Windows geeignet sind.

2
JamieH

Die erste Antwort auf C++ - Bibliotheksfragen lautet im Allgemeinen BOOST: http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm . Tun das, was Sie wollen? Wahrscheinlich nicht, aber es ist ein Anfang.

Das Problem ist, dass Sie möchten, dass tragbare und Timer-Funktionen in Betriebssystemen nicht universell sind.

2
jmucchiello

Ich bin mir nicht sicher über Ihre Anforderung. Wenn Sie das Zeitintervall berechnen möchten, lesen Sie bitte den folgenden Thread 

Berechnung der verstrichenen Zeit in einem C-Programm in Millisekunden

1
Satbir

Spät zur Party hier, aber ich arbeite in einer alten Codebase, die noch nicht auf C++ 11 aktualisiert werden kann. Niemand in unserem Team ist in C++ sehr gut ausgebildet, daher ist das Hinzufügen einer Bibliothek wie STL schwierig (zusätzlich zu den potenziellen Bedenken, die von anderen bei Bereitstellungsproblemen aufgeworfen wurden). Ich brauchte wirklich einen extrem einfachen plattformübergreifenden Timer, der ohne bare-bone Standard-Systembibliotheken alleine leben konnte. Folgendes habe ich gefunden:

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

Veröffentlichen Sie die gesamte Quelle hier, damit sie nicht verloren geht, wenn die Site einmal 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 ([email protected]) - 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 Headerdatei:

//////////////////////////////////////////////////////////////////////////////
// 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 ([email protected]) - 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
Patrick

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

0
lpapp