Avatar billede hclarsen Nybegynder
06. april 2004 - 09:04 Der er 6 kommentarer og
1 løsning

Forklaring af kode

Kære Eksperter;

Jeg har fundet denne kode på nettet, som viser ens CPU clock frekvens. Jeg ville dog bare spørge, hvor den "finder" clock frekvensen fra ?

function GetCPUSpeed: Double;
const TimeOfDelay = 500;

var
TimerHigh, TimerLow: DWord;
begin
  SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
  SetThreadPriority(GetCurrentThread,
    THREAD_PRIORITY_TIME_CRITICAL);
    asm
        dw 310Fh
        mov TimerLow, eax
        mov TimerHigh, edx
      end;
      Sleep(TimeOfDelay);
    asm
        dw 310Fh
        sub eax, TimerLow
        sub edx, TimerHigh
        mov TimerLow, eax
        mov TimerHigh, edx
      end;
    Result := TimerLow / (1000.0 * TimeOfDelay);
    Showmessage('Computerens CPU er på ' + floattostr(result) + ' MHz');
  end;
Avatar billede gonzo123 Nybegynder
06. april 2004 - 09:13 #1
Det gøre den i asembler kode der stå imellem asm og end, men det er forlænge siden at jeg har lavet assembler programmering til at kommen med en pracis forklaring på koden
Avatar billede borrisholt Novice
06. april 2004 - 10:38 #2
JEg må formode det er Assembler delen der volder problemer ?

i Delphi kan man lave inline assembler, hvilket betyder man kan lave en del af din Delphi kode i Assembler. Grunden til at man måtte ønske dette, kan være at man fx ønsker extrem performance, eller ønsker direkte adgang til hardwaren CPU'en fx.

I Pentium CPU'en og frem (Cyrix undtaget) efter er der et 64 bits register der indeholder antallet af clock cykler der er exekveret i CPU'en siden den blev tænt. Det bliver ganske hurtigt et voldsomt tal.

Inline assembler i Delphi kan kun håndtere 386 instruktioner. Det giver et lille problem fordi man skal bruge en instruktion fra Pentium Assembler til at tilgå dette register. In struktionen hedder RDTSC. FOr at kalde denne snyder man Delphis compiler ved oplat at kalde den fra sin opcode : $0F31. Derfor skriver man dw 0f31h. h for Hex.

RDTSC retunerer dig dette tal nede fra registere, hvor den ligger den høje del i edx, og resten i ecx. De værdier bliver så kopierte over i TimerLow og TimerHi (mov TimerLow, eax). mov for MOVE !

Resten er simpel matematik, omkring beregning af hastighed ud fra aftsand og tid. man holder en pause    Sleep(TimeOfDelay);, og spørger efter tallet igen. så skal lotrdet blot trækkes fra hinanden :

sub eax, TimerLow og sub edx, TimerHigh


Så nu har du afstanden, målt i clock cykler, og du har tiden i ms : TimeOfDelay  så er det blot at beregne .


Jeg vil gerne benytte lejligheden til at pointere en faktuel fej i koden :

sub edx, TimerHigh BURDE være sbb edx, TimerHigh, fordi sub i modsætning til sbb ikke tager højde for en eventuel mente, når man trækker fra ! Og den tester slet ikke om man overhoved kan kalde den funktion, hvliket ikke kan lade sig gøre på ældere CPU'er samt Cyris processorer !

Så brug den her i stedet for :

Function GetCpuSpeed : Extended;

Function IsCPUID_Available : Boolean;assembler;register;
  asm
    PUSHFD               {direct access to flags no possible, only via stack}
    POP    EAX              {flags to EAX}
    MOV    EDX,EAX    {save current flags}
    XOR    EAX,$200000    {not ID bit}
    PUSH    EAX        {onto stack}
    POPFD        {from stack to flags, with not ID bit}
    PUSHFD        {back to stack}
    POP    EAX        {get back to EAX}
    XOR    EAX,EDX    {check if ID bit affected}
    JZ      @exit    {no, CPUID not availavle}
    MOV    AL,True    {Result=True}
    @exit:
  end;

const
  Delay = 500;

var
  TimerHi, TimerLo: Integer;
  PriorityClass, Priority: Integer;
