Einzelnen Beitrag anzeigen

Fridolin Walther

Registriert seit: 11. Mai 2008
Ort: Kühlungsborn
446 Beiträge
 
Delphi 2009 Professional
 
#2

Re: CPU Speed Assembler & Delphi - Was passiert da?

  Alt 25. Jun 2009, 01:55
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.
Fridolin Walther
"While Mr. Kim, by virtue of youth and naiveté, has fallen prey to the inexplicable need for human contact, let me step in and assure you that my research will go on uninterrupted, and that social relationships will continue to baffle and repulse me."
  Mit Zitat antworten Zitat