C ++, das unter Linux Millisekunden Zeit erhält - clock () scheint nicht richtig zu funktionieren

102

Gibt unter Windows clock()die Zeit in Millisekunden zurück, aber auf dieser Linux-Box, an der ich arbeite, wird sie auf die nächste 1000 gerundet, sodass die Genauigkeit nur auf der "zweiten" Ebene und nicht auf der Millisekunden-Ebene liegt.

Ich habe mit Qt eine Lösung gefunden, bei der die QTimeKlasse verwendet, ein Objekt instanziiert und aufgerufen start()und dann aufgerufen wurde elapsed(), um die Anzahl der verstrichenen Millisekunden abzurufen.

Ich hatte ein bisschen Glück, weil ich zunächst mit Qt zusammenarbeite, aber ich hätte gerne eine Lösung, die nicht auf Bibliotheken von Drittanbietern basiert.

Gibt es keinen Standardweg, um dies zu tun?

AKTUALISIEREN

Bitte empfehlen Sie Boost nicht ..

Wenn Boost und Qt das können, ist es sicherlich keine Magie, es muss einen Standard geben, den sie verwenden!

hasen
quelle
2
Über das Bearbeiten - aber es auf tragbare Weise zu tun, ist ein Schmerz.
Anonym

Antworten:

37

Sie können gettimeofday zu Beginn und am Ende Ihrer Methode verwenden und dann die beiden Rückgabestrukturen unterscheiden. Sie erhalten eine Struktur wie die folgende:

struct timeval {
  time_t tv_sec;
  suseconds_t tv_usec;
}

BEARBEITEN: Wie aus den beiden folgenden Kommentaren hervorgeht, ist clock_gettime (CLOCK_MONOTONIC) eine viel bessere Wahl, wenn Sie es zur Verfügung haben, was heutzutage fast überall sein sollte.

EDIT: Jemand anderes hat kommentiert, dass Sie auch modernes C ++ mit std :: chrono :: high_resolution_clock verwenden können, aber das ist nicht garantiert monoton. Verwenden Sie stattdessen stetige Uhr.

Adam Hawes
quelle
38
schrecklich für ernsthafte Arbeit. Große Probleme zweimal im Jahr, wenn jemand Datumsangaben macht, und natürlich NTP-Synchronisierung. Verwenden Sie clock_gettime (CLOCK_MONOTONIC,)
AndrewStone
9
@ AndrewStone: Die UNIX-Zeit ändert sich nicht zweimal pro Jahr. Oder sogar einmal im Jahr. Aber ja, CLOCK_MONOTONICist großartig, um lokalisierte Systemzeitanpassungen zu vermeiden.
Leichtigkeitsrennen im Orbit
138
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    struct timeval start, end;

    long mtime, seconds, useconds;    

    gettimeofday(&start, NULL);
    usleep(2000);
    gettimeofday(&end, NULL);

    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

    printf("Elapsed time: %ld milliseconds\n", mtime);

    return 0;
}
CTT
quelle
4
Warum addieren Sie +0,5 zur Differenz?
Mahmoud Al-Qudsi
19
@ Computer Guru, es ist eine übliche Technik zum Runden positiver Werte. Wenn der Wert auf einen ganzzahligen Wert gekürzt wird, wird alles zwischen 0,0 und 0,4999 ... bevor die Addition auf 0 und zwischen 0,5 und 0,9999 ... auf 1 gekürzt wird.
Mark Ransom
8
tv_usec ist keine Millisekunde, sondern Mikrosekunden.
NebulaFox
13
schrecklich für ernsthafte Arbeit. Große Probleme zweimal im Jahr, wenn jemand Datumsangaben macht, und natürlich NTP-Synchronisierung
AndrewStone
14
@AndrewStone ist richtig. Verwenden Sie clock_gettime (2) mit CLOCK_REALTIME, um die Zeiten auf demselben Computer zu vergleichen. Vom gettimeofday (2) manpage: POSIX.1-2008 marks gettimeofday() as obsolete, recommending the use of clock_gettime(2) instead. @CTT, Sie das Beispiel , indem die Aktualisierung konnte struct timevalzu struct timespec, und gettimeofday(&start, NULL)zu clock_gettime(CLOCK_MONOTONIC, &start)so , dass die Menschen in Schwierigkeiten nicht laufen können?
Bobby Powers
58

