Creating a Hi-Res Timer

S

Scott McNair

Hi,

I'm trying to create a hi-resolution timer for an application that I'm
working on which needs to be sensitive to at least 10ms. I'd like to
have it mimic the built-in MS timer as much as possible, mainly for ease
of code re-use.

I've found several examples of how to use QueryPerformanceCounter and
QueryPerformanceFrequency to get high-precision time, and I've written
something that works fine for single iterations, but when the timer
starts to repeat itself, the app gets bogged down. I realize it's
bogging down due to recursion, but that's sort of the nature of the beast
with a timer.

Does anybody have a suggestion for how I can recurse elegantly, or
barring that, some good example code for how to implement a hi-res timer
that's MS-like?

My current code is attached:

Class HiResTimer
Declare Function QueryPerformanceCounter Lib "Kernel32" (ByRef X As
Long) As Short
Declare Function QueryPerformanceFrequency Lib "Kernel32" (ByRef X As
Long) As Short

Private iEnabled As Boolean = False
Private StartCount As Long = 0
Private CurrentCount As Long = 0
Private Frequency As Long = 0
Private iInterval As Long = 0
Private Overhead As Long = 0

Public Interval As Long = 1000

Public Property Enabled() As Boolean
Get
Return iEnabled
End Get
Set(ByVal value As Boolean)
iEnabled = value
RaiseEvent EnabledEvent()
End Set
End Property

Public Event Tick()
Private Event EnabledEvent()

Public Sub New()
If (QueryPerformanceCounter(StartCount) = 0) Then
Throw New HiResTimerException()
End If
If (QueryPerformanceCounter(CurrentCount) = 0) Then
Throw New HiResTimerException()
End If
' calculate the time just to call start and end
Overhead = (CurrentCount - StartCount)
End Sub

Private Sub EnabledSub() Handles Me.EnabledEvent
If iEnabled = True Then
'Get the interval in terms of microseconds
iInterval = Interval / 1000
If (QueryPerformanceCounter(StartCount) = 0) Then
Throw New HiResTimerException()
End If
If (QueryPerformanceCounter(CurrentCount) = 0) Then
Throw New HiResTimerException()
End If
QueryPerformanceFrequency(Frequency)
While ((CurrentCount - StartCount - Overhead) / Frequency) <
iInterval
QueryPerformanceCounter(CurrentCount)
End While
RaiseEvent Tick()
RaiseEvent EnabledEvent()
End If
End Sub
End Class

Public Class HiResTimerException
Inherits System.ApplicationException

Overrides ReadOnly Property Message() As String
Get
Return "HiResTimer: QueryPerformanceCounter[Kernel32]
returned 0"
End Get
End Property
End Class
 
S

Scott McNair

Can't you use System.Timer.Timer? Ticks are generated out of process
and it's supposedly very accurate.

Actually I performed a test that had some scary results, which prompted
this search:

* I captured the current time in a variable (StartTime).

* I created a timer with a tick of 10ms, then enabled the timer. In the
timer's ontick event, I had it increment a variable by 1.

* When the number reached 1000 (i.e. after what should have been 10
seconds, at 10ms*1000 ticks) I captured the EndTime. I then displayed
datediff between StartTime and EndTime. The process which should have
taken 10 seconds actually took more like 15.

I took it a step further and found a third-party hi-res timer component,
and had the two components "race" each other to 1000 ticks @ 10ms per
tick. The third-party component hit 1000 ticks at exactly 10 seconds, at
which point the MS timer had only reached 651 ticks, for a deficiency of
34.9%.

Then for fun I raised the count to 10,000 ticks, and lowered the per-tick
rate to 1ms (still 10sec, but at the highest granularity). In the time
it took the third-party to run 10,000 iterations (in exactly 10 seconds,
mind you) the MS timer ran a measly 671 times, for a deficiency of
93.29%.

I then altered the tick count upward, to see when it would normalize...
when I got to 100 count at 100ms, the MS timer reported about 92 ticks
out of 100 (8% deficiency). I then bumped it to 80 count at 125ms (or
1/8 of a second) and at that point it seemed to normalize.

In probably 99.9% of my coding, the MS timer would work fine.
Unfortunately, right now I'm writing an application that requires a 10ms
granularity (it's calling on hardware that requires finely tuned and
calibrated steps), and having a disparity of about 35% is unacceptable
for this application.

I suppose I could just use the third-party component, but its
implementation is kind of clunky, and I'd prefer something that mimics
the MS timer code style.

Thanks for listening to my rant. :)

-Scott
 
G

Guest

Actually I performed a test that had some scary results, which prompted
this search:

Which timer did you use? .NET has 3 timers, System.Windows.Forms.Timer
isn't accurate.
 
H

Herfried K. Wagner [MVP]

Scott McNair said:
Can't you use System.Timer.Timer? Ticks are generated out of process
and it's supposedly very accurate.

Actually I performed a test that had some scary results, which prompted
this search:

* I captured the current time in a variable (StartTime).

* I created a timer with a tick of 10ms, then enabled the timer. In the
timer's ontick event, I had it increment a variable by 1.
[Timer is not very accurate]

Well, as 'System.Windows.Forms.Timer' is the only timer which has an
'OnTick' method, you have definitely used the wrong timer:

| The Windows Forms Timer component is single-threaded, and is
| limited to an accuracy of 55 milliseconds.

Take a look at 'System.Timers.Timer' (with 'AutoReset' set to 'True') or
maybe 'System.Threading.Timer'.
 
S

Scott McNair

Which timer did you use? .NET has 3 timers, System.Windows.Forms.Timer
isn't accurate.

Ah, that's the one I used.

