Page 2 of 2

Posted: Thu Sep 25, 2003 11:50 am
by freak
The problem is, that all values here are 64bit values, but the
calculations are only done with the lowpart of them. So the negative
values are simply the result of too big numbers.

I made a version of the code that calculates everything in 64bit.

Unfortunalety, on about every 2nd call, it returns a wrong value.
(always the same wrong value.)

Do you guys get the same? And does anybody have an idea why?

Here comes the code:

Code: Select all

Structure bit64
  LowPart.l
  HighPart.l
EndStructure

Procedure CPUSpeed()
  DefType.bit64 ulValue, ulTicks, ulFreq, ulStart
  QueryPerformanceFrequency_(ulFreq)
  QueryPerformanceCounter_(ulTicks) 
  
  ; ulValue = ulTicks
  ulValue\LowPart = ulTicks\LowPart
  ulValue\HighPart = ulTicks\HighPart
  
  ; ulValue + ulFreq
  MOV eax, ulFreq\LowPart
  MOV edx, ulFreq\HighPart
  ADD ulValue\LowPart, eax
  ADC ulValue\HighPart, edx   
  
  ; ulStart = Processor Timestamp
  !RDTSC
  MOV ulStart\LowPart, eax
  MOV ulStart\HighPart, edx
  
  ; While ulTicks <= ulValue
CPU_Loop_Start:
  MOV eax, ulTicks\HighPart
  CMP eax, ulValue\HighPart
  JG l_CPU_Loop_End
  JNE l_CPU_Loop_Run
  MOV eax, ulTicks\LowPart
  CMP eax, ulValue\LowPart
  JG l_CPU_Loop_End
CPU_Loop_Run:
  QueryPerformanceCounter_(ulTicks)  
  JMP l_CPU_Loop_Start
CPU_Loop_End:

  ; eax:edx = Processor Timestamp
  ; eax:edx - ulStart
  !RDTSC  
  SUB eax, ulStart\LowPart
  SBB edx, ulStart\HighPart
  
  ; lStart\LowPart = eax:edx / 1000000
  MOV ebx, dword 1000000
  DIV ebx
  MOV ulStart\LowPart, eax
  
  ProcedureReturn ulStart\LowPart
EndProcedure; Takes 1 second to calculate...

For i = 1 To 20
  Debug CPUSpeed()
Next i
Timo

Posted: Thu Sep 25, 2003 2:47 pm
by blueb
Timo,

Hmmm... Running your sample I get an error message:
Invalid name: Same as an external command.

I must have a command named CPUSpeed() in one of my libraries... don't know which one.

