Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   CPU Speed Assembler & Delphi - Was passiert da? (https://www.delphipraxis.net/136190-cpu-speed-assembler-delphi-passiert-da.html)

psd-sign 25. Jun 2009 01:02


CPU Speed Assembler & Delphi - Was passiert da?
 
Hiermit ermittle ich die CPU Geschwindigkeit. Eine nicht ganz unbekannte Assembler Routine in DElphi eingebunden .... ich gebe aber ehrlich zu: Ich verstehe den CODE nich zu 100%!!

Delphi-Quellcode:
function CPU_SPEED_FLOAT: Extended;
const
  DelayTime = 500; // measure time in ms
var
  TimerHi, TimerLo: DWord;
  PriorityClass, Priority: Integer;
begin
  try
    PriorityClass := GetPriorityClass(GetCurrentProcess);
    Priority := GetThreadPriority(GetCurrentThread);

    SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
    SetThreadPriority(GetCurrentThread,
                      THREAD_PRIORITY_TIME_CRITICAL);
    try
      Sleep(10);
      asm
        dw 310Fh // rdtsc
        mov TimerLo, eax
        mov TimerHi, edx
      end;
      Sleep(DelayTime);
      asm
        dw 310Fh // rdtsc
        sub eax, TimerLo
        sbb edx, TimerHi
        mov TimerLo, eax
        mov TimerHi, edx
      end;
    finally
      SetThreadPriority(GetCurrentThread, Priority);
      SetPriorityClass(GetCurrentProcess, PriorityClass);
    end;
    Result := TimerLo / (1000.0 * DelayTime);
  except
    Result := 0;
  end;
end;

Warum setze bzw. operiere ich mit "TimerHi"? ... Kann jemand mir den CODE auskommentieren bzw. erklären, was ich mit den Registern wirklich mache und warum ich "TimerHi" in der Ausgabe nicht nutze? Wenn die Tutorial und Programmier-Cracks wie Luckie & Co einen Augenblick Zeit haben, würde ich mich freuen ... mag es nicht, Code zu verwenden, den ich nicht zu 100% verstehe.

Fridolin Walther 25. Jun 2009 01:55

Re: CPU Speed Assembler & Delphi - Was passiert da?
 
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.

psd-sign 25. Jun 2009 02:14

Re: CPU Speed Assembler & Delphi - Was passiert da?
 
Mit anderen Worten, müsste ich das Result so ausgeben??
Delphi-Quellcode:
Result := (TimerLo + TimerHi / (1000.0 * DelayTime);

Fridolin Walther 25. Jun 2009 02:18

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

Zitat von psd-sign
Mit anderen Worten, müsste ich das Result so ausgeben??
Delphi-Quellcode:
Result := (TimerLo + TimerHi / (1000.0 * DelayTime);

Nein ... Du müsstest einen ULARGE_INTEGER definieren (einen 64bit Typ) und den HighPart und LowPart entsprechend setzen.

Hier mal eine fehlerbereinigte Version:
Delphi-Quellcode:
function GetProcessorSpeed(TimeToWait : Cardinal) : Cardinal;
var
  TickCountFirst, TickCountSecond : Cardinal;
  CycleCountFirst, CycleCountSecond : ULARGE_INTEGER;
begin
  TickCountFirst := GetTickCount;

  // Erster Meßpunkt
  asm
    dw 310Fh // rdtsc
    mov CycleCountFirst.LowPart, eax
    mov CycleCountFirst.HighPart, edx
  end;

  sleep(TimeToWait);
  TickCountSecond := GetTickCount;

  // Zweiter Meßpunkt
  asm
    dw 310Fh // rdtsc
    mov CycleCountSecond.LowPart, eax
    mov CycleCountSecond.HighPart, edx
  end;

  result := (CycleCountSecond.QuadPart - CycleCountFirst.QuadPart) div ((TickCountSecond - TickCountFirst) * 1000);
end;
Die Funktion empfiehlt sich übrigens nicht zum messen. Stromsparfunktionen etc. verfälschen das Ergebnis. Wenn Du die wirkliche Frequenz haben willst, solltest Du WMI bemühen ;).

psd-sign 25. Jun 2009 02:20

Re: CPU Speed Assembler & Delphi - Was passiert da?
 
W M I ... wieder die böse Abkürzung ... :wall: .

Dank Dir für die Antwort zu so später Stunde :-D

Fridolin Walther 25. Jun 2009 02:29

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

Zitat von psd-sign
W M I ... wieder die böse Abkürzung ... :wall: .

So schlim ist WMI gar nicht. Kannst ja mal Suchen hier im Board. Jaenicke hatte da eine recht hübsche Funktion geposted glaube ich ;).

Zitat:

Zitat von psd-sign
Dank Dir für die Antwort zu so später Stunde :-D

Wieso spät? Früh!

himitsu 25. Jun 2009 08:25

Re: CPU Speed Assembler & Delphi - Was passiert da?
 
wozu WMI ... WMI ließt auch nur dieses hier aus:
http://www.delphipraxis.net/internal...=488470#488470

dann ist Sleep nicht grade genau, drum lesen viele zusätzlich noch mit den QueryPerformanceCounter die tatsächliche Pause-Zeit aus
http://www.delphipraxis.net/internal...ighlight=rdtsc

außerdem zeigt dein Code bei mehr als einer CPU eventuell was Falsches an:
http://www.delphipraxis.net/internal...ighlight=rdtsc

PS:
http://www.delphipraxis.net/internal...ighlight=rdtsc
ja, ich nutze immernoch Cool&Quite :angel2:

PSS: auch wenn's nichts Schlimmes ist, warum geben fast alle immernoch RDTSC als Bytecode an?
der Delphicompiler kennt diesen ASM-Befehl schon seit seeeeeeeehr vielen Jahren :nerd:

Fridolin Walther 25. Jun 2009 10:21

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

Zitat von himitsu
PSS: auch wenn's nichts Schlimmes ist, warum geben fast alle immernoch RDTSC als Bytecode an?
der Delphicompiler kennt diesen ASM-Befehl schon seit seeeeeeeehr vielen Jahren :nerd:

Steht seine Delphi Version dabei? I don't think so ;). Es gibt Delphi Versionen, die den Bytecode nicht kennen ;).


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:41 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz