Page 1 of 2

High Resolution Timer Research

Posted: Sun Mar 04, 2012 9:39 pm
by Guimauve
Hello everyone,

I currently experimenting with some of depth time function in the "UNIX" based operating system (Linux, Mac OS, FreeBSD and so on)

I have found a web site (http://www.songho.ca/misc/timer/timer.html) describing the way about how to create a High Resolution Timer class in C++. The following code is inspired from this example but I don't know if I do a mistake or it's normal but "gettimeofday()" function is supposed to fill this structure :

Code: Select all

Structure TimeVal
  Second.l
  MicroSecond.l
EndStructure
but the MicroSecond field value is always equal to "0". Any clue about this ?

Thanks beforehand.
Guimauve

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : High Resolution Timer Research
; File Name : High Res Timer.pb
; File version: 1.0.0
; Programming : Bugged or inappropriate use of gettimeofday()
; Programmed by : Guimauve
; Date : 04-03-2012
; Last Update : 04-03-2012
; PureBasic code : 4.61 Beta 1 x 64
; Platform : Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Source : http://www.songho.ca/misc/timer/timer.html
;
; This is an experience to create a High Resolution Timer
; all platform compilant.
;
; For Windows :
;  - QueryPerformanceCounter_()
;  - QueryPerformanceFrequency_()
;
; For Linux, MacOS or any UNIX Based operating systems
; - gettimeofday()
;
; For Windows it's already OK (Tested by many forum 
; members) but on a UNIX Based system it don't work
; properly. Apparently the "gettimeofday()" return 
; the MicroSecond always equal to "0".
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Structure TimeVal
  Second.l
  MicroSecond.l
EndStructure

Macro GetTimeValSecond(TimeValA)
  TimeValA\Second
EndMacro

Macro GetTimeValMicroSecond(TimeValA)
  TimeValA\MicroSecond
EndMacro

Macro ResetTimeVal(TimeValA)
  GetTimeValSecond(TimeValA) = 0
  GetTimeValMicroSecond(TimeValA) = 0
EndMacro

; The "TimeZone" Structure has been declared here only
; to Import the "gettimeofday()" function. It should not
; be used anywere.

Structure TimeZone
  MinutesWest.L
  DestinationTime.l
EndStructure

ImportC ""
  gettimeofday(*TimeValA.TimeVal, *TimeZoneA.TimeZone)
EndImport

Var1.TimeVal
Var2.TimeVal

WaitTime.l = 3498

gettimeofday(@Var1, #Null)

Delay(WaitTime)

gettimeofday(@Var2, #Null)

EllipsedTime.d = (Var2\second - Var1\second) * 1000000 + (Var2\Microsecond - Var1\Microsecond)

Debug StrD(EllipsedTime, 10)

Debug "; If you see something very different from " + Str((WaitTime * 1000)) + " you should call the Houston Space Center and say :"
Debug "; " + Chr(34) + "Houston we have a Problem !" + Chr(34)

Delta = Int(Abs(EllipsedTime - WaitTime * 1000))
Debug "; The MicroSecond Delta is : " + Str(Delta)

If Delta > 5000
  Debug "; Completely unacceptable For High Resolution Timer"
Else
  Debug "; Accaptable for High Resolution Timer"
EndIf

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<

Re: High Resolution Timer Research

Posted: Sun Mar 04, 2012 11:13 pm
by dhouston
I can't help with your problem but here are a few links I saved a couple of years back when I was researching the same issue.

http://www.kernel.org/doc/man-pages/onl ... ime.2.html
http://www.kernel.org/doc/man-pages/onl ... eep.2.html
http://elinux.org/High_Resolution_Timers

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 2:18 am
by Guimauve
I will take a look, thanks.

Best regards.
Guimauve

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 7:07 am
by wilbert
@Guimauve, on OS X and Ubuntu 11 your code works fine.
gettimeofday does return microseconds on OS X as well as on Ubuntu 11 (on my computer).
For OS X you can also use nanoseconds as I posted yesterday
http://www.purebasic.fr/english/viewtop ... 19&t=49359

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 12:44 pm
by Guimauve
wilbert wrote:@Guimauve, on OS X and Ubuntu 11 your code works fine.
gettimeofday does return microseconds on OS X as well as on Ubuntu 11 (on my computer).
For OS X you can also use nanoseconds as I posted yesterday
http://www.purebasic.fr/english/viewtop ... 19&t=49359

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Type V3.139.671
; Project name : High Resolution Timer
; File name : HighResTimer.pb
; File Version : 0.1.9
; Programmation : In progress
; Programmed by : Guimauve
; Creation Date : 04-03-2012
; Last update : 04-03-2012
; Coded for PureBasic V4.60
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Notes :
;
; This is a Try to create a Windows, Linux and 
; MacOS X compliant High Resolution Timer sustem.
;
; It's insperated from Timer Class found here :
; http://www.songho.ca/misc/timer/timer.html
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

CompilerIf #PB_Compiler_OS <> #PB_OS_Windows 
  
  Structure TimeVal
    Second.l
    MicroSecond.l
  EndStructure
  
  Macro GetTimeValSecond(TimeValA)
    TimeValA\Second
  EndMacro
  
  Macro GetTimeValMicroSecond(TimeValA)
    TimeValA\MicroSecond
  EndMacro
  
  Macro ResetTimeVal(TimeValA)
    GetTimeValSecond(TimeValA) = 0
    GetTimeValMicroSecond(TimeValA) = 0
  EndMacro
  
  ; The "TimeZone" Structure has been declared here only
  ; to Import the "gettimeofday()" function. It should not
  ; be used anywere.
  
  Structure TimeZone
    MinutesWest.L
    DestinationTime.l
  EndStructure
  
  ImportC ""
    gettimeofday(*TimeValA.TimeVal, *TimeZoneA.TimeZone)
  EndImport
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Structure declaration <<<<<

Structure HighResTimer
  
  Stopped.b
  StartMicroSec.d
  StopMicroSec.d
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    Frequency.q
    StartCount.q
    EndCount.q
  CompilerElse
    StartCount.TimeVal
    EndCount.TimeVal
  CompilerEndIf
  
EndStructure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The observators <<<<<

Macro GetHighResTimerStopped(HighResTimerA)
  
  HighResTimerA\Stopped
  
EndMacro

Macro GetHighResTimerStartMicroSec(HighResTimerA)
  
  HighResTimerA\StartMicroSec
  
EndMacro

Macro GetHighResTimerStopMicroSec(HighResTimerA)
  
  HighResTimerA\StopMicroSec
  
EndMacro

CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
  
  Macro GetHighResTimerFrequency(HighResTimerA)
    
    HighResTimerA\Frequency
    
  EndMacro
  
  Macro GetHighResTimerStartCount(HighResTimerA)
    
    HighResTimerA\StartCount
    
  EndMacro
  
  Macro GetHighResTimerEndCount(HighResTimerA)
    
    HighResTimerA\EndCount
    
  EndMacro
  
CompilerElse
  
  Macro GetHighResTimerStartCount(HighResTimerA)
    
    HighResTimerA\StartCount
    
  EndMacro
  
  Macro GetHighResTimerEndCount(HighResTimerA)
    
    HighResTimerA\EndCount
    
  EndMacro
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The mutators <<<<<

Macro SetHighResTimerStopped(HighResTimerA, P_Stopped)
  
  GetHighResTimerStopped(HighResTimerA) = P_Stopped
  
EndMacro

Macro SetHighResTimerStartMicroSec(HighResTimerA, P_StartMicroSec)
  
  GetHighResTimerStartMicroSec(HighResTimerA) = P_StartMicroSec
  
EndMacro

Macro SetHighResTimerStopMicroSec(HighResTimerA, P_StopMicroSec)
  
  GetHighResTimerStopMicroSec(HighResTimerA) = P_StopMicroSec
  
EndMacro

CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
  
  Macro SetHighResTimerFrequency(HighResTimerA, P_Frequency)
    
    GetHighResTimerFrequency(HighResTimerA) = P_Frequency
    
  EndMacro
  
  Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)
    
    GetHighResTimerStartCount(HighResTimerA) = P_StartCount
    
  EndMacro
  
  Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
    
    GetHighResTimerEndCount(HighResTimerA) = P_EndCount
    
  EndMacro
  
CompilerElse
  
  Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)
    
    CopyTimeVal(P_StartCount, GetHighResTimerStartCount(HighResTimerA))
    
  EndMacro
  
  Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
    
    CopyTimeVal(P_EndCount, GetHighResTimerEndCount(HighResTimerA))
    
  EndMacro
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Reset operator <<<<<