begin
  Result:=0;
  if not IsCPUID_Available then Exit;
  PriorityClass := GetPriorityClass(GetCurrentProcess);
  Priority := GetThreadPriority(GetCurrentThread);

  SetPriorityClass  (GetCurrentProcess, REALTIME_PRIORITY_CLASS);
  SetThreadPriority (GetCurrentThread,  THREAD_PRIORITY_TIME_CRITICAL);

  SleepEx(10, FALSE);

  asm
    db $0F
    db $31          { $0F31 op-code for RDTSC pentiun instruction returns a 64 Bit Integer}
    mov TimerLo, eax
    mov TimerHi, edx
  end;

  SleepEx(Delay , FALSE);

  asm
    db $0F
    db $31 { $0F31 op-code for RDTSC pentiun instruction returns a 64 Bit Integer}
    sub eax, TimerLo
    sbb edx, TimerHi
    mov TimerLo, eax
    mov TimerHi, edx
  end;

  SetThreadPriority (GetCurrentThread,  Priority);
  SetPriorityClass  (GetCurrentProcess, PriorityClass);
  Result := TimerLo / (1000 * Delay);
end;


eller den her skrevet i Assembler :

Function GetCpuSpeed : Real;

  function IsCPUID_Available: Boolean; assembler; register;
  asm
    PUSHFD                   {direct access to flags not possible, only via stack}
    POP    EAX              {flags to EAX}
    MOV    EDX,EAX    {save current flags}
    XOR    EAX,$200000    {not ID bit}
    PUSH    EAX              {onto stack}
    POPFD                    {from stack to flags, with not ID bit}
    PUSHFD                  {back to stack}
    POP    EAX              {get back to EAX}
    XOR    EAX,EDX        {check if ID bit affected}
    JZ      @exit          {no, CPUID not availavle}
    MOV    AL,True        {Result=True}
    @exit:
  end;

const
  Delay = 500;

var
  TimerHi, TimerLo: Integer;
  PriorityClass, Priority: Integer;
begin
  Result := 0;
  if not IsCPUID_Available then
    exit;

  asm
    call GetCurrentProcess
    push eax
    call GetPriorityClass
    mov PriorityClass, eax //Priority := GetThreadPriority(GetCurrentThread);

    call GetCurrentThread
    push eax
    call GetThreadPriority
    mov Priority, eax //Priority := GetThreadPriority(GetCurrentThread);

    push REALTIME_PRIORITY_CLASS
    call GetCurrentProcess
    push eax
    call SetPriorityClass //SetPriorityClass  (GetCurrentProcess, REALTIME_PRIORITY_CLASS);

    push THREAD_PRIORITY_TIME_CRITICAL
    call GetCurrentThread
    push eax
    Call SetThreadPriority //SetThreadPriority (GetCurrentThread,  THREAD_PRIORITY_TIME_CRITICAL);

    push false
    push $A
    Call SleepEx //SleepEx(10 , FALSE);

    db $0F
    db $31          { $0F31 op-code for RDTSC pentiun instruction returns a 64 Bit Integer}
    mov TimerLo, eax
    mov TimerHi, edx

    push false
    push Delay
    Call SleepEx //SleepEx(Delay , FALSE);

    db $0F
    db $31 { $0F31 opcode for RDTSC pentiun instruction returns a 64 Bit Integer}
    sub eax, TimerLo
    sbb edx, TimerHi
    mov TimerLo, eax
    mov TimerHi, edx

    mov eax,  Priority
    push eax
    call GetCurrentThread
    push eax
    call SetThreadPriority //SetThreadPriority (GetCurrentThread,  Priority);

    mov eax,  PriorityClass
    push eax
    call GetCurrentThread
    push eax
    call SetPriorityClass //SetPriorityClass  (GetCurrentProcess, PriorityClass);
  end;

  Result := TimerLo / (1000 * Delay);
end;


Jens B
Avatar billede hclarsen Nybegynder
06. april 2004 - 11:47 #3
Det var da noget af et svar! :)

Tak for det !
Avatar billede borrisholt Novice
06. april 2004 - 12:07 #4
JEg fik aldrig lagt et SVAR !!!

Forstod du det eller hvad ?

Jens B
Avatar billede hclarsen Nybegynder
06. april 2004 - 12:21 #5
Ja, men vil det sige, at det man finder i virkeligheden er clock cykler/ms ?
Avatar billede borrisholt Novice
06. april 2004 - 12:44 #6
Ja ligebestemt .. Det er også det man måler CPU hastighed i !

Hz = Svingninger pr. sekund
MHz = Millioner  (10^6) Svingninger per sekund
GHz = Milliarder (10^9) Svingninger per sekund
etc.

Og du fik dit eksempel rettet ? således du også buger sbb til sidst ?

Jens B
Avatar billede hclarsen Nybegynder
06. april 2004 - 12:49 #7
Helt sikkert, nu forstår jeg det!

Yes, eksemplet er rettet og jeg bruger nu sbb.

HcLarsen
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester