C ++ uzyskuje milisekundowy czas na Linuksie - clock () wydaje się nie działać poprawnie

102

W systemie Windows clock()zwraca czas w milisekundach, ale na tym Linuksie, nad którym pracuję, zaokrągla go do najbliższego 1000, więc precyzja jest tylko do poziomu „drugiego”, a nie do poziomu milisekund.

Znalazłem rozwiązanie, w którym Qt używa QTimeklasy, tworząc instancję obiektu i wywołując start()go, a następnie wywołując, elapsed()aby uzyskać liczbę milisekund, które upłynęły.

Miałem szczęście, ponieważ na początku pracuję z Qt, ale chciałbym rozwiązanie, które nie opiera się na bibliotekach innych firm,

Czy nie ma na to standardowego sposobu?

AKTUALIZACJA

Nie polecaj Boost.

Jeśli Boost i Qt mogą to zrobić, z pewnością nie jest to magia, musi być coś standardowego, czego używają!

hasen
źródło
2
O edycji - ale zrobienie tego w przenośny sposób jest trochę uciążliwe.
Anonimowy

Odpowiedzi:

37

Możesz użyć gettimeofday na początku i na końcu metody, a następnie rozróżnić dwie struktury zwracające. Otrzymasz strukturę podobną do poniższej:

struct timeval {
  time_t tv_sec;
  suseconds_t tv_usec;
}

EDYCJA: Jak sugerują dwa poniższe komentarze, clock_gettime (CLOCK_MONOTONIC) jest znacznie lepszym wyborem, jeśli masz go dostępnym, co powinno być obecnie prawie wszędzie.

EDYCJA: Ktoś inny skomentował, że można również używać nowoczesnego C ++ ze std :: chrono :: high_resolution_clock, ale nie ma gwarancji, że będzie to monotoniczne. Zamiast tego użyj steady_clock.

Adam Hawes
źródło
38
straszne do poważnej pracy. Duże problemy dwa razy w roku, kiedy ktoś robi randki i oczywiście synchronizację NTP. Użyj clock_gettime (CLOCK_MONOTONIC,)
AndrewStone
9
@AndrewStone: czas UNIX nie zmienia się dwa razy w roku. Lub nawet raz w roku. Ale tak, CLOCK_MONOTONICświetnie nadaje się do unikania lokalnych korekt czasu systemu.
Wyścigi lekkości na orbicie
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
źródło
4
Dlaczego dodajesz +0,5 do różnicy?
Mahmoud Al-Qudsi
19
@Computer Guru, to powszechna technika zaokrąglania wartości dodatnich. Gdy wartość zostanie obcięta do liczby całkowitej, wszystko z przedziału od 0,0 do 0,4999 ... przed dodaniem zostanie obcięte do 0, a od 0,5 do 0,9999 ... zostanie obcięte do 1.
Okup Okup
8
tv_usec to nie milisekundy, to mikrosekundy.
NebulaFox
13
straszne do poważnej pracy. Duże problemy dwa razy w roku, kiedy ktoś robi randki i oczywiście synchronizację NTP
AndrewStone
14
@AndrewStone ma rację, użyj clock_gettime (2) z CLOCK_REALTIME, aby porównać czasy na tym samym komputerze. Z gettimeofday (2) podręcznika: POSIX.1-2008 marks gettimeofday() as obsolete, recommending the use of clock_gettime(2) instead. @CTT, można zaktualizować przykład zmieniając struct timevalsię struct timespec, i gettimeofday(&start, NULL)aby clock_gettime(CLOCK_MONOTONIC, &start)tak, że ludzie nie popaść w kłopoty?
Bobby Powers
58

Należy pamiętać, że clocknie nie mierzyć czas zegar ścienny. Oznacza to, że jeśli twój program zajmuje 5 sekund, clockniekoniecznie będzie mierzył 5 sekund, ale może więcej (twój program może uruchamiać wiele wątków, a więc może zużywać więcej procesora niż w czasie rzeczywistym) lub mniej. Mierzy przybliżenie używanego czasu procesora . Aby zobaczyć różnicę, rozważ ten kod

#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;
}

Wyprowadza w moim systemie

$ difference: 0

Ponieważ wszystko, co robiliśmy, to spanie i nie zużywanie czasu procesora! Jednak używając gettimeofdayotrzymujemy to, czego chcemy (?)

#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;
}

Wyjścia w moim systemie

$ difference: 5

Jeśli potrzebujesz większej precyzji, ale chcesz uzyskać czas procesora , możesz rozważyć użycie getrusagefunkcji.

Johannes Schaub - litb
źródło
⁺¹ o wzmiance a sleep()- Pomyślałem już o zadaniu pytania (dlaczego to działa dobrze dla wszystkich oprócz mnie ?!) , gdy znalazłem odpowiedź.
Hi-Angel
18

Polecam również narzędzia oferowane przez Boost. Albo wspomniany Boost Timer, albo zhakuj coś z Boost.DateTime albo w piaskownicy pojawi się nowa proponowana biblioteka - Boost.Chrono : Ta ostatnia będzie zamiennikiem Timera i będzie zawierała:

  • Narzędzia czasowe biblioteki standardowej C ++ 0x, w tym:
    • Szablon zajęć duration
    • Szablon zajęć time_point
    • Zegary:
      • system_clock
      • monotonic_clock
      • high_resolution_clock
  • Szablon klasy timerz typami definicji:
    • system_timer
    • monotonic_timer
    • high_resolution_timer
  • Przetwarzanie zegarów i timerów:
    • process_clock, rejestrując rzeczywiste czasy procesora użytkownika i systemu.
    • process_timer, przechwytywanie czasu rzeczywistego, czasu procesora użytkownika i czasu procesora systemu.
    • run_timer, wygodne raportowanie | process_timer | wyniki.
  • Arytmetyka racjonalna czasu kompilacji biblioteki standardowej C ++ 0x.

