Ich bin jetzt kein Programmiercrack, wie Luckie und Co, aber ich antworte trotzdem mal. Der Luckie is nämlich schon müde
.
Bevor es los geht:
Wie ist Hertz definiert? Nun,
Wikipedia sagt uns: Hertz gibt die Schwingungen (bei Prozessoren spricht man von Zyklen) pro Sekunde an. Ein Megahertz sind entsprechend eine Million Zyklen pro Sekunde und ein Gigahertz sind eine Milliarde Zyklen pro Sekunde.
Außerdem einen kurzen Exkurs zum RDTSC (ReaD TSC) Befehl. Der Befehl wurde mit der Pentium Prozessorklasse eingeführt. Prinzipiel kann man mit ihm den Time Stamp Counter auslesen. Der Time Stamp Counter ist dabei ein 64bit großer Zähler, der mit jedem Zyklus um eins inkrementiert wird. Ich denke Du wirst jetzt schon erahnen worauf der Code hinaus läuft ...
Delphi-Quellcode:
function CPU_SPEED_FLOAT: Extended;
const
DelayTime = 500;
// measure time in ms
var
TimerHi, TimerLo: DWord;
PriorityClass, Priority: Integer;
begin
try
// Zuerst einmal sichern wir die alten Prioritäten für unseren Prozess bzw. Thread, damit wir unsere Änderungen rückgängig machen können ...
PriorityClass := GetPriorityClass(GetCurrentProcess);
Priority := GetThreadPriority(GetCurrentThread);
// ... und dann sagen wir Windows das wir derart wichtig sind, daß wir möglichst vor allen anderen Threads und Anwendungen ausgeführt werden wollen.
SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread,
THREAD_PRIORITY_TIME_CRITICAL);
try
Sleep(10);
// Wir holen uns als erstes den aktuellen Zykluszähler. Da der Zähler ein 64bit Wert ist, wir aber nur 32bit Register haben, muss der Zähler
// auf 2 Register verteilt werden. In EAX landen die Low Order Bits und in EDX die High Order Bits.
asm
dw 310Fh
// rdtsc
// Da wir die später nochmal brauchen werden, müssen die aber noch gesichert werden: Die Low Bits in TimerLo und die High Bits in TimerHi.
mov TimerLo, eax
mov TimerHi, edx
end;
// Und dann warten wir einen definierten Zeitraum ...
Sleep(DelayTime);
// Um danach den Zähler nochmal auszulesen ...
asm
dw 310Fh
// rdtsc
// ... und die Differenz aus den neuen Werten und den gespeicherten zu berechnen ...
sub eax, TimerLo
sbb edx, TimerHi
// ... die wir dann auch direkt in unsere Timer* Variablen packen.
mov TimerLo, eax
mov TimerHi, edx
end;
finally
// Da wir nicht ewig mit Echtzeit Priorität laufen dürfen, weil der User sonst böse Mails schreibt, werden die Prioritäten wieder zurückgesetzt.
SetThreadPriority(GetCurrentThread, Priority);
SetPriorityClass(GetCurrentProcess, PriorityClass);
end;
// Hier gibts einen Bug. Zur Berechnung wird nur TimerLo herangezogen, was falsch ist. Man müsste auf TimerHi und TimerLo wieder einen
// 64bit Wert bauen um mit dem zu rechnen. Ab einer bestimmten Geschwindigkeit wird der Code also nicht mehr korrekt funktionieren.
// Aber egal ... da Hertz die Anzahl der Zyklen in Sekunden angibt und wir wissen, wieviele Zyklen im von uns gestoppten Zeitraum
// durchlaufen wurden, rechnen wir das ganze um (in Megahertz übrigens ;)).
Result := TimerLo / (1000.0 * DelayTime);
except
Result := 0;
end;
end;
Und ja ... da wars. Bei weiteren Fragen, einfach stellen.