Renamed it to CPUSpeed2 and got the same as you did: (except the pattern isn't same as yours)

I get:
1707
6002
1707
1707
6002
1707
6002
1707
1707
6002
repeats....

Notice the pattern is: 1, 2, 1, 2, etc.

I commented out your procedure and tried CPUSpeed() (located in one of my libraries) and it returned:
1707
1707, etc. etc.

On another forum, I had read that 'QueryPerformanceCounter' used signed 64-bits of precision, but I'm not sure. This may account for the 1, 2 pattern.

P4 - 1.7 and Win XP Pro
blueb

Posted: Thu Sep 25, 2003 6:11 pm
by jack
for people getting negative results, please try this.

Code: Select all

; from PB forums by Hi-Toro 
; post http://jconserv.net/purebasic/viewtopic.php?t=3811&highlight=utility 

Structure bit64 
  LowPart.l 
  HighPart.l 
EndStructure 

Procedure CPUSpeed() 
  DefType.bit64 ulEAX_EDX, ulFreq, ulTicks, ulValue, ulStartCounter, ulResult 
  QueryPerformanceFrequency_(ulFreq) 
  QueryPerformanceCounter_(ulTicks) 
  ulValue\LowPart = ulTicks\LowPart + ulFreq\LowPart 
  
  ! RDTSC 
  MOV ulEAX_EDX\LowPart, eax 
  MOV ulEAX_EDX\HighPart, edx 
  
  ulStartCounter\LowPart = ulEAX_EDX\LowPart 
  While (ulTicks\LowPart <= ulValue\LowPart) 
    QueryPerformanceCounter_(ulTicks) 
  Wend 
    
  ! RDTSC 
  MOV ulEAX_EDX\LowPart, eax 
  MOV ulEAX_EDX\HighPart, edx 
  
  ulResult\LowPart = ulEAX_EDX\LowPart - ulStartCounter\LowPart 
  ProcedureReturn ulResult\LowPart / 1000000 
EndProcedure; Takes 1 second to calculate... 
mhz = CPUSpeed()&$FFFFFFFF 
MessageRequester("CPU Speed", "CPU speed: " + Str (mhz) + " MHz", #MB_ICONINFORMATION) 
notice that i only changed the line: mhz = CPUSpeed()
to mhz = CPUSpeed()&$FFFFFFFF

Posted: Thu Sep 25, 2003 7:37 pm
by freak
jack:

What should be the effect of those changes?
The number is still negative, and wrong. Even using Stru() helps nothing
here, because the numbers are 64bit, not 32bit.

blueb:

QueryPerformanceCounter_() uses ULARGE_INTEGER, which is defined
as 2 longs or one quad, so it is 64bit.
I can't explain these strange results though.

You have an idea who wrote the lib where your command is in?
Or where to get it?
I'd like to test it to see if the results are correct.

This lib here also containes such a command, and it is right in general,
but the results also are different with each call.
http://www.purearea.net/pb/download/use ... nHware.zip

Timo

Posted: Thu Sep 25, 2003 10:10 pm
by jack
freak you are absolutly right :oops:
maybe this will work:

Code: Select all

; from PB forums by Hi-Toro 
; post http://jconserv.net/purebasic/viewtopic.php?t=3811&highlight=utility 

; inline asm by jack

Structure bit64 
  LowPart.l 
  HighPart.l 
EndStructure 
Procedure CPUSpeed() 
  OneMillion.l=1000000
  DefType.bit64 ulEAX_EDX, ulFreq, ulTicks, ulValue, ulStartCounter, ulResult 
  QueryPerformanceFrequency_(ulFreq) 
  QueryPerformanceCounter_(ulTicks) 
  ! fild qword [esp+12] ;ulFreq
  ! fild qword [esp+20] ;ulTicks
  ! faddp st1,st0       ;ST0=ulFreq+ulTicks
  ! fistp qword [esp+28];ST0->ulValue
  ;ulValue\LowPart = ulTicks\LowPart + ulFreq\LowPart 
  ! RDTSC 
  ! MOV [esp+4], eax ;MOV ulEAX_EDX\LowPart, eax
  ! MOV [esp+8], edx ;MOV ulEAX_EDX\HighPart,edx
  ! fild qword [esp+4]  ;ulEAX_EDX
  ! fistp qword [esp+36];ulStartCounter
  ;ulStartCounter\LowPart = ulEAX_EDX\LowPart 
  ! fild qword [esp+28] ;ulValue
startloop:
  ! fild qword [esp+20] ;ulTicks
  ! FCOMP
  ! FNSTSW ax
  ! SAHF
  ! JAE l_endloop
  ;While (ulTicks\LowPart <= ulValue\LowPart) 
    QueryPerformanceCounter_(ulTicks) 
  ;Wend 
  Goto startloop
endloop:
  ! fstp st0    
  ! RDTSC 
  ! MOV [esp+4], eax ;MOV ulEAX_EDX\LowPart, eax
  ! MOV [esp+8], edx ;MOV ulEAX_EDX\HighPart,edx
  ! fild qword [esp+4]  ;ulEAX_EDX
  ! fild qword [esp+36] ;ulStartCounter
  ! fsubp st1,st0       ;ST0=ulEAX_EDX - ulStartCounter
  ! fild dword [esp]    ;OneMillion
  ! fdivp st1,st0       ;ST0=(ulEAX_EDX - ulStartCounter)/1000000
  ! fistp qword [esp+44];ST0->ulResult
  ;ulResult\LowPart = (ulEAX_EDX\LowPart - ulStartCounter\LowPart)/1000000 
  ProcedureReturn ulResult\LowPart-2; / 1000000 
EndProcedure; Takes 1 second to calculate... 
mhz = CPUSpeed()
MessageRequester("CPU Speed", "CPU speed: " + Str (mhz) + " MHz", #MB_ICONINFORMATION) 

Posted: Thu Sep 25, 2003 10:57 pm
by Pupil
If you run a loop calling the procedure you trash the FPU stack as you're not POP:ing all the stuff you put on there, so eventully you'll get the wrong results. You can fix this by poping the FPU stack one more time in the 'endloop:' section.

Posted: Thu Sep 25, 2003 11:05 pm
by jack
thanks Pupil :)

Posted: Fri Sep 26, 2003 3:02 pm
by blueb
Freak:
You have an idea who wrote the lib where your command is in?
Or where to get it?
I'd like to test it to see if the results are correct.
I located the command in the UserLibrary file: HelpFuncs

Don't know who wrote it, but when I opened it in an editor the coder who wrote it spells 'convert' as 'konvert' as in: konvert a Long to a binary

I suspect it is someone from the german forum.... just a guess :?


HTH,
blueb

Final version?

Posted: Sat Sep 27, 2003 6:03 pm
by Hi-Toro
Could someone post a final working version? I don't really know assembly, so I have no idea how to 'pop a stack'... ;)

Re: Final version?

Posted: Sat Sep 27, 2003 8:16 pm
by Pupil
Hi-Toro wrote:Could someone post a final working version? I don't really know assembly, so I have no idea how to 'pop a stack'... ;)
jack edited the code in his previous post so it all should work fine now, no need for you to pop anything :)

Cool

Posted: Sun Sep 28, 2003 2:44 am
by Hi-Toro
Thanks all -- see how we all benefit by sharing stuff? :)

