CPU Frequency MicroSecond time

Share your advanced PureBasic knowledge/code with the community.
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

CPU Frequency MicroSecond time

Post by idle »

Edited

MicroSecond not Nano :lol:

See posts bellow, doesn't work on x64 and probably has issues on multicores and speed stepping processors.

if it has any chance of working it needs to lock the thread with setThreadMaskAffinity to a CPU or Core (though I don't know if that works or not) to try and counter the speed step issues it periodically recalculates the CPU frequency.

Code: Select all


;Attemps to produce a workable Microsecond timer / delay
;I don't have a multicore system or speed stepping cpu so I have no idea if it will be stable
;
;The timer needs to be initalised in the main thread as it will try to lock the thread to a core
;should avoid issues with Time Stamps being out of sync on multi cores
;It also spawns another thread to check the frequency periodically (may need a mutex)
;
;Turn debugger off
;
;set to 0 to test without beginTimePeriod higher accuracy 
EnableExplicit 

#CompareMilliSeconds = 1

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
#X64 = 0
CompilerElse
#X64 = 1 
CompilerEndIf

CompilerIf #X64  
  Procedure.i CPUTime()
CompilerElse 
  Procedure.q CPUTime()
CompilerEndIf
   !XOr eax, eax
   !cpuid
   !rdtsc
   ProcedureReturn
EndProcedure

Global gthreadIDCheck.i,gtidCheckRun.i=1,bchangeFreq 

CompilerIf #X64
  Global gStartTime.i,gfrequency.i 
  Procedure.i CPUFrequency(intDelayTime.l=500)
CompilerElse 
  Global gStartTime.q,gfrequency.q 
  Procedure.q CPUFrequency(intDelayTime.l=500)
CompilerEndIf 

  Protected TimerHi.l,TimerLo.l,PriorityClass.l,Priority.l
  
  PriorityClass = GetPriorityClass_(GetCurrentProcess_());
  Priority = GetThreadPriority_(GetCurrentThread_());
  SetPriorityClass_(GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS);
  SetThreadPriority_(GetCurrentThread_(), #THREAD_PRIORITY_TIME_CRITICAL);
  Delay(10);

  EnableASM
  XOr eax, eax
  !cpuid
  rdtsc
  mov TimerLo, eax
  mov TimerHi, edx
 
  Delay(intDelayTime);

  XOr eax, eax
  !cpuid
  rdtsc
  sub eax, TimerLo
  sbb edx, TimerHi
  mov TimerLo, eax
  mov TimerHi, edx
  DisableASM

  SetThreadPriority_(GetCurrentThread_(),Priority);
  SetPriorityClass_(GetCurrentProcess_(), PriorityClass);
  
  ProcedureReturn TimerLo / (1000.0 * intDelayTime);

EndProcedure

Procedure CheckFreqency(CheckInterval.i=500)

 While gtidCheckRun
   gfrequency = CPUFrequency(CheckInterval)
   bchangeFreq  = 1 
   Delay(CheckInterval)
 Wend
 
EndProcedure

Procedure KillTimer()
  gtidCheckRun = 0
  If IsThread(gthreadIDCheck)
    WaitThread(gthreadIDCheck)
  EndIf   
EndProcedure 

Procedure InitTimer(CheckInterval.i=500)
  Protected mask.i=1
    
  If Not IsThread(gthreadIDCheck) 
     SetThreadAffinityMask_(GetCurrentThread_(),mask)
     gfrequency = CPUFrequency(CheckInterval)
     gThreadIDCheck = CreateThread(@CheckFreqency(),CheckInterval)
  EndIf     
    
  gStartTime = CPUTime()

  ProcedureReturn gfrequency

EndProcedure

Procedure GetElapsedMicroSeconds()
  CompilerIf #x64
     Protected tt.i,dt.i
  CompilerElse
     Protected tt.q,dt.q
  CompilerEndIf
  
  tt=CPUTime()
  dt = tt-gStartTime 
  If dt > 0
    ProcedureReturn dt / gFrequency
  Else 
    ProcedureReturn 0
  EndIf 
    
EndProcedure

Procedure DelayMicro(mtime.i)
  CompilerIf #x64
    Protected te.i, tn.i
  CompilerElse 
    Protected te.q, tn.q
  CompilerEndIf  
  
  te = GetElapsedMicroSeconds() + (mtime)
  Repeat
    Delay(0)
  Until GetElapsedMicroSeconds() >= te

EndProcedure

;test----------------------------------
;Set #CompareMilliSeconds to 1 to compare MS, higher accuracy without TimeBeginPeriod 
CompilerIf #CompareMilliSeconds 
  Global tt.TIMECAPS
  timeGetDevCaps_(@tt, SizeOf(TIMECAPS)) 
  timeBeginPeriod_(tt\wPeriodMin)
CompilerEndIf 
 
Global sum1.i, sum2.i,ct.i,sta.i,elt.i,mst.i,a,el.i,avg1,avg2

OpenConsole()
Delay(100)

Global mfreq = InitTimer(500)

PrintN(Str(mfreq) + " MHz") 
 
Repeat
  
  sta = GetElapsedMicroSeconds()
  CompilerIf #CompareMilliSeconds 
    mst = timeGetTime_()
  CompilerEndIf 
  a=0
  
  While a < 1000;  should be ~1000 Micro Seconds or 1MS to compleate loop
    DelayMicro(1)
    a+1
  Wend

  elt = GetElapsedMicroSeconds() - sta
  CompilerIf #CompareMilliSeconds 
    el = timeGetTime_() - mst
    PrintN(Str(gfrequency) + " MHz " + Str(elt) + " MicroSeconds " + Str(el) + " MilliSeconds")
  CompilerElse 
    If bchangeFreq 
      PrintN(Str(gfrequency) + " MHz " + Str(elt) + " MicroSeconds " +"frequency bump")
      bchangeFreq = #False 
    Else    
      PrintN(Str(gfrequency) + " MHz " + Str(elt) + " MicroSeconds ")
    EndIf 
  CompilerEndIf
  ct+1
  sum1 + elt
  sum2 + el

Until Inkey()
  
  avg1.i = sum1 / ct
  CompilerIf #CompareMilliSeconds 
    avg2.i = sum2 / ct
    PrintN("Averages " + Str(avg1) + " MicroSeconds " + Str(avg2) + " Milliseconds")
  CompilerElse
    PrintN("Averages " + Str(avg1) + " MicroSeconds ")
  CompilerEndIf  

KillTimer() 
 
Input() 
Last edited by idle on Wed Aug 19, 2009 5:29 am, edited 11 times in total.
AAT
Enthusiast
Enthusiast
Posts: 256
Joined: Sun Jun 15, 2008 3:13 am
Location: Russia

Re: CPU Frequency

Post by AAT »

My comp, Pentium Dual-Core CPU:
CPU_Z - 3204 MHz
your code - 3471 kHz
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Intel Pentium D, Dual Core with 2x1.86 GHz=3.4 GHz...
With your code, the Debugger wrote:3392
31238200311146
PB 4.30

Code: Select all

onErrorGoto(?Fred)
milan1612
Addict
Addict
Posts: 894
Joined: Thu Apr 05, 2007 12:15 am
Location: Nuremberg, Germany
Contact:

Post by milan1612 »

Doesn't work with Purebasic x64 :(
Windows 7 & PureBasic 4.4
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

Turns out accurate on my AMD 2000 mhz, maybe it has some problems problems with multi cores, though I don't know.
but if you need a timer with resolution less than 1 ms then I guess it'd be no worse than the regular timers.
Thorium
Addict
Addict
Posts: 1271
Joined: Sat Aug 15, 2009 6:59 pm

Post by Thorium »

idle wrote:Turns out accurate on my AMD 2000 mhz, maybe it has some problems problems with multi cores, though I don't know.
but if you need a timer with resolution less than 1 ms then I guess it'd be no worse than the regular timers.
Yes, there are problems with rdtsc and multi core CPU's. You have to ensure that the execution don't switches between the cores.

And if i understand it right, it counts on every CPU cycle. Whats about CPU's with variable frequenzy. It's standard on every laptop. And my Core i7 desktop is lowering the frequenzy too, if CPU load is low.

I don't trust this timer.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

works OK with my dual core. Why is it necessary to set the thread priority?
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

Yes you are right, it cant be trusted

Just what I saw done!
setting it to real time to get an accurate frequency
and by locking it to a CPU with SetThreadAffinityMask, though not sue if that's the same for mulitcore systems.

Don't know how your supposed to deal with the speed step stuff. I guess you could poll the frequency in a thread and fudge it. It's not like the cpu is going to step up and down at random, not when it's being pushed to grunt and if you need high performance time, I expect your intending to make it grunt!

Anyway I give up, was just a thought but after ogooling I realize that it's a problem fraught with difficulty.
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

maybe this is on track?

initializes the timer getting the frequency and start time
creates a thread to update the frequency while timing is going on
in the call to ElapsedNanoSeconds it will loop if it's got a negative time delta (I don't know if setting the thread affinity mask will keep it on one core or work with hyper threading.

I don't expect it'll be accurate.

see code in first post
2000 MHz
1011 NanoSeconds 1 MilliSeconds
1009 NanoSeconds 1 MilliSeconds
1014 NanoSeconds 1 MilliSeconds
1017 NanoSeconds 1 MilliSeconds
1009 NanoSeconds 1 MilliSeconds
1009 NanoSeconds 1 MilliSeconds
1009 NanoSeconds 1 MilliSeconds
1009 NanoSeconds 1 MilliSeconds
1010 NanoSeconds 1 MilliSeconds
1008 NanoSeconds 1 MilliSeconds
1684 NanoSeconds 2 MilliSeconds
1566 NanoSeconds 2 MilliSeconds
1064 NanoSeconds 1 MilliSeconds
1051 NanoSeconds 1 MilliSeconds
1040 NanoSeconds 1 MilliSeconds
1047 NanoSeconds 1 MilliSeconds
1008 NanoSeconds 1 MilliSeconds
1008 NanoSeconds 1 MilliSeconds
1009 NanoSeconds 1 MilliSeconds
1008 NanoSeconds 1 MilliSeconds
1008 NanoSeconds 1 MilliSeconds
Averages 1031 NanoSeconds 1 Milliseconds
2000 MHz
Last edited by idle on Mon Aug 17, 2009 9:50 pm, edited 1 time in total.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

m, you do realize that QueryPerformanceCounter may use RDTSC as well?, and in cases when RDTSC can't be trusted it uses the high precision timer of the bios or similar. (there are still some small issues with time skipping with QPC but not as bad as the issues RDTSC has)

So I don't really understand why your not basing this on the QPC instead if you need less than ms timing.

PS! Since you are already using timeBeginPeriod_() you might as well use timeGetTime_() instead of ticks.
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

There's no reason, other than seeing if it can be done.

I realize that QueryPerfomanceCounter will utilize another source if it can and still be subject to suffer the same problems, which presents a bit of a challenge.

So while I'm aware that it's probably not going to pan out as a solid timer there's no reason for not trying.

locking the thread to a cpu / core should mitigate the issue of drift and periodically measuring the frequency will at least enable it to recover on a speed stepping processor.

But I can't test it anymore, as I don't have a multicore or speed stepping processor.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Yeah! It's baffling M$ hasn't a solid solution for this.
Kinda why I love timeGetTime_() (with timeBeginPeriod_() obviously) so much, sure it only gives like 1ms but it's darn reliable, and luckily Im' yet to need nanosecs and I hope I never do ;)
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

Yes there's not much need for nanosecond timing, if your profiling code your more concerned about how many cycles it takes and if your timing a routine then your going to do it over 100's of iterations.

Still I'll like to see a some result of it on multi core systems.
Thorium
Addict
Addict
Posts: 1271
Joined: Sat Aug 15, 2009 6:59 pm

Post by Thorium »

idle wrote:Yes there's not much need for nanosecond timing, if your profiling code your more concerned about how many cycles it takes and if your timing a routine then your going to do it over 100's of iterations.

Still I'll like to see a some result of it on multi core systems.
Well your new source seems to be pretty good.

CPU: Intel Core i7 920 @2,66GHz (4 Cores)

Code: Select all

1055 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1067 NanoSeconds 1 MilliSeconds
1068 NanoSeconds 1 MilliSeconds
1074 NanoSeconds 1 MilliSeconds
1060 NanoSeconds 1 MilliSeconds
1059 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1069 NanoSeconds 1 MilliSeconds
1058 NanoSeconds 1 MilliSeconds
1067 NanoSeconds 1 MilliSeconds
1054 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1061 NanoSeconds 1 MilliSeconds
1054 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1124 NanoSeconds 2 MilliSeconds
1059 NanoSeconds 1 MilliSeconds
1079 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1054 NanoSeconds 1 MilliSeconds
1058 NanoSeconds 1 MilliSeconds
1059 NanoSeconds 1 MilliSeconds
1128 NanoSeconds 2 MilliSeconds
1057 NanoSeconds 1 MilliSeconds
1053 NanoSeconds 1 MilliSeconds
1090 NanoSeconds 1 MilliSeconds
1058 NanoSeconds 1 MilliSeconds
1070 NanoSeconds 1 MilliSeconds
1042 NanoSeconds 1 MilliSeconds
1066 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1124 NanoSeconds 2 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1067 NanoSeconds 1 MilliSeconds
1063 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1057 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1057 NanoSeconds 1 MilliSeconds
1057 NanoSeconds 1 MilliSeconds
1065 NanoSeconds 1 MilliSeconds
1068 NanoSeconds 1 MilliSeconds
1067 NanoSeconds 1 MilliSeconds
1057 NanoSeconds 1 MilliSeconds
1066 NanoSeconds 1 MilliSeconds
1069 NanoSeconds 1 MilliSeconds
1066 NanoSeconds 1 MilliSeconds
1058 NanoSeconds 1 MilliSeconds
1111 NanoSeconds 2 MilliSeconds
1062 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1119 NanoSeconds 2 MilliSeconds
1057 NanoSeconds 1 MilliSeconds
1059 NanoSeconds 1 MilliSeconds
1058 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1059 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1069 NanoSeconds 1 MilliSeconds
1068 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1103 NanoSeconds 1 MilliSeconds
1055 NanoSeconds 1 MilliSeconds
1058 NanoSeconds 1 MilliSeconds
1063 NanoSeconds 1 MilliSeconds
1073 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
1067 NanoSeconds 1 MilliSeconds
1056 NanoSeconds 1 MilliSeconds
Averages 1064 NanoSeconds 1 Milliseconds
2665 MHz
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

that looks promising, though not sure how to deal with the issues of the speed changes.
Post Reply