2012-01-28 17 views
5

Próbuję mierzyć krótkie interwały czasowe, rzędu kilku milisekund. To okazuje się jakimś nietrywialnym zadaniem, ponieważ wszechobecna funkcja time() działa z dokładnością do kilku sekund. Po niektórych badań I wyszedł z czterech metod widzianych w próbce kodu poniżej:Mierzenie krótkich odstępów czasu w C na Ubuntu/VMware

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

int main(int argc, char *argv[]) 
{ 
    clock_t t0, t1; 
    double dt0; 

    struct timespec t2, t3; 
    double dt1; 

    struct timespec t4, t5; 
    double dt2; 

    struct timeval t6, t7; 
    double dt3; 

    volatile long long i; 

    t2.tv_sec = 0; 
    t2.tv_nsec = 0; 
    clock_settime(CLOCK_MONOTONIC, &t2); 
    clock_settime(CLOCK_REALTIME, &t2); 

    t0 = clock(); 
    clock_gettime(CLOCK_REALTIME, &t2); 
    clock_gettime(CLOCK_MONOTONIC, &t4); 
    gettimeofday(&t6, NULL); 

    getchar(); 
    for (i=0; i<1e9; i++) {}; 

    gettimeofday(&t7, NULL); 
    clock_gettime(CLOCK_MONOTONIC, &t5); 
    clock_gettime(CLOCK_REALTIME, &t3); 
    t1 = clock(); 

    dt0 = (double) (t1 - t0)/CLOCKS_PER_SEC; 
    dt1 = (double) (t3.tv_nsec - t2.tv_nsec)/1e9; 
    dt2 = (double) (t5.tv_nsec - t4.tv_nsec)/1e9; 
    dt3 = (double) (t7.tv_usec - t6.tv_usec)/1e6; 

    printf("1. clock():   [0]=%10.0f, [1]=%10.0f, [1-0]=%10.6f sec\n", (double) t0, (double) t1, dt0); 
    printf("2. clock_gettime(R): [2]=%10ld, [3]=%10ld, [3-2]=%10f sec\n", (long) t2.tv_nsec, (long) t3.tv_nsec, dt1); 
    printf("3. clock_gettime(M): [2]=%10ld, [3]=%10ld, [3-2]=%10f sec\n", (long) t4.tv_nsec, (long) t5.tv_nsec, dt2); 
    printf("4. gettimeofday(): [4]=%10ld, [5]=%10ld, [5-4]=%10f sec\n", (long) t6.tv_usec, (long) t7.tv_usec, dt3); 

    return 0; 
} 

Potem skompilowany i uruchomić go z:

gcc -lrt -o timer.e timer.c; time ./timer.e 

użyłem połączenia getchar() wprowadzić kilka sekund opóźnienia, aby zobaczyć różnicę między real i user raportów czasu, a wynik był następujący:

1. clock():   [0]=   0, [1]= 3280000, [1-0]= 3.280000 sec 
2. clock_gettime(R): [2]= 823922476, [3]= 478650549, [3-2]= -0.345272 sec 
3. clock_gettime(M): [2]= 671137949, [3]= 325864897, [3-2]= -0.345273 sec 
4. gettimeofday(): [4]= 823924, [5]= 478648, [5-4]= -0.345276 sec 

real 0m6.659s 
user 0m3.280s 
sys  0m0.010s 

Jak widać, jedyną istotnym wynikiem jest korelacja między metodą 1 i czasem user zgłoszonym przez komendę time.

To przynosi kilka pytań: 1. Dlaczego wyniki 2-4 są bez znaczenia? 2. Jak zmierzyć czas spędzony w czasie pracy programu, taki jak raporty real numer time?

Moje środowisko to Ubuntu 10.04 LTS 64-bit na VMware na Windows 7 na laptopie HP z procesorem AMD.

+0

Jeśli jest to w celach testowych, może okazać się pomocne, aby zwiększyć liczbę powtórzeń. To oczywiście sprawi, że test będzie działał dłużej, ale może wykorzystywać pomiary taktowania o niższej rozdzielczości. –