Bitte beachten Sie, dass die clockWanduhrzeit nicht gemessen wird. Das heißt, wenn Ihr Programm 5 Sekunden dauert, clockmisst es nicht unbedingt 5 Sekunden, sondern könnte mehr (Ihr Programm könnte mehrere Threads ausführen und somit mehr CPU als Echtzeit verbrauchen) oder weniger. Es misst eine Annäherung an die verwendete CPU-Zeit . Um den Unterschied zu sehen, betrachten Sie diesen Code

#include <iostream>
#include <ctime>
#include <unistd.h>

int main() {
    std::clock_t a = std::clock();
    sleep(5); // sleep 5s
    std::clock_t b = std::clock();

    std::cout << "difference: " << (b - a) << std::endl;
    return 0;
}

Es wird auf meinem System ausgegeben

$ difference: 0

Weil wir nur geschlafen haben und keine CPU-Zeit verbraucht haben! Mit gettimeofdaybekommen wir jedoch, was wir wollen (?)

#include <iostream>
#include <ctime>
#include <unistd.h>
#include <sys/time.h>

int main() {
    timeval a;
    timeval b;

    gettimeofday(&a, 0);
    sleep(5); // sleep 5s
    gettimeofday(&b, 0);

    std::cout << "difference: " << (b.tv_sec - a.tv_sec) << std::endl;
    return 0;
}

Ausgänge auf meinem System

$ difference: 5

Wenn Sie mehr Präzision benötigen, aber CPU-Zeit erhalten möchten, können Sie die getrusageFunktion verwenden.

Johannes Schaub - litb
quelle
⁺¹ über Erwähnung a sleep()- Es wird bereits angenommen, dass ich eine Frage stelle (warum funktioniert es für alle außer mir?!) , Wenn Ihre Antwort gefunden wurde.
Hi-Angel
18

Ich empfehle auch die von Boost angebotenen Tools. Entweder der erwähnte Boost-Timer oder etwas aus Boost.DateTime hacken oder es gibt eine neue vorgeschlagene Bibliothek in der Sandbox - Boost.Chrono : Dieser letzte wird ein Ersatz für den Timer sein und Folgendes bieten :

  • Die Zeitdienstprogramme der C ++ 0x Standard Library, einschließlich:
    • Klassenvorlage duration
    • Klassenvorlage time_point
    • Uhren:
      • system_clock
      • monotonic_clock
      • high_resolution_clock
  • Klassenvorlage timermit typedefs:
    • system_timer
    • monotonic_timer
    • high_resolution_timer
  • Prozessuhren und Timer:
    • process_clockErfassen von Real-, Benutzer-CPU- und System-CPU-Zeiten.
    • process_timerErfassen der verstrichenen Real-, Benutzer-CPU- und System-CPU-Zeiten.
    • run_timer, bequeme Berichterstattung über | process_timer | Ergebnisse.
  • Die rationale Arithmetik zur Kompilierungszeit der C ++ 0x-Standardbibliothek.

Hier ist die Quelle der Funktionsliste

Anonym
quelle
Im Moment können Sie den Boost-Timer verwenden und dann ordnungsgemäß zu Chrono migrieren, wenn er überprüft / akzeptiert wird.
Anonym
13

Ich habe eine TimerKlasse geschrieben, die auf der Antwort von CTT basiert . Es kann folgendermaßen verwendet werden:

Timer timer = Timer();
timer.start();
/* perform task */
double duration = timer.stop();
timer.printTime(duration);

Hier ist seine Implementierung:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
using namespace std;

class Timer {
private:

    timeval startTime;

public:

    void start(){
        gettimeofday(&startTime, NULL);
    }