Macro ResetHighResTimer(HighResTimerA)
  
  SetHighResTimerStopped(HighResTimerA, 0)
  SetHighResTimerStartMicroSec(HighResTimerA, 0.0)
  SetHighResTimerStopMicroSec(HighResTimerA, 0.0)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    SetHighResTimerFrequency(HighResTimerA, 0)
    SetHighResTimerStartCount(HighResTimerA, 0)
    SetHighResTimerEndCount(HighResTimerA, 0)
  CompilerElse
    ResetTimeVal(GetHighResTimerStartCount(HighResTimerA))
    ResetTimeVal(GetHighResTimerEndCount(HighResTimerA))
  CompilerEndIf
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.010 seconds (17200.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Macro HighResTimerElapsedMilliSec(HighResTimerA)
  
  (HighResTimerElapsedMicroSec(HighResTimerA) * 0.001)
  
EndMacro 

Macro HighResTimerElapsedSec(HighResTimerA)
  
  (HighResTimerElapsedMicroSec(HighResTimerA) * 0.000001)
  
EndMacro 

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Initialize operator <<<<<

Procedure InitializeHighResTimer(*HighResTimerA.HighResTimer)
  
  ResetHighResTimer(*HighResTimerA)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    QueryPerformanceFrequency_(@GetHighResTimerFrequency(*HighResTimerA))
  CompilerElse
    ResetTimeVal(GetHighResTimerStartCount(*HighResTimerA))
    ResetTimeVal(GetHighResTimerEndCount(*HighResTimerA))
  CompilerEndIf
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Start operator <<<<<

Procedure StartHighResTimer(*HighResTimerA.HighResTimer)
  
  SetHighResTimerStopped(*HighResTimerA, #False)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    
    If GetHighResTimerFrequency(*HighResTimerA) = 0
      InitializeHighResTimer(*HighResTimerA.HighResTimer)
    EndIf
    
    QueryPerformanceCounter_(@GetHighResTimerStartCount(*HighResTimerA))
    
  CompilerElse
    
    gettimeofday(@GetHighResTimerStartCount(*HighResTimerA), #Null)
    
  CompilerEndIf
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Stop operator <<<<<

Procedure StopHighResTimer(*HighResTimerA.HighResTimer)
  
  SetHighResTimerStopped(*HighResTimerA, #True)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    QueryPerformanceCounter_(@GetHighResTimerEndCount(HighResTimerA))
  CompilerElse
    gettimeofday(@GetHighResTimerEndCount(*HighResTimerA), #Null)
  CompilerEndIf
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The ElapsedMicroSec <<<<<

Procedure.d HighResTimerElapsedMicroSec(*HighResTimerA.HighResTimer)
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    
    If GetHighResTimerStopped(*HighResTimerA) = #False
      QueryPerformanceCounter_(@GetHighResTimerEndCount(*HighResTimerA))
    EndIf
    
    SetHighResTimerStartMicroSec(*HighResTimerA, GetHighResTimerStartCount(*HighResTimerA) * (1000000.0 / GetHighResTimerFrequency(*HighResTimerA)))
    SetHighResTimerStopMicroSec(*HighResTimerA, GetHighResTimerEndCount(*HighResTimerA) * (1000000.0 / GetHighResTimerFrequency(*HighResTimerA)))
    
  CompilerElse
   
    If GetHighResTimerStopped(*HighResTimerA) = #False
      gettimeofday(@GetHighResTimerEndCount(*HighResTimerA), #Null)
    EndIf

    SetHighResTimerStartMicroSec(*HighResTimerA, (GetTimeValSecond(GetHighResTimerStartCount(*HighResTimerA)) * 1000000.0) + GetTimeValMicroSecond(GetHighResTimerStartCount(*HighResTimerA))) 
    SetHighResTimerStopMicroSec(*HighResTimerA, (GetTimeValSecond(GetHighResTimerEndCount(*HighResTimerA)) * 1000000.0) + GetTimeValMicroSecond(GetHighResTimerStartCount(*HighResTimerA))) 
    
  CompilerEndIf
  
  ProcedureReturn GetHighResTimerStopMicroSec(*HighResTimerA) - GetHighResTimerStartMicroSec(*HighResTimerA)
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
; <<<<< !!! WARNING - YOU ARE NOW IN A TESTING ZONE - WARNING !!! <<<<< 
; <<<<< !!! WARNING - THIS CODE SHOULD BE COMMENTED - WARNING !!! <<<<< 
; <<<<< !!! WARNING - BEFORE THE FINAL COMPILATION. - WARNING !!! <<<<< 
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

StartHighResTimer(HResTimer.HighResTimer)

Delay(5)
Debug StrD(HighResTimerElapsedMicroSec(HResTimer), 6)
Delay(50)
Debug StrD(HighResTimerElapsedMicroSec(HResTimer), 6)
Delay(499)
Debug StrD(HighResTimerElapsedMicroSec(HResTimer), 6)
Delay(500)
Debug StrD(HighResTimerElapsedMicroSec(HResTimer), 6)
Delay(1499)
Debug StrD(HighResTimerElapsedMicroSec(HResTimer), 6)
Delay(1501)
Debug StrD(HighResTimerElapsedMicroSec(HResTimer), 6)

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
For me with computer with Linux Mint 12 x64 the debugger output show this :
0.000000
0.000000
0.000000
1000000.000000
2000000.000000
4000000.000000
It's exactly like the time is rounded to the nearest second. If this command work properly, it should return something on after the Delay(5), Delay(50) and Delay(499) but always 0.000000.
I say it's not normal or I have some wrong BIOS settings, I don't know.

Best regards
Guimauve

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 1:07 pm
by wilbert
Are you sure the problem isn't somewhere else in your code ?
The code you posted above also gives results on OS X that end on all zeros while I'm 100% sure the api call itself does fill the microseconds part.

Linux - nanosecond - alternative based on this thread
http://www.purebasic.fr/english/viewtop ... 15&t=38826

Code: Select all

#CLOCK_MONOTONIC = 1

ImportC "-lrt"
  clock_gettime(clk_id, *tp)
EndImport

Structure timespec
  tv_sec.i;          /* seconds */
  tv_nsec.l;         /* nanoseconds [0 .. 999999999] */
EndStructure 


clock_gettime(#CLOCK_MONOTONIC, @start_time.timespec)

Delay(75)

clock_gettime(#CLOCK_MONOTONIC, @end_time.timespec)

delta.q = (end_time\tv_sec - start_time\tv_sec) * 1e9 + end_time\tv_nsec - start_time\tv_nsec

Debug delta

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 3:35 pm
by Guimauve
Thanks Wilbert, I finally get it to work.

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Type V3.139.671
; Project name : High Resolution Timer
; File name : HighResTimer.pb
; File Version : 1.0.0
; Programmation : OK
; Programmed by : Guimauve
; Creation Date : 04-03-2012
; Last update : 04-03-2012
; Coded for PureBasic V4.60
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Notes :
;
; This is a Try to create a Windows, Linux and 
; MacOS X compliant High Resolution Timer sustem.
;
; It's insperated from Timer Class found here :
; http://www.songho.ca/misc/timer/timer.html
;
; Additional Code :
;
; - Wilbert (PureBasic English Forum)
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Linux
    
    #CLOCK_MONOTONIC = 1
    
    Structure TimeSpec
      Second.i
      NanoSecond.l
    EndStructure
    
    Macro GetTimeSpecSecond(TimeSpecA)
      TimeSpecA\Second
    EndMacro
    
    Macro GetTimeSpecNanoSecond(TimeSpecA)
      TimeSpecA\NanoSecond
    EndMacro
    
    Macro ResetTimeSpec(TimeSpecA)
      GetTimeSpecSecond(TimeSpecA) = 0
      GetTimeSpecNanoSecond(TimeSpecA) = 0
    EndMacro
    
    ImportC "-lrt"
      clock_gettime(ClockID.l, *TimeSpecA.TimeSpec)
    EndImport

  CompilerCase #PB_OS_MacOS
    
    ImportC "/System/Library/Frameworks/CoreServices.framework/CoreServices"
      mach_absolute_time.q()
    EndImport
    
CompilerEndSelect

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Structure declaration <<<<<

Structure HighResTimer
  
  Stopped.b
  StartMicroSec.q
  StopMicroSec.q
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      Frequency.q
      StartCount.q
      EndCount.q
      
    CompilerCase #PB_OS_Linux
      StartCount.TimeSpec
      EndCount.TimeSpec

    CompilerCase #PB_OS_MacOS
      StartCount.q
      EndCount.q
      
  CompilerEndSelect
  
EndStructure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The observators <<<<<

Macro GetHighResTimerStopped(HighResTimerA)
  
  HighResTimerA\Stopped
  
EndMacro

Macro GetHighResTimerStartMicroSec(HighResTimerA)
  
  HighResTimerA\StartMicroSec
  
EndMacro

Macro GetHighResTimerStopMicroSec(HighResTimerA)
  
  HighResTimerA\StopMicroSec
  
EndMacro

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Windows 
    
    Macro GetHighResTimerFrequency(HighResTimerA)
      HighResTimerA\Frequency
    EndMacro
    
    Macro GetHighResTimerStartCount(HighResTimerA)
      HighResTimerA\StartCount
    EndMacro
    
    Macro GetHighResTimerEndCount(HighResTimerA)
      HighResTimerA\EndCount
    EndMacro
    
  CompilerCase #PB_OS_Linux
    
    Macro GetHighResTimerStartCount(HighResTimerA)
      HighResTimerA\StartCount
    EndMacro
    
    Macro GetHighResTimerEndCount(HighResTimerA)
      HighResTimerA\EndCount
    EndMacro

  CompilerCase #PB_OS_MacOS
    
    Macro GetHighResTimerStartCount(HighResTimerA)
      HighResTimerA\StartCount
    EndMacro
    
    Macro GetHighResTimerEndCount(HighResTimerA)
      HighResTimerA\EndCount
    EndMacro
    
CompilerEndSelect

; <<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The mutators <<<<<

Macro SetHighResTimerStopped(HighResTimerA, P_Stopped)
  
  GetHighResTimerStopped(HighResTimerA) = P_Stopped
  
EndMacro

Macro SetHighResTimerStartMicroSec(HighResTimerA, P_StartMicroSec)
  
  GetHighResTimerStartMicroSec(HighResTimerA) = P_StartMicroSec
  
EndMacro

Macro SetHighResTimerStopMicroSec(HighResTimerA, P_StopMicroSec)
  
  GetHighResTimerStopMicroSec(HighResTimerA) = P_StopMicroSec
  
EndMacro

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Windows 
    
    Macro SetHighResTimerFrequency(HighResTimerA, P_Frequency)
      GetHighResTimerFrequency(HighResTimerA) = P_Frequency
    EndMacro
    
    Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)
      GetHighResTimerStartCount(HighResTimerA) = P_StartCount
    EndMacro
    
    Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
      GetHighResTimerEndCount(HighResTimerA) = P_EndCount
    EndMacro
    
  CompilerCase #PB_OS_Linux
    
    Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)  
      CopyTimeSpec(P_StartCount, GetHighResTimerStartCount(HighResTimerA))
    EndMacro
    
    Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
      CopyTimeSpec(P_EndCount, GetHighResTimerEndCount(HighResTimerA))
    EndMacro
    
  CompilerCase #PB_OS_MacOS
    
    Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)
      GetHighResTimerStartCount(HighResTimerA) = P_StartCount
    EndMacro
    
    Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
      GetHighResTimerEndCount(HighResTimerA) = P_EndCount
    EndMacro
    
CompilerEndSelect

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Reset operator <<<<<

Macro ResetHighResTimer(HighResTimerA)
  
  SetHighResTimerStopped(HighResTimerA, 0)
  SetHighResTimerStartMicroSec(HighResTimerA, 0.0)
  SetHighResTimerStopMicroSec(HighResTimerA, 0.0)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      SetHighResTimerFrequency(HighResTimerA, 0)
      SetHighResTimerStartCount(HighResTimerA, 0)
      SetHighResTimerEndCount(HighResTimerA, 0)
      
    CompilerCase #PB_OS_Linux
      ResetTimeSpec(GetHighResTimerStartCount(HighResTimerA))
      ResetTimeSpec(GetHighResTimerEndCount(HighResTimerA))
      
    CompilerCase #PB_OS_MacOS
      SetHighResTimerStartCount(HighResTimerA, 0)
      SetHighResTimerEndCount(HighResTimerA, 0)
      
  CompilerEndSelect
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.010 seconds (17200.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Macro HighResTimerElapsedMilliSec(HighResTimerA)
  
  (HighResTimerElapsedMicroSec(HighResTimerA) * 0.001)
  
EndMacro 

Macro HighResTimerElapsedSec(HighResTimerA)
  
  (HighResTimerElapsedMicroSec(HighResTimerA) * 0.000001)
  
EndMacro 

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Initialize operator <<<<<

Procedure InitializeHighResTimer(*HighResTimerA.HighResTimer)
  
  ResetHighResTimer(*HighResTimerA)

  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    QueryPerformanceFrequency_(@GetHighResTimerFrequency(*HighResTimerA))
  CompilerEndIf
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Start operator <<<<<

Procedure StartHighResTimer(*HighResTimerA.HighResTimer)
  
  SetHighResTimerStopped(*HighResTimerA, #False)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      
      If GetHighResTimerFrequency(*HighResTimerA) = 0
        InitializeHighResTimer(*HighResTimerA.HighResTimer)
      EndIf
      
      QueryPerformanceCounter_(@GetHighResTimerStartCount(*HighResTimerA))
      
    CompilerCase #PB_OS_Linux
      clock_gettime(#CLOCK_MONOTONIC, @GetHighResTimerStartCount(*HighResTimerA))
      
    CompilerCase #PB_OS_MacOS
      SetHighResTimerStartCount(HighResTimerA, mach_absolute_time())
      
  CompilerEndSelect

EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Stop operator <<<<<

Procedure StopHighResTimer(*HighResTimerA.HighResTimer)
  
  SetHighResTimerStopped(*HighResTimerA, #True)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      QueryPerformanceCounter_(@GetHighResTimerEndCount(*HighResTimerA))
      
    CompilerCase #PB_OS_Linux
      clock_gettime(#CLOCK_MONOTONIC, @GetHighResTimerEndCount(*HighResTimerA))
      
    CompilerCase #PB_OS_MacOS
      SetHighResTimerEndCount(HighResTimerA, mach_absolute_time())
      
  CompilerEndSelect
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The ElapsedMicroSec <<<<<

Procedure.q HighResTimerElapsedMicroSec(*HighResTimerA.HighResTimer)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      
      If GetHighResTimerStopped(*HighResTimerA) = #False
        QueryPerformanceCounter_(@GetHighResTimerEndCount(*HighResTimerA))
      EndIf
      
      SetHighResTimerStartMicroSec(*HighResTimerA, GetHighResTimerStartCount(*HighResTimerA) * (1000000 / GetHighResTimerFrequency(*HighResTimerA)))
      SetHighResTimerStopMicroSec(*HighResTimerA, GetHighResTimerEndCount(*HighResTimerA) * (1000000 / GetHighResTimerFrequency(*HighResTimerA)))
      
    CompilerCase #PB_OS_Linux
      
      If GetHighResTimerStopped(*HighResTimerA) = #False
        clock_gettime(#CLOCK_MONOTONIC, @GetHighResTimerEndCount(*HighResTimerA))
      EndIf
      
      SetHighResTimerStartMicroSec(*HighResTimerA, GetTimeSpecSecond(GetHighResTimerStartCount(*HighResTimerA)) * 1000000 + GetTimeSpecNanoSecond(GetHighResTimerStartCount(*HighResTimerA)) / 1000) 
      SetHighResTimerStopMicroSec(*HighResTimerA, GetTimeSpecSecond(GetHighResTimerEndCount(*HighResTimerA)) * 1000000 + GetTimeSpecNanoSecond(GetHighResTimerEndCount(*HighResTimerA)) / 1000) 
      
    CompilerCase #PB_OS_MacOS
      
      If GetHighResTimerStopped(*HighResTimerA) = #False
        SetHighResTimerEndCount(HighResTimerA, mach_absolute_time())
      EndIf
      
      SetHighResTimerStartMicroSec(*HighResTimerA, GetHighResTimerStartMicroSec(*HighResTimerA) / 1000)
      SetHighResTimerStopMicroSec(*HighResTimerA, GetHighResTimerStopMicroSec(*HighResTimerA) / 1000)
      
  CompilerEndSelect
  
  ProcedureReturn GetHighResTimerStopMicroSec(*HighResTimerA) - GetHighResTimerStartMicroSec(*HighResTimerA)
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
; <<<<< !!! WARNING - YOU ARE NOW IN A TESTING ZONE - WARNING !!! <<<<< 
; <<<<< !!! WARNING - THIS CODE SHOULD BE COMMENTED - WARNING !!! <<<<< 
; <<<<< !!! WARNING - BEFORE THE FINAL COMPILATION. - WARNING !!! <<<<< 
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

StartHighResTimer(HResTimer.HighResTimer)

Delay(1)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(5)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(499)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(500)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(1499)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(1501)
Debug HighResTimerElapsedMicroSec(HResTimer)

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
Then the Debugger output :
1058
6138
505244
1005381
2504528
4005675
Best regards
Guimauve

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 4:20 pm
by wilbert
Glad you got it to work :D

A little remark on OS X ... mach_absolute_time() like you are using now is the best function to use since it is very fast.
The return value however is absolute time. On most systems this seems to be identical to nanoseconds but it is not guaranteed to be so.
To make sure you get nanoseconds, you can use a simple conversion function or convert it yourself

Code: Select all

Structure tb_info
  numer.l
  denom.l
EndStructure
  
ImportC ""
  mach_absolute_time.q()
  mach_timebase_info(*info)
EndImport

mach_timebase_info(@info.tb_info)
micro_conv.d = 1e-3 * info\numer / info\denom

abs_time.q = mach_absolute_time()
micro_time.q = abs_time * micro_conv
AbsoluteToNanoseconds from the CoreServices framework is the easy way.
The other (more low level) way is to get information using mach_timebase_info once and when you want to convert absolute time to nanoseconds, multiply by numer and divide by denom.
Since you want microseconds, the easiest way is maybe to add a double precision value to your timer structure for OS X that you initialize once and multiply by that like the example above.

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 5:20 pm
by Guimauve
wilbert wrote:Glad you got it to work :D

A little remark on OS X ... mach_absolute_time() like you are using now is the best function to use since it is very fast.
The return value however is absolute time. On most systems this seems to be identical to nanoseconds but it is not guaranteed to be so.
To make sure you get nanoseconds, you can use a simple conversion function or convert it yourself

AbsoluteToNanoseconds from the CoreServices framework is the easy way.
The other (more low level) way is to get information using mach_timebase_info once and when you want to convert absolute time to nanoseconds, multiply by numer and divide by denom.
Since you want microseconds, the easiest way is maybe to add a double precision value to your timer structure for OS X that you initialize once and multiply by that like the example above.
Like this :

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Type V3.139.671
; Project name : High Resolution Timer
; File name : HighResTimer.pb
; File Version : 1.1.0
; Programmation : OK
; Programmed by : Guimauve
; Creation Date : 04-03-2012
; Last update : 05-03-2012
; Coded for PureBasic V4.60
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Notes :
;
; This is a Windows, Linux and MacOS X compliant 
; High Resolution Timer sustem.
;
; It's insperated from Timer Class found here :
; http://www.songho.ca/misc/timer/timer.html
;
; Additional Code
; - Wilbert (PureBasic English Forum)
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Linux
    
    #CLOCK_MONOTONIC = 1
    
    Structure TimeSpec
      Second.i
      NanoSecond.l
    EndStructure
    
    Macro GetTimeSpecSecond(TimeSpecA)
      TimeSpecA\Second
    EndMacro
    
    Macro GetTimeSpecNanoSecond(TimeSpecA)
      TimeSpecA\NanoSecond
    EndMacro
    
    Macro ResetTimeSpec(TimeSpecA)
      GetTimeSpecSecond(TimeSpecA) = 0
      GetTimeSpecNanoSecond(TimeSpecA) = 0
    EndMacro
    
    ImportC "-lrt"
      clock_gettime(ClockID.l, *TimeSpecA.TimeSpec)
    EndImport

  CompilerCase #PB_OS_MacOS
    
    Structure TimeBase
      Numerator.l
      Denominator.l
    EndStructure
    
    Macro GetTimeBaseNumerator(TimeBaseA)
      TimeBaseA\Numerator
    EndMacro
    
    Macro GetTimeBaseDenominator(TimeBaseA)
      TimeBaseA\Denominator
    EndMacro
    
    Macro ResetTimeBase(TimeBaseA)
      GetTimeBaseNumerator(TimeBaseA) = 0
      GetTimeBaseDenominator(TimeBaseA) = 0
    EndMacro

    ImportC ""
      mach_absolute_time.q()
      mach_timebase_info(*TimeBaseA.TimeBase)
    EndImport
    
CompilerEndSelect

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Structure declaration <<<<<

Structure HighResTimer
  
  Stopped.b
  StartMicroSec.q
  StopMicroSec.q
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      Frequency.q
      StartCount.q
      EndCount.q
      
    CompilerCase #PB_OS_Linux
      StartCount.TimeSpec
      EndCount.TimeSpec

    CompilerCase #PB_OS_MacOS
      MicroSecConversion.d
      TimeBase.TimeBase
      StartCount.q
      EndCount.q
      
  CompilerEndSelect
  
EndStructure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The observators <<<<<

Macro GetHighResTimerStopped(HighResTimerA)
  
  HighResTimerA\Stopped
  
EndMacro

Macro GetHighResTimerStartMicroSec(HighResTimerA)
  
  HighResTimerA\StartMicroSec
  
EndMacro

Macro GetHighResTimerStopMicroSec(HighResTimerA)
  
  HighResTimerA\StopMicroSec
  
EndMacro

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Windows 
    
    Macro GetHighResTimerFrequency(HighResTimerA)
      HighResTimerA\Frequency
    EndMacro
    
    Macro GetHighResTimerStartCount(HighResTimerA)
      HighResTimerA\StartCount
    EndMacro
    
    Macro GetHighResTimerEndCount(HighResTimerA)
      HighResTimerA\EndCount
    EndMacro
    
  CompilerCase #PB_OS_Linux
    
    Macro GetHighResTimerStartCount(HighResTimerA)
      HighResTimerA\StartCount
    EndMacro
    
    Macro GetHighResTimerEndCount(HighResTimerA)
      HighResTimerA\EndCount
    EndMacro

  CompilerCase #PB_OS_MacOS
    
    Macro GetHighResTimerMicroSecConversion(HighResTimerA)
      HighResTimerA\MicroSecConversion
    EndMacro
    
    Macro GetHighResTimerTimeBase(HighResTimerA)
      HighResTimerA\TimeBase
    EndMacro
    
    Macro GetHighResTimerStartCount(HighResTimerA)
      HighResTimerA\StartCount
    EndMacro
    
    Macro GetHighResTimerEndCount(HighResTimerA)
      HighResTimerA\EndCount
    EndMacro
    
CompilerEndSelect

; <<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The mutators <<<<<

Macro SetHighResTimerStopped(HighResTimerA, P_Stopped)
  
  GetHighResTimerStopped(HighResTimerA) = P_Stopped
  
EndMacro

Macro SetHighResTimerStartMicroSec(HighResTimerA, P_StartMicroSec)
  
  GetHighResTimerStartMicroSec(HighResTimerA) = P_StartMicroSec
  
EndMacro

Macro SetHighResTimerStopMicroSec(HighResTimerA, P_StopMicroSec)
  
  GetHighResTimerStopMicroSec(HighResTimerA) = P_StopMicroSec
  
EndMacro

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Windows 
    
    Macro SetHighResTimerFrequency(HighResTimerA, P_Frequency)
      GetHighResTimerFrequency(HighResTimerA) = P_Frequency
    EndMacro
    
    Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)
      GetHighResTimerStartCount(HighResTimerA) = P_StartCount
    EndMacro
    
    Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
      GetHighResTimerEndCount(HighResTimerA) = P_EndCount
    EndMacro
    
  CompilerCase #PB_OS_Linux
    
    Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)  
      CopyTimeSpec(P_StartCount, GetHighResTimerStartCount(HighResTimerA))
    EndMacro
    
    Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
      CopyTimeSpec(P_EndCount, GetHighResTimerEndCount(HighResTimerA))
    EndMacro
    
  CompilerCase #PB_OS_MacOS
    
    Macro SetHighResTimerMicroSecConversion(HighResTimerA, P_MicroSecConversion)
      GetHighResTimerMicroSecConversion(HighResTimerA) = P_MicroSecConversion
    EndMacro
    
    Macro SetHighResTimerTimeBase(HighResTimerA, P_TimeBase)
      CopyMemory(P_TimeBase, GetHighResTimerTimeBase(HighResTimerA), SizeOf(TimeBase))
    EndMacro
    
    Macro SetHighResTimerStartCount(HighResTimerA, P_StartCount)
      GetHighResTimerStartCount(HighResTimerA) = P_StartCount
    EndMacro
    
    Macro SetHighResTimerEndCount(HighResTimerA, P_EndCount)
      GetHighResTimerEndCount(HighResTimerA) = P_EndCount
    EndMacro
    
CompilerEndSelect

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Reset operator <<<<<

Macro ResetHighResTimer(HighResTimerA)
  
  SetHighResTimerStopped(HighResTimerA, 0)
  SetHighResTimerStartMicroSec(HighResTimerA, 0)
  SetHighResTimerStopMicroSec(HighResTimerA, 0)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      SetHighResTimerFrequency(HighResTimerA, 0)
      SetHighResTimerStartCount(HighResTimerA, 0)
      SetHighResTimerEndCount(HighResTimerA, 0)
      
    CompilerCase #PB_OS_Linux
      ResetTimeSpec(GetHighResTimerStartCount(HighResTimerA))
      ResetTimeSpec(GetHighResTimerEndCount(HighResTimerA))
      
    CompilerCase #PB_OS_MacOS
      SetHighResTimerMicroSecConversion(HighResTimerA, 0.0)
      ResetTimeBase(GetHighResTimerTimeBase(HighResTimerA))
      SetHighResTimerStartCount(HighResTimerA, 0)
      SetHighResTimerEndCount(HighResTimerA, 0)
      
  CompilerEndSelect
  
EndMacro

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.010 seconds (17200.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Macro HighResTimerElapsedMilliSec(HighResTimerA)
  
  (HighResTimerElapsedMicroSec(HighResTimerA) * 0.001)
  
EndMacro 

Macro HighResTimerElapsedSec(HighResTimerA)
  
  (HighResTimerElapsedMicroSec(HighResTimerA) * 0.000001)
  
EndMacro 

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Initialize operator <<<<<

Procedure InitializeHighResTimer(*HighResTimerA.HighResTimer)
  
  ResetHighResTimer(*HighResTimerA)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      QueryPerformanceFrequency_(@GetHighResTimerFrequency(*HighResTimerA))
      
    CompilerCase #PB_OS_MacOS
      mach_timebase_info(@GetHighResTimerTimeBase(*HighResTimerA))
      SetHighResTimerMicroSecConversion(*HighResTimerA, 1e-3 * GetTimeBaseNumerator(GetHighResTimerTimeBase(*HighResTimerA)) / GetTimeBaseDenominator(GetHighResTimerTimeBase(*HighResTimerA)))
      
  CompilerEndSelect
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Start operator <<<<<

Procedure StartHighResTimer(*HighResTimerA.HighResTimer)
  
  SetHighResTimerStopped(*HighResTimerA, #False)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      
      If GetHighResTimerFrequency(*HighResTimerA) = 0
        InitializeHighResTimer(*HighResTimerA)
      EndIf
      
      QueryPerformanceCounter_(@GetHighResTimerStartCount(*HighResTimerA))
      
    CompilerCase #PB_OS_Linux
      clock_gettime(#CLOCK_MONOTONIC, @GetHighResTimerStartCount(*HighResTimerA))
      
    CompilerCase #PB_OS_MacOS
      
      If GetHighResTimerMicroSecConversion(*HighResTimerA) = 0.0
        InitializeHighResTimer(*HighResTimerA)
      EndIf
      
      SetHighResTimerStartCount(*HighResTimerA, mach_absolute_time() * GetHighResTimerMicroSecConversion(*HighResTimerA))
      
  CompilerEndSelect

EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Stop operator <<<<<

Procedure StopHighResTimer(*HighResTimerA.HighResTimer)
  
  SetHighResTimerStopped(*HighResTimerA, #True)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      QueryPerformanceCounter_(@GetHighResTimerEndCount(*HighResTimerA))
      
    CompilerCase #PB_OS_Linux
      clock_gettime(#CLOCK_MONOTONIC, @GetHighResTimerEndCount(*HighResTimerA))
      
    CompilerCase #PB_OS_MacOS
      SetHighResTimerEndCount(*HighResTimerA, mach_absolute_time() * GetHighResTimerMicroSecConversion(*HighResTimerA))
      
  CompilerEndSelect
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The ElapsedMicroSec <<<<<

Procedure.q HighResTimerElapsedMicroSec(*HighResTimerA.HighResTimer)
  
  CompilerSelect #PB_Compiler_OS
      
    CompilerCase #PB_OS_Windows 
      
      If GetHighResTimerStopped(*HighResTimerA) = #False
        QueryPerformanceCounter_(@GetHighResTimerEndCount(*HighResTimerA))
      EndIf
      
      SetHighResTimerStartMicroSec(*HighResTimerA, GetHighResTimerStartCount(*HighResTimerA) * (1000000 / GetHighResTimerFrequency(*HighResTimerA)))
      SetHighResTimerStopMicroSec(*HighResTimerA, GetHighResTimerEndCount(*HighResTimerA) * (1000000 / GetHighResTimerFrequency(*HighResTimerA)))
      
    CompilerCase #PB_OS_Linux
      
      If GetHighResTimerStopped(*HighResTimerA) = #False
        clock_gettime(#CLOCK_MONOTONIC, @GetHighResTimerEndCount(*HighResTimerA))
      EndIf
      
      SetHighResTimerStartMicroSec(*HighResTimerA, GetTimeSpecSecond(GetHighResTimerStartCount(*HighResTimerA)) * 1000000 + GetTimeSpecNanoSecond(GetHighResTimerStartCount(*HighResTimerA)) / 1000) 
      SetHighResTimerStopMicroSec(*HighResTimerA, GetTimeSpecSecond(GetHighResTimerEndCount(*HighResTimerA)) * 1000000 + GetTimeSpecNanoSecond(GetHighResTimerEndCount(*HighResTimerA)) / 1000) 
      
    CompilerCase #PB_OS_MacOS
      
      If GetHighResTimerStopped(*HighResTimerA) = #False
        SetHighResTimerEndCount(*HighResTimerA, mach_absolute_time() * GetHighResTimerMicroSecConversion(*HighResTimerA))
      EndIf
      
      SetHighResTimerStartMicroSec(*HighResTimerA, GetHighResTimerStartCount(*HighResTimerA))
      SetHighResTimerStopMicroSec(*HighResTimerA, GetHighResTimerEndCount(*HighResTimerA))
      
  CompilerEndSelect
  
  ProcedureReturn GetHighResTimerStopMicroSec(*HighResTimerA) - GetHighResTimerStartMicroSec(*HighResTimerA)
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
; <<<<< !!! WARNING - YOU ARE NOW IN A TESTING ZONE - WARNING !!! <<<<< 
; <<<<< !!! WARNING - THIS CODE SHOULD BE COMMENTED - WARNING !!! <<<<< 
; <<<<< !!! WARNING - BEFORE THE FINAL COMPILATION. - WARNING !!! <<<<< 
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 

StartHighResTimer(HResTimer.HighResTimer)

Delay(1)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(5)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(499)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(500)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(1499)
Debug HighResTimerElapsedMicroSec(HResTimer)

Delay(1501)
Debug HighResTimerElapsedMicroSec(HResTimer)

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
I don't have a Mac so I can't test this code. I hope it work !

Best regards
Guimauve

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 5:37 pm
by wilbert
I assumed you had a Mac :shock:

I tested your code but get an error message 'Too many parameters' for this line
Macro GetHighResTimerTimeBase(HighResTimerA, P_TimeBase)
You have so many macros that I don't know where to look for a solution.

A little other remark, both information from mach_timebase_info(..) and QueryPerformanceFrequency_(..) does not change while the system is running so instead of adding information to each timer, you could also use a global variable for this and run these functions only once.

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 6:16 pm
by Guimauve
wilbert wrote:I assumed you had a Mac :shock:
Of course not, I only have a old notebook with Windows XP, a more recent notebook with LinuxMint 12 and a PC also with LinuxMint 12.
wilbert wrote:I tested your code but get an error message 'Too many parameters' for this line
Macro GetHighResTimerTimeBase(HighResTimerA, P_TimeBase)
You have so many macros that I don't know where to look for a solution.
It's corrected in my previous code, It's an error in the macro definition on line 236 or something.
wilbert wrote:A little other remark, both information from mach_timebase_info(..) and QueryPerformanceFrequency_(..) does not change while the system is running so instead of adding information to each timer, you could also use a global variable for this and run these functions only once.
It's really an issue when you use many timer but when you use only one it don't change anything to have these parameters has a local field within the structure. But I can modify my code to remove them from the timer structure then set them once as a Global variables. I will do it this afternoon, now it's time to lunch.

Best regards.
Guimauve

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 6:48 pm
by wilbert
It's complaining now about *HighResTimerA not having a structure on this line
SetHighResTimerEndCount(HighResTimerA, mach_absolute_time() * GetHighResTimerMicroSecConversion(*HighResTimerA))

For myself your code is more than I need but I like your cross platform approach.
Based on your code I put together a simple single procedure ElapsedNanoseconds()
The Linux timings however seem to be inaccurate and vary quite a bit between difference runs.
I don't know if the clock_gettime function itself is to blame or if it has to do because my Linux runs on Virtualbox. :?

Code: Select all

; *** nano_time.q = ElapsedNanoseconds() ***

; EnableExplicit

CompilerSelect #PB_Compiler_OS
    
  CompilerCase #PB_OS_Windows 
    
    Global hr_timer_conv.d
    Define hr_timer_freq.q
    QueryPerformanceFrequency_(@hr_timer_freq)
    hr_timer_conv = 1e9
    hr_timer_conv / hr_timer_freq
    
    Procedure.q ElapsedNanoseconds()
      Protected counter.q
      QueryPerformanceCounter_(@counter)
      ProcedureReturn counter * hr_timer_conv
    EndProcedure
    
  CompilerCase #PB_OS_Linux
    
    #CLOCK_MONOTONIC = 1
    
    Structure timespec
      tv_sec.i
      tv_nsec.l
    EndStructure 
    
    ImportC "-lrt"
      clock_gettime(clk_id, *tp)
    EndImport
    
    Procedure.q ElapsedNanoseconds()
      Protected time.timespec
      clock_gettime(#CLOCK_MONOTONIC, @time)
      ProcedureReturn time\tv_sec * 1e9 + time\tv_nsec
    EndProcedure
    
  CompilerCase #PB_OS_MacOS
    
    Structure tb_info
      numer.l
      denom.l
    EndStructure
    
    ImportC ""
      mach_absolute_time.q()
      mach_timebase_info(*info)
    EndImport
    
    Global hr_timer_conv.d
    Define hr_tb_info.tb_info
    mach_timebase_info(@hr_tb_info)
    hr_timer_conv = hr_tb_info\numer
    hr_timer_conv / hr_tb_info\denom
    
    Procedure.q ElapsedNanoseconds()
      ProcedureReturn mach_absolute_time() * hr_timer_conv
    EndProcedure
    
CompilerEndSelect

; *** test the procedure ***

Define.q start_time, end_time

start_time = ElapsedNanoseconds()
Delay(25)
end_time = ElapsedNanoseconds()

MessageRequester("nano timing", Str(end_time - start_time))

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 7:17 pm
by Guimauve
@Wilbert

I have just try your code, the result after 6 runs
25100222
25085239
25085865
25086024
25071758
25086026
About the complain about *HighResTimerA not having a structure on this line if you check carefully you will see the pointer don't have the asterisk symbol.
If should be

Code: Select all

SetHighResTimerEndCount(*HighResTimerA, mach_absolute_time() * GetHighResTimerMicroSecConversion(*HighResTimerA))
instead of

Code: Select all

SetHighResTimerEndCount(HighResTimerA, mach_absolute_time() * GetHighResTimerMicroSecConversion(*HighResTimerA))
I will correct this.

Best regards.
Guimauve

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 7:26 pm
by wilbert
Line 401 and 402 have the same issue.
After correcting them to *HighResTimerA your code works fine on OS X :)

Thanks for your timings.
They seem more more accurate on your computer.

Re: High Resolution Timer Research

Posted: Mon Mar 05, 2012 7:37 pm
by Guimauve
wilbert wrote:Line 401 and 402 have the same issue.
After correcting them to *HighResTimerA your code works fine on OS X :)

Thanks for your timings.
They seem more more accurate on your computer.
Also corrected. It's fun to know it work on MacOS :D

It's difficult to debug a code for a specific plat-form when you don't have it. By the way I have also place your ElapsedNanoseconds() inside my toolbox, I will use it to reach a better control over the Frame rate. See http://www.purebasic.fr/english/viewtop ... ol#p334296

But I will need a NanoSleep() ou NanoDelay() function to do it.

Best regards
Guimauve