+0

@MichaelMior - jest używany do bardzo surowych testów porównawczych - Próbuję porównać czas działania jakiegoś algorytmu, który jest wykonywany na hoście (linuksie) i na akceleratorze sprzętowym podłączonym przez USB. W obu przypadkach program wywołujący jest tym samym programem, programem hosta i potrzebuję sposobu na uzyskanie całkowitego czasu, w którym "siedzi i czeka" na zewnętrzny sprzęt, aby zakończyć pracę. Właśnie dlatego clock() nie jest odpowiedzią, b/c nie liczy się na czas oczekiwania (jak pokazano tu w wywołaniu getchar()). – ysap

Odpowiedz

12

Jesteś na dobrej drodze dzięki clock_gettime().

Powinieneś jednak pozbyć się tych połączeń clock_settime(): po prostu zadzwoń pod clock_gettime() przed i po bloku kodu i spójrz na różnicę.

Również przy obliczaniu różnicy należy uwzględnić zarówno wartości tv_sec, jak i tv_nsec; twój aktualny kod tylko patrzy na nanosekundowy komponent i ignoruje pełne sekundy.

W moim systemie Ubuntu następujący kod daje bardzo dokładny pomiar czasu potrzebnego do wykonania pętli:

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

int main(int argc, char *argv[]) 
{ 
    struct timespec t2, t3; 
    double dt1; 

    volatile long long i; 

    clock_gettime(CLOCK_MONOTONIC, &t2); 
    for (i=0; i<1e9; i++) {}; 
    clock_gettime(CLOCK_MONOTONIC, &t3); 
    //time in seconds 
    dt1 = (t3.tv_sec - t2.tv_sec) + (double) (t3.tv_nsec - t2.tv_nsec) * 1e-9; 
    printf("%f\n", dt1); 

    return 0; 
} 
+0

Dzięki! Ta korekta robi to, czego szukam. Co jakiś czas po prostu tęskni się za oczywistością. Pomyślałem, że pola _sec i _nsec obarczają te same dane z różną precyzją, ale teraz widzę, że to całe i nanoseczkowe części znacznika czasu. – ysap

1

Niektóre wskazówkę na temat pomiaru czasu do mikro i nawet nano sekund http://en.wikipedia.org/wiki/Time_Stamp_Counter

+0

Dzięki za wskazanie wyjaśnienia na rdtsc. Może być przydatny w niektórych kontekstach, ale staram się dowiedzieć, dlaczego napisany przeze mnie kod nie daje oczekiwanego rezultatu. – ysap

1

Twoje połączenia z numerami clock_settime nie są zgodne z Twoim zdaniem. Gdybyś miał pozwolenie, ustawilibyśmy po prostu systemowy zegar na 0, prawdopodobnie nie to, co chcesz. Prawie wszystkie funkcje POSIX mają wartość zwracaną, która mówi, czy połączenie się powiodło. Zazwyczaj ignorowanie tej wartości jest złym pomysłem. W Twoim przypadku w szczególności myślę, że powinien dać tę wartość, jak można je znaleźć na stronie man:

EPERM clock_settime() nie ma uprawnień, aby ustawić zegar wskazaną.

Na szczęście nie masz uprawnień, aby ustawić zegar tak prosto jak to :)

+0

Dodałem rozliczenie po otrzymaniu dziwnych wyników z gettime. Najwyraźniej nie miało to żadnego wpływu na wynik. – ysap

+0

@ysap, to jest dokładnie to, co mówię. Nie ma to wpływu na wynik, ponieważ ** zawodzi **. W przeciwnym razie miałoby to dramatyczne konsekwencje dla twojego komputera, ustawiając zegar systemowy na coś takiego jak 1 stycznia 1970 roku. Nie rób tego. Sprawdź zwrot wywołań systemowych, aby sprawdzić, czy się uda, czy nie. –

+0

Tak, punkt zajęty, a kod został usunięty z programu. – ysap

Powiązane problemy