    double stop(){
        timeval endTime;
        long seconds, useconds;
        double duration;

        gettimeofday(&endTime, NULL);

        seconds  = endTime.tv_sec  - startTime.tv_sec;
        useconds = endTime.tv_usec - startTime.tv_usec;

        duration = seconds + useconds/1000000.0;

        return duration;
    }

    static void printTime(double duration){
        printf("%5.6f seconds\n", duration);
    }
};
Chris Redford
quelle
2
Das ist cool, aber die "nseconds" sind irreführend, weil timeval keine Nanosekunden, sondern Mikrosekunden enthält. Ich würde daher vorschlagen, dass die Leute dies "useconds" nennen.
Pho0
Vielen Dank. Korrektur vorgenommen.
Chris Redford
9

Wenn Sie nicht möchten, dass der Code auf alte Unices portierbar ist, können Sie clock_gettime () verwenden, mit dem Sie die Zeit in Nanosekunden angeben können (wenn Ihr Prozessor diese Auflösung unterstützt). Es ist POSIX, aber ab 2001.

Zachary Hamm
quelle
4

clock () hat oft eine ziemlich miese Auflösung. Wenn Sie die Zeit im Millisekundenbereich messen möchten, können Sie alternativ clock_gettime () verwenden, wie in dieser Frage erläutert.

(Denken Sie daran, dass Sie unter Linux eine Verknüpfung mit -lrt herstellen müssen.)

JesperE
quelle
4

Mit C ++ 11 std::chrono::high_resolution_clockkönnen Sie dies tun:

#include <iostream>
#include <chrono>
#include <thread>
typedef std::chrono::high_resolution_clock Clock;

int main()
{
    std::chrono::milliseconds three_milliseconds{3};

    auto t1 = Clock::now();
    std::this_thread::sleep_for(three_milliseconds);
    auto t2 = Clock::now();

    std::cout << "Delta t2-t1: " 
              << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count()
              << " milliseconds" << std::endl;
}

Ausgabe:

Delta t2-t1: 3 milliseconds

Link zur Demo: http://cpp.sh/2zdtu

Martin G.
quelle
2

clock () gibt unter Linux keine Millisekunden oder Sekunden zurück. Normalerweise gibt clock () auf einem Linux-System Mikrosekunden zurück. Die richtige Methode zur Interpretation des von clock () zurückgegebenen Werts besteht darin, ihn durch CLOCKS_PER_SEC zu teilen, um herauszufinden, wie viel Zeit vergangen ist.

Seifenkiste
quelle
nicht in der Box, an der ich arbeite! plus, ich bin Dividieren durch CLOCKS_PER_SEC, aber es ist sinnlos , weil die Auflösung nur bis zum zweiten ist
hase
Um fair zu sein, die Einheiten sind Mikrosekunden (CLOCKS_PER_SEC ist 1000000 auf allen POSIX-Systemen). Nur hat es Sekunden Auflösung. :-P.
Evan Teran
1

Dies sollte funktionieren ... auf einem Mac getestet ...

#include <stdio.h>
#include <sys/time.h>