After I sent the last email I looked into System.Timers.Timer, but I keep
running into problems with cross-thread issues. I've used the items in
http://msdn2.microsoft.com/en-us/library/ms171728(VS.80).aspx to try to get
around this, but with not much success. My guess is that I'm calling the
thread so much (100 times a second!) that I'm stepping on my own toes by
executing the background worker before it has a chance to complete its
prior cycle.
 
G

Galen Somerville

Herfried K. Wagner said:
Scott McNair said:
Can't you use System.Timer.Timer? Ticks are generated out of process
and it's supposedly very accurate.

Actually I performed a test that had some scary results, which prompted
this search:

* I captured the current time in a variable (StartTime).

* I created a timer with a tick of 10ms, then enabled the timer. In the
timer's ontick event, I had it increment a variable by 1.
[Timer is not very accurate]

Well, as 'System.Windows.Forms.Timer' is the only timer which has an
'OnTick' method, you have definitely used the wrong timer:

| The Windows Forms Timer component is single-threaded, and is
| limited to an accuracy of 55 milliseconds.

Take a look at 'System.Timers.Timer' (with 'AutoReset' set to 'True') or
maybe 'System.Threading.Timer'.
Have you tried vbAccelerator to see if he has one similiar to "vbAccelerator
VB6 Safe High Resolution (Multi-Media) Timer "

Galen
 
S

Scott McNair

Have you tried vbAccelerator to see if he has one similiar to
"vbAccelerator VB6 Safe High Resolution (Multi-Media) Timer "

If there is one, it's extremely well-hidden on that site.

I can however import the VB6 DLL into my project and it works without a
hitch.
 
T

Tom Shelton

Hi,

I'm trying to create a hi-resolution timer for an application that I'm
working on which needs to be sensitive to at least 10ms. I'd like to
have it mimic the built-in MS timer as much as possible, mainly for ease
of code re-use.

I've found several examples of how to use QueryPerformanceCounter and
QueryPerformanceFrequency to get high-precision time, and I've written
something that works fine for single iterations, but when the timer
starts to repeat itself, the app gets bogged down. I realize it's
bogging down due to recursion, but that's sort of the nature of the beast
with a timer.

Does anybody have a suggestion for how I can recurse elegantly, or
barring that, some good example code for how to implement a hi-res timer
that's MS-like?

My current code is attached:

Class HiResTimer
Declare Function QueryPerformanceCounter Lib "Kernel32" (ByRef X As
Long) As Short
Declare Function QueryPerformanceFrequency Lib "Kernel32" (ByRef X As
Long) As Short

Private iEnabled As Boolean = False
Private StartCount As Long = 0
Private CurrentCount As Long = 0
Private Frequency As Long = 0
Private iInterval As Long = 0
Private Overhead As Long = 0

Public Interval As Long = 1000

Public Property Enabled() As Boolean
Get
Return iEnabled
End Get
Set(ByVal value As Boolean)
iEnabled = value
RaiseEvent EnabledEvent()
End Set
End Property

Public Event Tick()
Private Event EnabledEvent()

Public Sub New()
If (QueryPerformanceCounter(StartCount) = 0) Then
Throw New HiResTimerException()
End If
If (QueryPerformanceCounter(CurrentCount) = 0) Then
Throw New HiResTimerException()
End If
' calculate the time just to call start and end
Overhead = (CurrentCount - StartCount)
End Sub

Private Sub EnabledSub() Handles Me.EnabledEvent
If iEnabled = True Then
'Get the interval in terms of microseconds
iInterval = Interval / 1000
If (QueryPerformanceCounter(StartCount) = 0) Then
Throw New HiResTimerException()
End If
If (QueryPerformanceCounter(CurrentCount) = 0) Then
Throw New HiResTimerException()
End If
QueryPerformanceFrequency(Frequency)
While ((CurrentCount - StartCount - Overhead) / Frequency) <
iInterval
QueryPerformanceCounter(CurrentCount)
End While
RaiseEvent Tick()
RaiseEvent EnabledEvent()
End If
End Sub
End Class

Public Class HiResTimerException
Inherits System.ApplicationException

Overrides ReadOnly Property Message() As String
Get
Return "HiResTimer: QueryPerformanceCounter[Kernel32]
returned 0"
End Get
End Property
End Class

I'll look and see, but I wrote a timer based on the multi-media timers a
long time ago. It was based on Karl Peterson's multi-media timer
objects. The resolution is close to 1ms on most hardware. Of cousre,
it's written in C#, not vb.net...
 
G

Guest

After I sent the last email I looked into System.Timers.Timer, but I
keep running into problems with cross-thread issues. I've used the
items in http://msdn2.microsoft.com/en-us/library/ms171728(VS.80).aspx
to try to get around this, but with not much success. My guess is
that I'm calling the thread so much (100 times a second!) that I'm
stepping on my own toes by executing the background worker before it
has a chance to complete its prior cycle.

System.Timers.Timers is a high resolution timer, it should be able to
handle the accuracies you need easily. Cross threading issues can be solved
with a bit of code rearranging... shouldn't really be much of an issue?

You can also look at System.Threading.Timers too.
 
G

Guest

Ah, that's the one I used.

After I sent the last email I looked into System.Timers.Timer, but I
keep running into problems with cross-thread issues. I've used the
items in http://msdn2.microsoft.com/en-us/library/ms171728(VS.80).aspx
to try to get around this, but with not much success. My guess is
that I'm calling the thread so much (100 times a second!) that I'm
stepping on my own toes by executing the background worker before it
has a chance to complete its prior cycle.

Not sure if you saw this yet:

http://msdn.microsoft.com/msdnmag/issues/04/02/TimersinNET/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top