Oto źródło listy funkcji

Anonimowy
źródło
Na razie możesz użyć Boost Timer, a następnie z wdziękiem przejść do Chrono, gdy zostanie sprawdzony / zaakceptowany.
Anonimowy
13

Napisałem Timerzajęcia na podstawie odpowiedzi CTT . Można go używać w następujący sposób:

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

Oto jego realizacja:

#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
źródło
2
To jest fajne, ale „nseconds” jest mylące, ponieważ timeval nie ma nanosekund, ale zawiera mikrosekundy, więc sugerowałbym, aby ludzie nazywali to „useconds”.
pho0
Dzięki. Korekta wykonana.
Chris Redford,
9

Jeśli nie potrzebujesz, aby kod był przenoszony na stare monitory, możesz użyć clock_gettime (), która poda czas w nanosekundach (jeśli twój procesor obsługuje tę rozdzielczość). To POSIX, ale od 2001 roku.

Zachary Hamm
źródło
4

clock () ma często kiepską rozdzielczość. Jeśli chcesz mierzyć czas na poziomie milisekund, jedną z możliwości jest użycie metody clock_gettime (), as wyjaśniono w tym pytaniu.

(Pamiętaj, że musisz połączyć się z -lrt w systemie Linux).

JesperE
źródło
4

Dzięki C ++ 11 std::chrono::high_resolution_clockmożesz to zrobić:

#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;
}

Wynik:

Delta t2-t1: 3 milliseconds

Link do demo: http://cpp.sh/2zdtu

Martin G.
źródło
2

clock () nie zwraca milisekund ani sekund w systemie Linux. Zwykle clock () zwraca mikrosekundy w systemie Linux. Właściwym sposobem interpretacji wartości zwracanej przez funkcję clock () jest podzielenie jej przez CLOCKS_PER_SEC w celu ustalenia, ile czasu minęło.

SoapBox
źródło
nie w pudełku, nad którym pracuję! Plus, ja jestem podzielenie przez CLOCKS_PER_SEC, ale to nie ma sensu, ponieważ rozdzielczość to tylko w dół do drugiego
hasen
cóż, aby być uczciwym, jednostkami mikrosekundy (CLOCKS_PER_SEC to 1000000 we wszystkich systemach POSIX). Po prostu ma rozdzielczość sekund. :-P.
Evan Teran,
1

To powinno działać ... przetestowane na komputerze Mac ...

#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);
}

Tak ... uruchom to dwa razy i odejmij ...

Jason Punyon
źródło
1

W standardzie POSIX clockwartość zwracana jest zdefiniowana w postaci symbolu CLOCKS_PER_SEC i implementacja może to zdefiniować w dowolny dogodny sposób. W Linuksie miałem szczęście z tą times()funkcją.

Jon Trauntvein
źródło
1

gettimeofday - problem polega na tym, że jeśli zmienisz zegar sprzętowy (na przykład z NTP), mogą mieć niższe wartości. Boost - niedostępne dla tego projektu clock () - zwykle zwraca 4-bajtową liczbę całkowitą, co oznacza, że ​​ma małą pojemność i po pewnym czasie zwraca liczby ujemne.

Wolę tworzyć własną klasę i aktualizować co 10 milisekund, więc ten sposób jest bardziej elastyczny, a nawet mogę go ulepszyć, aby mieć subskrybentów.

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;

aby odświeżyć to używam 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); 
.....

Spójrz na setitimer oraz ITIMER_VIRTUAL i ITIMER_REAL.

Nie używaj funkcji alarmu lub ualarmu, uzyskasz niską precyzję, gdy proces będzie ciężki.

Hola Soy Edu Feliz Navidad
źródło
0

Wolę bibliotekę Boost Timer ze względu na jej prostotę, ale jeśli nie chcesz korzystać z bibliotek trzeciej strony, użycie clock () wydaje się rozsądne.

Nick Presta
źródło
0

Jako aktualizacja wydaje się, że w systemie Windows clock () mierzy czas zegara ściennego (z dokładnością do CLOCKS_PER_SEC)

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

podczas gdy w Linuksie mierzy czas procesora na rdzeniach używanych przez bieżący proces

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

i (wydaje się, i jak zauważył oryginalny plakat) faktycznie z mniejszą precyzją niż CLOCKS_PER_SEC, chociaż może to zależy od konkretnej wersji Linuksa.

rogerdpack
źródło
0

Podoba mi się metoda Hola Soy, która nie używa gettimeofday (). Zdarzyło mi się, że na działającym serwerze administrator zmienił strefę czasową. Zegar został zaktualizowany, aby pokazywał tę samą (poprawną) wartość lokalną. Spowodowało to przesunięcie funkcji time () i gettimeofday () o 2 godziny, a wszystkie znaczniki czasu w niektórych usługach utknęły.

gkolarov
źródło
0

Napisałem C++klasę używając timeb.

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

Funkcje członków:

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);
}

Przykład użycia:

#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;
}

Wyjście na moim komputerze to „19”. Dokładność msTimerklasy jest rzędu milisekund. W powyższym przykładzie użycia forśledzony jest całkowity czas wykonania zajęty przez -loop. W tym czasie system operacyjny włączał i wyłączał kontekst wykonania z main()powodu wielozadaniowości.

user3674642
źródło