int main() {
        struct timeval tv;
        struct timezone tz;
        struct tm *tm;
        gettimeofday(&tv,&tz);
        tm=localtime(&tv.tv_sec);
        printf("StartTime: %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec);
}

Ja ... zweimal ausführen und subtrahieren ...

Jason Punyon
quelle
1

Im POSIX-Standard clockist der Rückgabewert anhand des Symbols CLOCKS_PER_SEC definiert, und eine Implementierung kann diesen auf jede bequeme Weise definieren. Unter Linux hatte ich viel Glück mit der times()Funktion.

Jon Trauntvein
quelle
1

gettimeofday - das Problem ist, dass es niedrigere Werte geben kann, wenn Sie die Hardware-Uhr ändern (z. B. mit NTP). Boost - für diese Projektuhr nicht verfügbar () - gibt normalerweise eine 4-Byte-Ganzzahl zurück, was bedeutet, dass die Kapazität niedrig ist, und Nach einiger Zeit werden negative Zahlen zurückgegeben.

Ich ziehe es vor, meine eigene Klasse zu erstellen und alle 10 Millisekunden zu aktualisieren, damit dieser Weg flexibler ist und ich ihn sogar verbessern kann, um Abonnenten zu haben.

class MyAlarm {
static int64_t tiempo;
static bool running;
public:
static int64_t getTime() {return tiempo;};
static void callback( int sig){
    if(running){
        tiempo+=10L;
    }
}
static void run(){ running = true;}
};

int64_t MyAlarm::tiempo = 0L;
bool MyAlarm::running = false;

Um es zu aktualisieren, benutze ich Setitimer:

int main(){
struct sigaction sa; 
struct itimerval timer; 

MyAlarm::run();
memset (&sa, 0, sizeof (sa)); 
sa.sa_handler = &MyAlarm::callback; 

sigaction (SIGALRM, &sa, NULL); 


timer.it_value.tv_sec = 0; 
timer.it_value.tv_usec = 10000; 



timer.it_interval.tv_sec = 0; 
timer.it_interval.tv_usec = 10000; 


setitimer (ITIMER_REAL, &timer, NULL); 
.....

Schauen Sie sich Setitimer und ITIMER_VIRTUAL und ITIMER_REAL an.

Verwenden Sie nicht die Alarm- oder ualarm-Funktionen, da Sie eine geringe Präzision haben, wenn Ihr Prozess eine harte Arbeit erhält.

Hola Soja Edu Feliz Navidad
quelle
0

Ich bevorzuge die Boost-Timer-Bibliothek wegen ihrer Einfachheit, aber wenn Sie keine Bibliotheken für Drittanbieter verwenden möchten, erscheint die Verwendung von clock () sinnvoll.

Nick Presta
quelle
0

Als Update erscheint, dass unter Windows clock () die Wanduhrzeit misst (mit CLOCKS_PER_SEC-Genauigkeit)

 http://msdn.microsoft.com/en-us/library/4e2ess30(VS.71).aspx

Unter Linux wird die CPU-Zeit über die Kerne hinweg gemessen, die vom aktuellen Prozess verwendet werden

http://www.manpagez.com/man/3/clock

und (wie es scheint und wie auf dem Originalplakat vermerkt) tatsächlich mit geringerer Genauigkeit als CLOCKS_PER_SEC, obwohl dies möglicherweise von der spezifischen Linux-Version abhängt.

Rogerdpack
quelle
0

Ich mag die Hola Soy-Methode, gettimeofday () nicht zu verwenden. Es ist mir auf einem laufenden Server passiert, dass der Administrator die Zeitzone geändert hat. Die Uhr wurde aktualisiert, um denselben (korrekten) lokalen Wert anzuzeigen. Dies führte dazu, dass die Funktionszeit () und gettimeofday () um 2 Stunden verschoben wurden und alle Zeitstempel in einigen Diensten stecken blieben.

gkolarov
quelle
0

Ich habe eine C++Klasse mit geschrieben timeb.

#include <sys/timeb.h>
class msTimer 
{
public:
    msTimer();
    void restart();
    float elapsedMs();
private:
    timeb t_start;
};

Mitgliedsfunktionen:

msTimer::msTimer() 
{ 
    restart(); 
}

void msTimer::restart() 
{ 
    ftime(&t_start); 
}

float msTimer::elapsedMs() 
{
    timeb t_now;
    ftime(&t_now);
    return (float)(t_now.time - t_start.time) * 1000.0f +
           (float)(t_now.millitm - t_start.millitm);
}

Anwendungsbeispiel:

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char** argv) 
{
    msTimer t;
    for (int i = 0; i < 5000000; i++)
        ;
    std::cout << t.elapsedMs() << endl;
    return 0;
}

Die Ausgabe auf meinem Computer ist '19'. Die Genauigkeit der msTimerKlasse liegt in der Größenordnung von Millisekunden. Im obigen Verwendungsbeispiel wird die Gesamtzeit der Ausführung, die von der forSchleife benötigt wird, verfolgt. Diese Zeit beinhaltete das Ein- und Ausschalten des Betriebssystems in den Ausführungskontext main()aufgrund von Multitasking.

user3674642
quelle