Autor Beitrag
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Di 05.03.13 11:46 
Moin!

mal wieder ein lustiges Problem ;)

Unsere Engine benötigt an verschiedenen Stellen die aktuelle Systemzeit mit möglichst hoher Genauigkeit, mindestens 2 Größenordnungen besser als GetTickCount; üblicher Messwert bei mir sind ca 100-150µs.
Unter POSIX hätte man dafür den Syscall gettimeofday() mit einer Auflösung von 1µs.

Was nimmt man da unter Windows? Wir haben das mit QueryPerformanceCounter simuliert, aber das hat eine Reihe von Problemen. Bekannt ist ja, dass QPC unter 2 Bedingungen kompletten Blödsinn ausgibt: wenn die CPU wechselt (Multiprocessor) und wenn die Taktrate (CnQ etc) wechselt. Zusätzlich ist es auch noch sehr langsam, je nach Implementation sind da Locks oder sogar CPU-HLT beteiligt. Das führt soweit, dass ich auf einem Rechner (AMD-K8 ) einen Kern 100% auslaste und davon über 90% Kerneltime sind, die in QPC verbraten werden (bestimmt mit CodeAnalyst).

Laut Wikipedia hat Windows da eigentlich nur GetSystemTimeAsFileTime() mit einer Res von 100 ns zu bieten, ansonsten kommt man nicht unter 1ms. Kann man das sinnvoll nutzen?

Systemanforderung ist XP aufwärts; die Absolutzeit ist praktisch egal, es muss aber linear und am Besten auch noch streng monoton sein.

Viele Grüße,
Martok

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Tranx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 648
Erhaltene Danke: 85

WIN 2000, WIN XP
D5 Prof
BeitragVerfasst: Di 05.03.13 12:52 
Ich habe mal die Funktion GetSystemtimeAsFiletime geprüft. Wenn man bloß einen Befehl dazwischen hat, zwischen zwei Aufrufen der Funktion, dann sind das gleich eine Differenz 156000 oder 0. Ich weiß nicht, ob diese Routine wirklich geeignet ist.

_________________
Toleranz ist eine Grundvoraussetzung für das Leben.

Für diesen Beitrag haben gedankt: Martok
Gerd Kayser
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 632
Erhaltene Danke: 121

Win 7 32-bit
Delphi 2006/XE
BeitragVerfasst: Di 05.03.13 13:17 
Ab Windows 8 soll es die Funktion GetSystemTimePreciseAsFileTime geben. Quelle: social.msdn.microsof...e3-9c58-343004243c1b

Ansonsten würde ich es mal mit TimeGetTime (uses MMSystem) versuchen. Liefert genauere Ergebnisse als GetTickCount. Allerdings beträgt die Auflösung auch nur Millisekunden.
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1321
Erhaltene Danke: 117

Win 10
RIO, CE, Lazarus
BeitragVerfasst: Di 05.03.13 23:30 
Ich verwende auch MMSystem. Die Funktion heißt "timeSetEvent" mit der man eine Procedure registriert die regelmäßig aufgerufen wird. Ich zähle in dieser dann einen Integer hoch. Sehe aber gerade das die Funktion als obsolete markiert ist und auch nur Millisekunden kann. Man soll jetzt CreateTimerQueueTimer verwenden. Lößt allerdings auch nur Millisekunden auf. Was ja irgendwie ein bisschen mickrig ist bei heutigen CPU Leistungen.
Zu DOS Zeiten gab es mal einen Interrupt der schneller feuern konnte. Irgendwo im Rechner gab es mal einen schnellen Taktgeber. Aber gibt es den noch? Kommt man da noch einfach ran?

_________________
Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: So 31.03.13 04:52 
Zitat:
Habe nun, ach! [...] leider auch Win32-APIs [1]
Durchaus studiert, mit heißem Bemühn.
Da steh ich nun, ich armer Tor!
Und bin so klug als wie zuvor;

Nunja, nicht ganz.

Nummer 1: eine einfach aufsteigende Zeitquelle neben QPC gibt es nicht. Alle Zeitgeber versuchen sich mehr oder weniger erfolgreich an Absolutzeiten [2].
Nummer 2: eine bestimmte Zeitspanne abwarten ist ein viel häufigeres Problem - und wird ganz zurecht damit abgebügelt dass das auf einem nicht-realtime-Kernel nicht geht.

Nummer 3: die in #1 erwähnten Geber sind granular.

Versuchsaufbau: Abwarten einer gewissen Zeit und Aufschreiben der Messwertdifferenz.
Erwarten würden wir sowas in der Art:
qpc
Abb. 1: QueryPerformanceCounter() Rot: Messwertdifferenz(us) über Sleep-Zeit(ms), Blau: Erwartungswert us=1000ms

Jeder Messwert ist leicht über dem Erwartungswert. Passt so, da Sleep immer kurz nach der eingestellten Zeit zurückkehrt. Bei der kurzen Zeit ist das wirklich so messbar.

Gleiches Experiment mit SysUtils.Now, welches Kernel32.GetLocalTime aufruft. Das fragt den gleichen Timer ab wie GetSystemTimeAsFileTime (kann man sich fast aus dem Namen denken), und dieser wird wie auch von user profile iconTranx festgestellt nur alle 15.625ms (1/64 Sekunde) [3] aktualisiert.
now1
Abb. 2: Now() Rot: Messwertdifferenz(us) über Sleep-Zeit(ms), Grün: Erwartungswert us=1000ms


Also, ab zu MMSystem's timeGetTime: diese liest einen Timer, der je nach Systemeinstellung einen anderen Teiler haben kann als 64, bis runter auf 1/1000s.

Spannenderweise ist das auf meinem System schon fast auf die minimale Auflösung von 1ms eingestellt, wie an Abb 3 und 4 zu erkennen ist:
tgt
Abb. 3: timeGetTime, Standardeinstellungen Rot: Messwertdifferenz(ms) über Sleep-Zeit(ms), Grün: Erwartungswert
Abb. 4: timeGetTime, 1ms angefordert Rot: Messwertdifferenz(ms) über Sleep-Zeit(ms), Grün: Erwartungswert us=ms

Auffällig an Abb.4 ist auch, dass die 1ms wirklich erreicht wird - die "Sprünge", die man dort erkennt sind wirklich 1ms-Stufen, die zu Rauschen in dieser Größe passen. In Abb.3 ist das alles etwas verstreuter, die Statistik dazu überlass ich mal dem Leser als Hausaufgabe. Die Rohdaten sind auf Anfrage verfügbar :zwinker:

Die zwei Alternativen sind also: QPC und hohe CPU-Last in Kauf nehmen oder timeGetTime und etwas Genauigkeit aufgeben. 1ms ist ja auch ganz nett.

So, und jetzt les ich [3] erstmal komplett...

Referenzen:
[1] stackoverflow.com/qu...imestamps-on-windows
[2] www.lochan.org/2005/...seful/win32time.html
[3] www.windowstimestamp.com/description#C_2
Einloggen, um Attachments anzusehen!
_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."

Für diesen Beitrag haben gedankt: BenBE, Marc., Narses, Teekeks