Wiem że to dziwne pytanie, ale dla mnie jest istotne. ;)
Mam sobie klasę zegara, który zapewnia stały framerate — może obsługiwać dowolny, choć w projektach używam 60fps oraz 50fps. Działa on w ten sposób, że czas wykonania danej klatki oraz czas do odczekania do kolejnej klatki mierzy z dużą dokładnością (do ~mikrosekundy). Korzystam z timerów multimedialnych, więc Sleep
może wstrzymać działanie wątku najmniej o 1ms
. Jednak czasem wstrzymuje o milisekundę dłużej niż się mu każe i framerate spada o kilka klatek (z reguły 2-3).
Aby temu zapobiec, wykorzystuję funkcję Sleep
do odczekania zawsze o jedną milisekundę mniej, a resztę czasu zjadam busy waitingiem (poniżej milisekundy). Dzięki temu framerate jest super-stabilny, elegancko utrzymuje pożądaną wartość, a niewykorzystaną moc obliczeniową oddaję innym procesom. Obecnie kod oczekujący na kolejną klatkę wygląda tak:
procedure TClock.UpdateFrameAlign();
begin
while GetCounterValue() < FFrameTicksNext - FTicksPerFrame do
Sleep(1);
while GetCounterValue() < FFrameTicksNext do
;
end;
W pierwszej pętli sprawdzany jest stan sprzętowego licznika (całość bazuje na wartościach pobranych lub obliczonych na podstawie QueryPerformanceCounter
) i jeśli można odczekać milisekundę to jest odczekiwana. I tak w kółko, aż czasu do odczekania zostanie mniej niż milisekunda — wtedy wykonywana jest druga pętla, czyli zwykły busy waiting. No i on mnie zastanawia.
Czy współczesne procesory będą się wkurzać na sukcesywne odczytywanie licznika sprzętowego i nie robienie niczego? Czy może lepiej zająć go jakimiś nic nie znaczącymi instrukcjami, np. nop
-ami? Czy to ma w ogóle jakieś znaczenie?