Przenoszę grę, która została pierwotnie napisana dla Win32 API, na Linuksa (cóż, przenoszę port OS X portu Win32 na Linuksa).
Mam realizowane QueryPerformanceCounter
przez podanie uSeconds ponieważ proces uruchamiania:
BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
gettimeofday(¤tTimeVal, NULL);
performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
performanceCount->QuadPart *= (1000 * 1000);
performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);
return true;
}
To, w połączeniu z QueryPerformanceFrequency()
podaniem stałej 1000000 jako częstotliwości, działa dobrze na moim komputerze , dając mi 64-bitową zmienną, która zawiera się uSeconds
od momentu uruchomienia programu.
Czy to jest przenośne? Nie chcę odkryć, że działa inaczej, jeśli jądro zostało skompilowane w określony sposób lub coś w tym rodzaju. Jednak nie przeszkadza mi to, że jest nieprzenośny na coś innego niż Linux.
Wysoka rozdzielczość, niskie taktowanie w przypadku procesorów Intel
Jeśli korzystasz ze sprzętu firmy Intel, oto jak odczytać licznik instrukcji procesora w czasie rzeczywistym. Podaje liczbę cykli procesora wykonanych od momentu uruchomienia procesora. Jest to prawdopodobnie najdrobniejszy licznik, jaki można uzyskać do pomiaru wydajności.
Zauważ, że jest to liczba cykli procesora. W Linuksie możesz pobrać prędkość procesora z / proc / cpuinfo i podzielić, aby uzyskać liczbę sekund. Konwersja tego na podwójną jest całkiem przydatna.
Kiedy uruchomię to na moim pudełku, dostaję
Oto przewodnik dla programistów firmy Intel, który zawiera mnóstwo szczegółów.
źródło
CPUID
ponownie użyty po pierwszejRDTSC
instrukcji i przed wykonaniem kodu, który jest testowany? W przeciwnym razie, co zatrzyma wykonanie kodu porównawczego przed / równolegle z pierwszymRDTSC
, aw konsekwencji niedostateczną reprezentację wRDTSC
delcie?@Bernard:
To dobre pytanie ... Myślę, że kod jest w porządku. Z praktycznego punktu widzenia używamy go w mojej firmie każdego dnia i pracujemy na dość szerokiej gamie pudełek, wszystko od 2 do 8 rdzeni. Oczywiście YMMV itp., Ale wydaje się, że jest to niezawodna i niewielka (ponieważ nie powoduje przełączenia kontekstu na przestrzeń systemową) metoda pomiaru czasu.
Ogólnie, jak to działa:
Szczegółowe uwagi:
wykonanie poza kolejnością może powodować niepoprawne wyniki, dlatego wykonujemy instrukcję "cpuid", która oprócz podania pewnych informacji o procesorze synchronizuje również wykonanie instrukcji poza kolejnością.
Większość systemów operacyjnych synchronizuje liczniki na procesorach podczas uruchamiania, więc odpowiedź jest dobra w ciągu kilku nanosekund.
Komentarz dotyczący hibernacji jest prawdopodobnie prawdziwy, ale w praktyce prawdopodobnie nie przejmujesz się taktowaniem poza granicami hibernacji.
w odniesieniu do szybkości: nowsze procesory Intel kompensują zmiany prędkości i zwracają skorygowaną liczbę. Zrobiłem szybkie skanowanie niektórych pudełek w naszej sieci i znalazłem tylko jedno pudełko, w którym go nie było: Pentium 3 ze starym serwerem bazy danych. (to są skrzynki linuxowe, więc sprawdziłem: grep constant_tsc / proc / cpuinfo)
Nie jestem pewien co do procesorów AMD, jesteśmy przede wszystkim sklepem Intela, chociaż wiem, że niektórzy z naszych guru od systemów niskiego poziomu przeprowadzili ocenę AMD.
Mam nadzieję, że to zaspokoi Twoją ciekawość, jest to interesujący i (IMHO) niedostatecznie zbadany obszar programowania. Wiesz, kiedy Jeff i Joel rozmawiali o tym, czy programista powinien znać C? Krzyczałem na nich, „hej, zapomnijcie o tych wysokopoziomowych rzeczach C ... asembler jest tym, czego powinniście się nauczyć, jeśli chcecie wiedzieć, co robi komputer!”.
źródło
Możesz być zainteresowany FAQ systemu Linux dla
clock_gettime(CLOCK_REALTIME)
źródło
Wine używa gettimeofday () do implementacji QueryPerformanceCounter () i jest znane z tego, że wiele gier Windows działa na Linuksie i Macu.
Rozpoczyna http://source.winehq.org/source/dlls/kernel32/cpu.c#L312
prowadzi do http://source.winehq.org/source/dlls/ntdll/time.c#L448
źródło
Struktura danych jest zdefiniowana jako jednostka miary w mikrosekundach, ale to nie znaczy, że zegar lub system operacyjny są w stanie dokładnie to zmierzyć.
Jak sugerowali inni,
gettimeofday()
jest złe, ponieważ ustawienie czasu może spowodować przesunięcie zegara i zepsuć obliczenia.clock_gettime(CLOCK_MONOTONIC)
jest tym, czego chcesz, iclock_getres()
powie ci precyzję twojego zegara.źródło
Otrzymałem tę odpowiedź z Pomiaru czasu i timerów o wysokiej rozdzielczości, część I.
źródło
Ta odpowiedź wspomina o problemach z regulacją zegara. Zarówno problemy z gwarantowaniem jednostek ticka, jak i problemy z dostosowywaniem czasu są rozwiązywane w C ++ 11 za pomocą rozszerzenia
<chrono>
biblioteką.std::chrono::steady_clock
Gwarantuje się, że zegar nie będzie regulowany, a ponadto będzie postępował ze stałą szybkością w stosunku do czasu rzeczywistego, więc technologie takie jak SpeedStep nie mogą na to wpływać.Możesz zdobyć jednostki bezpieczne, przechodząc na jedną ze
std::chrono::duration
specjalizacji, na przykładstd::chrono::microseconds
. W przypadku tego typu nie ma dwuznaczności co do jednostek używanych przez wartość ticka. Należy jednak pamiętać, że zegar niekoniecznie ma taką rozdzielczość. Możesz przekonwertować czas trwania na attosekundy bez posiadania tak dokładnego zegara.źródło
Z mojego doświadczenia iz tego, co przeczytałem w internecie, odpowiedź brzmi „Nie”, nie jest to gwarantowane. Zależy to od szybkości procesora, systemu operacyjnego, rodzaju Linuksa itp.
źródło
Odczyt RDTSC nie jest wiarygodny w systemach SMP, ponieważ każdy procesor utrzymuje swój własny licznik i nie ma gwarancji, że każdy licznik będzie zsynchronizowany z innym procesorem.
Mogę zasugerować spróbować
clock_gettime(CLOCK_REALTIME)
. Podręcznik posix wskazuje, że powinno to być wdrożone we wszystkich zgodnych systemach. Może podać liczbę nanosekund, ale prawdopodobnie będziesz chciał sprawdzićclock_getres(CLOCK_REALTIME)
w systemie, jaka jest rzeczywista rozdzielczość.źródło
clock_getres(CLOCK_REALTIME)
nie da prawdziwej rozdzielczości. Zawsze zwraca "1 ns" (jedną nanosekundę), gdy dostępne są hrtimery, sprawdźinclude/linux/hrtimer.h
plikdefine HIGH_RES_NSEC 1
(więcej na stackoverflow.com/a/23044075/196561 )