Używając tylko ANSI C, czy istnieje sposób mierzenia czasu z dokładnością do milisekund lub większą? Przeglądałem czas.h, ale znalazłem tylko funkcje drugiej precyzji.
c
portability
time-precision
corto
źródło
źródło
Odpowiedzi:
Nie ma funkcji ANSI C, która zapewnia rozdzielczość lepszą niż 1 sekunda, ale funkcja POSIX
gettimeofday
zapewnia rozdzielczość mikrosekundową. Funkcja zegara mierzy tylko ilość czasu, jaką proces spędził na wykonywaniu i nie jest dokładna w wielu systemach.Możesz użyć tej funkcji w następujący sposób:
To powraca
Time elapsed: 1.000870
na moim komputerze.źródło
timeval::tv_usec
jest zawsze poniżej jednej sekundy, jest zapętlony. Tj. Aby wziąć różnice czasu większe niż 1 sekundę, należy:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
funkcji. Możemy użyćtval_result
wartości (tv_sec i tv_usec) bez zmian.źródło
CLOCKS_PER_SEC / 1000
może być niedokładne, co może wpłynąć na wynik końcowy (chociaż z mojego doświadczeniaCLOCKS_PER_SEC
zawsze wynikało wielokrotność 1000). Robi(1000 * clock()) / CLOCKS_PER_SEC
to mniej podatne na podział niedokładność, ale z drugiej strony jest bardziej podatny na przepełnienie. Tylko kilka kwestii do rozważenia.Zawsze używam funkcji clock_gettime (), zwracając czas z zegara CLOCK_MONOTONIC. Zwrócony czas to ilość czasu wyrażona w sekundach i nanosekundach od jakiegoś nieokreślonego punktu w przeszłości, takiego jak uruchomienie systemu w danej epoce.
źródło
clock_gettime(CLOCK_MONOTONIC, ...)
i jest nawet makro testowania funkcji_POSIX_MONOTONIC_CLOCK
.Wdrożenie rozwiązania przenośnego
Jak już tutaj wspomniano, nie ma odpowiedniego rozwiązania ANSI o wystarczającej precyzji dla problemu pomiaru czasu, chcę napisać o sposobach uzyskania przenośnego i, jeśli to możliwe, rozwiązania do pomiaru czasu o wysokiej rozdzielczości.
Zegar monotoniczny a znaczniki czasu
Ogólnie rzecz biorąc, istnieją dwa sposoby pomiaru czasu:
Pierwsza używa monotonicznego licznika zegara (czasami nazywanego licznikiem tików), który zlicza tyknięcia z predefiniowaną częstotliwością, więc jeśli masz wartość tyknięć i częstotliwość jest znana, możesz łatwo przekonwertować tyknięcia na upływający czas. W rzeczywistości nie ma gwarancji, że zegar monotoniczny w jakikolwiek sposób odzwierciedla bieżący czas systemowy, może również liczyć tyknięcia od momentu uruchomienia systemu. Ale to gwarantuje, że zegar jest zawsze uruchamiany w sposób rosnący, niezależnie od stanu systemu. Zwykle częstotliwość jest związana ze sprzętowym źródłem o wysokiej rozdzielczości, dlatego zapewnia wysoką dokładność (zależy od sprzętu, ale większość współczesnego sprzętu nie ma problemów ze źródłami zegara o wysokiej rozdzielczości).
Drugi sposób zapewnia (datę) wartość czasu na podstawie bieżącej wartości zegara systemowego. Może mieć również wysoką rozdzielczość, ale ma jedną poważną wadę: na ten rodzaj wartości czasu mogą wpływać różne ustawienia czasu systemu, tj. Zmiana strefy czasowej, zmiana czasu letniego (DST), aktualizacja serwera NTP, hibernacja systemu i tak dalej. na. W niektórych okolicznościach można uzyskać ujemną wartość czasu, który upłynął, co może prowadzić do nieokreślonego zachowania. W rzeczywistości tego rodzaju źródło czasu jest mniej niezawodne niż pierwsze.
Tak więc pierwszą zasadą pomiaru odstępów czasu jest użycie zegara monotonicznego, jeśli to możliwe. Zwykle ma wysoką precyzję i jest niezawodny z założenia.
Strategia rezerwowa
Wdrażając rozwiązanie przenośne, warto rozważyć strategię awaryjną: użyj zegara monotonicznego, jeśli jest dostępny, i podejścia rezerwowego do znaczników czasu, jeśli w systemie nie ma zegara monotonicznego.
Windows
Jest świetny artykuł zatytułowany Uzyskiwanie znaczników czasu w wysokiej rozdzielczości w witrynie MSDN na temat pomiaru czasu w systemie Windows, w którym opisano wszystkie szczegóły, które mogą być potrzebne do obsługi oprogramowania i sprzętu. Aby uzyskać precyzyjny znacznik czasu w systemie Windows, należy:
zapytaj o częstotliwość zegara ( takty na sekundę) za pomocą QueryPerformanceFrequency :
Częstotliwość timera jest ustalana podczas rozruchu systemu, więc musisz ją pobrać tylko raz.
Zapytaj o bieżącą wartość ticków za pomocą QueryPerformanceCounter :
przeskaluj znaczniki do upływającego czasu, tj. do mikrosekund:
Według Microsoftu w większości przypadków nie powinno być żadnych problemów z takim podejściem na Windows XP i nowszych wersjach. Ale możesz też użyć dwóch rozwiązań awaryjnych w systemie Windows:
GetTickCount
, ale jest dostępna począwszy od systemu Windows Vista i nowszych.OS X (macOS)
OS X (macOS) ma własne jednostki czasu bezwzględnego Macha, które reprezentują zegar monotoniczny. Najlepszym sposobem na rozpoczęcie jest artykuł firmy Apple Technical Q&A QA1398: Mach Absolute Time Units, w którym opisano (z przykładami kodu), jak używać interfejsu API specyficznego dla Macha, aby uzyskać monotoniczne tiki. Istnieje również lokalne pytanie na ten temat, zwane alternatywą clock_gettime w systemie Mac OS X, które na końcu może sprawić, że będziesz nieco zdezorientowany, co zrobić z możliwym przepełnieniem wartości, ponieważ częstotliwość licznika jest używana w postaci licznika i mianownika. A więc krótki przykład, jak obliczyć upływ czasu:
pobierz licznik i mianownik częstotliwości zegara:
Musisz to zrobić tylko raz.
zapytaj o aktualną wartość ticka za pomocą
mach_absolute_time
:przeskaluj takty do upływającego czasu, tj. do mikrosekund, używając wcześniej zadanego licznika i mianownika:
Główną ideą zapobiegania przepełnieniu jest zmniejszenie taktów do pożądanej dokładności przed użyciem licznika i mianownika. Ponieważ początkowa rozdzielczość timera jest w nanosekundach, dzielimy ją przez,
1000
aby uzyskać mikrosekundy. Możesz znaleźć to samo podejście, które zastosowano w time_mac.c Chromium . Jeśli naprawdę potrzebujesz dokładności w nanosekundach, rozważ przeczytanie Jak mogę używać mach_absolute_time bez przepełnienia? .Linux i UNIX
clock_gettime
Rozmowa jest najlepszym sposobem na każdym systemie POSIX w obsłudze. Może sprawdzać czas z różnych źródeł zegara, a ten, którego potrzebujemy, toCLOCK_MONOTONIC
. Nie wszystkie systemy, które mająclock_gettime
wsparcieCLOCK_MONOTONIC
, więc pierwszą rzeczą, którą musisz zrobić, jest sprawdzenie jego dostępności:_POSIX_MONOTONIC_CLOCK
jest zdefiniowany do wartości,>= 0
to znaczy, żeCLOCK_MONOTONIC
jest dostępny;jeśli
_POSIX_MONOTONIC_CLOCK
jest zdefiniowany0
to znaczy że należy dodatkowo sprawdzić czy działa w runtime, proponuję użyćsysconf
:Użycie
clock_gettime
jest dość proste:pobierz wartość czasu:
Skróciłem czas do mikrosekund.
obliczyć różnicę w stosunku do poprzedniej wartości czasu otrzymanej w ten sam sposób:
Najlepszą strategią rezerwową jest użycie
gettimeofday
wywołania: nie jest on monotoniczny, ale zapewnia całkiem niezłą rozdzielczość. Pomysł jest taki sam jak w przypadkuclock_gettime
, ale aby uzyskać wartość czasu, należy:Ponownie wartość czasu jest zmniejszana do mikrosekund.
SGI IRIX
IRIX ma
clock_gettime
wezwanie, ale brakujeCLOCK_MONOTONIC
. Zamiast tego ma własne monotoniczne źródło zegara zdefiniowane jako,CLOCK_SGI_CYCLE
którego należy używać zamiastCLOCK_MONOTONIC
withclock_gettime
.Solaris i HP-UX
Solaris ma własny interfejs timera o wysokiej rozdzielczości,
gethrtime
który zwraca bieżącą wartość timera w nanosekundach. Chociaż nowsze wersje Solaris mogą miećclock_gettime
, możesz trzymać się,gethrtime
jeśli potrzebujesz obsługiwać stare wersje Solaris.Użycie jest proste:
Brakuje HP-UX
clock_gettime
, ale obsługuje, zgethrtime
którego należy korzystać w taki sam sposób, jak w Solarisie.BeOS
BeOS ma również własny interfejs timera o wysokiej rozdzielczości,
system_time
który zwraca liczbę mikrosekund, które upłynęły od momentu uruchomienia komputera.Przykładowe użycie:
OS / 2
OS / 2 ma własny interfejs API do pobierania precyzyjnych znaczników czasu:
Zapytaj o częstotliwość timera (takty na jednostkę) z
DosTmrQueryFreq
(dla kompilatora GCC):zapytaj o bieżącą wartość ticków za pomocą
DosTmrQueryTime
:przeskaluj znaczniki do upływającego czasu, tj. do mikrosekund:
Przykładowa realizacja
Możesz rzucić okiem na bibliotekę plibsys, która implementuje wszystkie opisane powyżej strategie (szczegóły w ptimeprofiler * .c).
źródło
timespec_get
: stackoverflow.com/a/36095407/895245timespec_get
nie jest monotonna.timespec_get
z C11Zwraca do nanosekund, w zaokrągleniu do rozdzielczości implementacji.
Wygląda na zdzierstwo ANSI z POSIX
clock_gettime
.Przykład: a
printf
jest wykonywane co 100 ms na Ubuntu 15.10:Wersja standardowa C11 N1570 7.27.2.5 "Funkcja timespec_get mówi":
C ++ 11 ma również
std::chrono::high_resolution_clock
: C ++ Cross-Platform High-Resolution Timerimplementacja glibc 2.21
Można znaleźć
sysdeps/posix/timespec_get.c
jako:tak wyraźnie:
tylko
TIME_UTC
jest obecnie obsługiwanyprzekazuje do
__clock_gettime (CLOCK_REALTIME, ts)
, który jest interfejsem API POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.htmlLinux x86-64 ma
clock_gettime
wywołanie systemowe.Należy pamiętać, że nie jest to niezawodna metoda mikro-benchmarkingu, ponieważ:
man clock_gettime
mówi, że miara ta może mieć nieciągłości, jeśli zmienisz ustawienia czasu systemowego podczas działania programu. Powinno to być oczywiście rzadkie zdarzenie i możesz je zignorować.mierzy czas na ścianie, więc jeśli planista zdecyduje się zapomnieć o zadaniu, będzie ono działać dłużej.
Z tych powodów
getrusage()
może być lepszym lepszym narzędziem do testów porównawczych POSIX, pomimo mniejszej maksymalnej precyzji w mikrosekundach.Więcej informacji: Mierz czas w Linuksie - czas vs zegar vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?
źródło
Najlepsza precyzja, jaką można uzyskać, to użycie instrukcji "rdtsc" tylko dla x86, która może zapewnić rozdzielczość na poziomie zegara (nie musi oczywiście uwzględniać kosztu samego wywołania rdtsc, który można łatwo zmierzyć uruchomienie aplikacji).
Głównym haczykiem jest tutaj pomiar liczby zegarów na sekundę, co nie powinno być zbyt trudne.
źródło
Przyjęta odpowiedź jest wystarczająca, ale moje rozwiązanie jest prostsze: po prostu testuję w Linuksie, używam gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.
Poza tym
gettimeofday
,tv_sec
jest częścią sekundy, atv_usec
jest to mikrosekundy , a nie milisekundy .To drukuje:
1522139691342 1522139692342
, dokładnie sekundę.źródło
Pod oknami:
źródło