Posted: Sat Mar 26, 2005 12:49 pm
by Rescator
Here is a alternative to the above routine.
Seems to work pretty accurately!

Lemme know if it matches what you think the CPU speed in Mhz is.
NOTE! You will get various results due to the overhead of actually calling procedures/functions like Delay() etc.
But the result variations should be within a few MHz, like most Mhz results.
(This is no exact sience :P

Code: Select all

Procedure.l GetCpuMhz()
Global int64val.LARGE_INTEGER
!FINIT
!rdtsc
!MOV dword [v_int64val+4],Edx
!MOV dword [v_int64val],Eax
!FILD qword [v_int64val]
Delay(1000)
!rdtsc
!MOV dword [v_int64val+4],Edx
!MOV dword [v_int64val],Eax
!FILD qword [v_int64val]
!FSUBR st1,st0
int64val\HighPart=0
int64val\LowPart=1000000
!FILD qword [v_int64val]
!FDIVR st0,st2
!fistp qword [v_int64val]
ProcedureReturn int64val\LowPart
EndProcedure

Debug GetCpuMhz() ;This should be the aprox CPU cycles that occured during the last second.
Debug GetCpuMhz()
Debug GetCpuMhz()
Debug GetCpuMhz()

Posted: Sat Mar 26, 2005 1:04 pm
by Rescator
Here's a fun test to run with the above routine:

Code: Select all

Debug "Doing 60 speed checks, should take aprox 1 minute."
c=0
For i=1 To 60
 Debug i
 c=c+GetCpuMhz()
Next
Debug ""
Debug "Cpu average speed: "+Str(c/60)+" Mhz"
Modern cpu's can change their speeds.
So if you makea game or program that depends on the cpu's speed.
Checking the Mhz at statup, but also now and again during (or between) certain tasks
should ensure that the cpu hasn't throttled down,
and if it has then you'll know it at least and can adapt the code to the lower speed :P