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;
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