none
High precision timers in C# RRS feed

  • Question

  • I am currently in need of a high precision timer.

    The implemented timers found in .NET are not sufficient.

    I have done some research and found that using HPET timers are possible with newer hardware (which I am targetting my application at).
    Previously the way to go was with the multimedia timer (found in winmm.dll), but now, it seems, the mm-timer has been superseded by the TimerQueueTimer (found in kernel32.dll).

    However, while wrapping the TimerQueueTimer in C#, I found that it was not as precise as I had hoped. When setting it to fire every 1 ms, the actual result (i.e. the time between the timer's callback function is invoked) is more like 2 ms.

    In order for a video, audio or MIDI file to be played back, there must exist a timer that will give a high degree of accuracy.
    So I was thinking, it the TimerQueueTimer the most precise timer available i Windows? Is there a DirectX timer that is more precise? Is there another way of generating high precision periodical events? Also I read that ASIO (audio stream I/O) is very low latency (~1 ms). What timer does ASIO use?
    Thursday, February 4, 2010 11:55 AM

All replies

  • I think the StopWatch class is what your looking for. System.Diagnostics.StopWatch.

    That is using the QueryPerformanceFrequency and QueryPerformanceCounter functions for the timing.
    Thursday, February 4, 2010 12:25 PM
  • I am using StopWatch to measure. I need something that will fire at precise intervals.
    Thursday, February 4, 2010 12:27 PM
  • Precise triggering isn't possible at application level (as far as I know). I believe at driver level there are possibilities, but not at application level. The ASIO is probably coming with a device driver to do the timing stuff.


    Thursday, February 4, 2010 12:35 PM
  • If so, how are videos played back? It would require a rather accurate timer, right?
    Friday, February 5, 2010 8:42 AM
  • Hi Hobz,
    There is an article which is a good start point @ http://www.mvps.org/directx/articles/selecting_timer_functions.htm
    Please help us improve this community forum for visitors by marking the replies as answers if they help and unmarking them if they provide no help.
    Thanks.
    Friday, February 5, 2010 9:08 AM
  • "There are a variety of functions available for dealing with timing issues, but they basically fall into two categories :

    Functions that you can call to return a readout of the current time.
    Timer services, that can be instructed to send a message to your application or execute a callback function after a specified period.

    For the purpose of this article we will be dealing with the former."

     

    So again, we are dealing with measuring time.

    Friday, February 5, 2010 9:48 AM
  • I mean following section:

    "timeGetTime
    This function provides a rock steady 1 millisecond time base, and is sufficient for timing applications that do not require a resolution of below a millisecond.
    Note, however, that it also has the longest execution time of any of these functions, so it should not be used for polling in a tight loop."


    Please help us improve this community forum for visitors by marking the replies as answers if they help and unmarking them if they provide no help.
    Thanks.

    Friday, February 5, 2010 8:29 PM
  • From http://msdn.microsoft.com/en-us/library/dd757629%28VS.85%29.aspx :
    "The timeGetTime function retrieves the system time, in milliseconds. The system time is the time elapsed since Windows was started."
    So it is not a timer.

    Furthermore it resides in winmm.dll which also has timeSetEvent which is obsolete according to http://msdn.microsoft.com/en-us/library/dd757634%28VS.85%29.aspx
    Wednesday, February 10, 2010 1:35 PM
  • Can you not just go back to using a Windows Multimedia timer?

    Here's an article about using it from .Net (apologies if you already know how to do that!):

    http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

    But as someone already mentioned, you're not going to be able to guarantee really precise timings from user code - you need device driver level stuff to do that.

    Things like Windows Media Player don't really need such precise timings - they send buffers full of data to the audio device driver for playing, and that device driver handles the precise timings. All Windows Media Player needs to do is supply a new buffer full of data before the old one has been used up; this is a standard double-buffered (or more than double-buffered) mechanism.
    Wednesday, February 10, 2010 1:46 PM
  • You can try using QueryPerformanceCounter (found in kernel.dll).
    Here is a tutorial and a ready to use class code to use it in C# -

    http://www.codeproject.com/KB/cs/highperformancetimercshar.aspx
    Wednesday, February 10, 2010 2:26 PM
  • You can try using QueryPerformanceCounter (found in kernel.dll).
    Here is a tutorial and a ready to use class code to use it in C# -

    http://www.codeproject.com/KB/cs/highperformancetimercshar.aspx
    That's what's used by Stopwatch, and has already been rejected. He wants an accurate periodic callback mechanism, not a timing mechanism.
    Wednesday, February 10, 2010 2:45 PM
  • Interesting stuff on the audio buffer.

    I suppose I could use the MM timer, but I guess it has become obsolete for a reason. From http://blogs.msdn.com/larryosterman/archive/2005/09/08/462477.aspx "[...] In fact, timeSetEvent will call PulseEvent API, which is fundamentally flawed ."

    What about the displaying of images in videos?
    Wednesday, February 10, 2010 3:36 PM
  • I suspect videos are also handled via double-buffered writing via a driver function.

    Maybe something here will be interesting: http://msdn.microsoft.com/en-us/library/aa480220.aspx

    An exerpt:

    First, WDDM enables Windows Vista and running applications to queue frames to be presented on the GPU. Second, working closely with the queuing feature is a feedback mechanism that determines when frames are presented. Together, these two features can immensely improve the quality of video playback by constantly maintaining the synchronicity between audio and video presentations, thus improving video playback and reducing video glitching substantially.
    Wednesday, February 10, 2010 3:47 PM
  • C# and other garbage collected languages are nondeterministic.  In other words performance is random at a precision of < 10 milliseconds or so. 

    Even if you implement a callback timer with a excellent resolution (such as one based off of Stopwatch on high resolution capable hardware), the CLR won't be able to offer you the real time precision you desire.  I suggest you either follow Matthew Watson's advice or stop using a GC language for real time programming.
    BrianMackey.NET
    Wednesday, February 10, 2010 6:07 PM
  • The MM methods are wrapped win32 API. You pass a delegate to the API which handles the callback. As I stated in my initial post, I can get steady callbacks at 2 ms.

    Tuesday, February 16, 2010 1:11 PM
  • Is it possible to see your code for achieving this?  I would like see if it is applicable for a timer that does not need to be as accurate as what you are looking for.

    Thanks in advance.
    Tuesday, February 16, 2010 3:16 PM
  • Sure. The steadiness is an average. Actually it's more like 1.7 ms.
    namespace PrecisionTimers
    {
        public class TimerQueueTimer : IDisposable
        {
    
            IntPtr phNewTimer; // Handle to the timer.
    
            #region Win32 TimerQueueTimer Functions
    
            [DllImport("kernel32.dll")]
            static extern bool CreateTimerQueueTimer(
                out IntPtr phNewTimer,          // phNewTimer - Pointer to a handle; this is an out value
                IntPtr TimerQueue,              // TimerQueue - Timer queue handle. For the default timer queue, NULL
                WaitOrTimerDelegate Callback,   // Callback - Pointer to the callback function
                IntPtr Parameter,               // Parameter - Value passed to the callback function
                uint DueTime,                   // DueTime - Time (milliseconds), before the timer is set to the signaled state for the first time 
                uint Period,                    // Period - Timer period (milliseconds). If zero, timer is signaled only once
                uint Flags                      // Flags - One or more of the next values (table taken from MSDN):
                                                // WT_EXECUTEINTIMERTHREAD 	The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.
                                                // WT_EXECUTEINIOTHREAD 	The callback function is queued to an I/O worker thread. This flag should be used if the function should be executed in a thread that waits in an alertable state.
    
                                                // The callback function is queued as an APC. Be sure to address reentrancy issues if the function performs an alertable wait operation.
                                                // WT_EXECUTEINPERSISTENTTHREAD 	The callback function is queued to a thread that never terminates. This flag should be used only for short tasks or it could affect other timer operations.
    
                                                // Note that currently no worker thread is persistent, although no worker thread will terminate if there are any pending I/O requests.
                                                // WT_EXECUTELONGFUNCTION 	Specifies that the callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
                                                // WT_EXECUTEONLYONCE 	The timer will be set to the signaled state only once.
                );
    
            [DllImport("kernel32.dll")]
            static extern bool DeleteTimerQueueTimer(
                IntPtr timerQueue,              // TimerQueue - A handle to the (default) timer queue
                IntPtr timer,                   // Timer - A handle to the timer
                IntPtr completionEvent          // CompletionEvent - A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.
                );
    
    
            [DllImport("kernel32.dll")]
            static extern bool DeleteTimerQueue(IntPtr TimerQueue);
    
            [DllImport("kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            static extern bool CloseHandle(IntPtr hObject);
    
            #endregion
    
            public delegate void WaitOrTimerDelegate(IntPtr lpParameter, bool timerOrWaitFired); 
    
            public TimerQueueTimer()
            {
    
            }
    
            public void Create(uint dueTime, uint period, WaitOrTimerDelegate callbackDelegate)
            {
                IntPtr pParameter = IntPtr.Zero;
    
                bool success = CreateTimerQueueTimer(
                    // Timer handle
                    out phNewTimer,
                    // Default timer queue. IntPtr.Zero is just a constant value that represents a null pointer.
                    IntPtr.Zero,
                    // Timer callback function
                    callbackDelegate,
                    // Callback function parameter
                    pParameter,
                    // Time (milliseconds), before the timer is set to the signaled state for the first time.
                    dueTime,
                    // Period - Timer period (milliseconds). If zero, timer is signaled only once.
                    period,
                    (uint)Flag.WT_EXECUTEINIOTHREAD);
    
                if (!success)
                    throw new QueueTimerException("Error creating QueueTimer");
            }
    
            public void Delete()
            {
                //bool success = DeleteTimerQueue(IntPtr.Zero);
                bool success = DeleteTimerQueueTimer(
                    IntPtr.Zero, // TimerQueue - A handle to the (default) timer queue
                    phNewTimer,  // Timer - A handle to the timer
                    IntPtr.Zero  // CompletionEvent - A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.
                    );
                int error = Marshal.GetLastWin32Error();
                //CloseHandle(phNewTimer);
            }
    
            private enum Flag
            {
                WT_EXECUTEDEFAULT = 0x00000000,
                WT_EXECUTEINIOTHREAD = 0x00000001,
                //WT_EXECUTEINWAITTHREAD       = 0x00000004,
                WT_EXECUTEONLYONCE = 0x00000008,
                WT_EXECUTELONGFUNCTION = 0x00000010,
                WT_EXECUTEINTIMERTHREAD = 0x00000020,
                WT_EXECUTEINPERSISTENTTHREAD = 0x00000080,
                //WT_TRANSFER_IMPERSONATION    = 0x00000100
            }
    
            #region IDisposable Members
    
            void IDisposable.Dispose()
            {
                Delete();
            }
    
            #endregion
        }
    
        public class QueueTimerException : Exception
        {
            public QueueTimerException(string message) : base(message)
            {
            }
    
            public QueueTimerException(string message, Exception innerException) : base(message, innerException)
            {
            }
        }
    }
    
    You can use the class as:

    TimerQueueTimer qt;
                qt = new TimerQueueTimer();
                TimerQueueTimer.WaitOrTimerDelegate CallbackDelete = new TimerQueueTimer.WaitOrTimerDelegate(QueueTimerCallback);
                qt.Create(uint.Parse(textBox2.Text), uint.Parse(textBox1.Text), CallbackDelete);
    
    
    With a callback delegate as
    private void QueueTimerCallback(IntPtr pWhat, bool success)
    {
    //...
    }
    I just have the Callback method output a stopwatch.elapsed.
    Tuesday, February 16, 2010 3:53 PM
  • Hello Hobz

    Have you found a solution to this other than buffering or double buffering? I have a similar issue (I am writing an app on .NET that will be used for testing and precise timing is needed).  I have tried the MMTimer but the resolution for firing events is pretty bad. see link for results

    http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx?msg=3498393#xx3498393xx

     

    I will be trying out the comments/suggestion made in this blog.. http://blogs.msdn.com/b/larryosterman/archive/2005/09/08/462477.aspx?PageIndex=2#comments

     

    I can appreciate your your help

    tnx

    Tuesday, July 6, 2010 1:43 PM
  • Hello Zero (Or One)

    Sorry for the late reply.

    I found that precise timing is not a reliable feature of the Win API.

    Depending on your precision requirements, you could probably do some continuous DateTime polling in a while loop and then do something on appropriate intervals.

     

    On a side note, I think the MM timers are legacy, and replaced by high precision event timers (HPET). I read the Intel specification on these babies, and they might improve accuracy.

    However, I am not sure that this is entirely true (the legacy/replacement part), nor that if they do, that HPET's are implemented or just works like a MM timer.

    Thursday, July 15, 2010 7:53 AM
  • Hi,

    I am trying to use your code to create a high performance timer, but I am getting a stack overflow exception. Moreover, the timer interval is varying all the time, when I have specified it to be 3 milliseconds. Is there anything that I am missing.

    Thank you in advance

    /SuraLK


    suralk
    Sunday, August 21, 2011 6:01 AM
  • may be http://msdn.microsoft.com/en-us/library/system.threading.timercallback.aspx can help you out
    Mark Answered, if it solves your question
    Rohit Arora
    Sunday, August 21, 2011 10:59 AM
  • Hi Hobz, 2+ years after your thread, did you find any solution?

    I think I got you. You need a deeper level soultion. Is 25 kHz enough resolution for you? that is a 40 microsecond interval... don't confuse: 40 microseconds, that is 0,04 milliseconds resolution.

    in fact you can up to 100kHz or more...

    If you are still alive tell me to give you a hint.



    • Edited by RodSTAR Friday, August 3, 2012 3:58 PM
    • Proposed as answer by Me_Ta Monday, October 1, 2012 9:19 AM
    Friday, August 3, 2012 3:54 PM
  • Hi,

    Real-time programming like yours require an appropiate language like C, C++ or Assembler.

    CLR is not up to that kind of task


    Sebastian Sajaroff Senior DBA Pharmacies Jean Coutu

    Friday, August 3, 2012 4:25 PM
  • Hey RodSTAR, I would be highly interested in how that would work out. If you could give me that hint, I'd be glad :)

    Cheers

    Thursday, September 20, 2012 11:43 AM
  • Hi Hobz, 2+ years after your thread, did you find any solution?

    I think I got you. You need a deeper level soultion. Is 25 kHz enough resolution for you? that is a 40 microsecond interval... don't confuse: 40 microseconds, that is 0,04 milliseconds resolution.

    in fact you can up to 100kHz or more...

    If you are still alive tell me to give you a hint.



    Hi RodSTAR, I'm also interested to know how you achieved this. Thanks in advance for any help! :)
    Wednesday, September 26, 2012 4:15 PM
  • Hey can you give me also a hint? :)
    Monday, October 1, 2012 7:39 AM
  • RodSTAR,

    Could you favor us all with your promised hint if you wouldn’t mind? It would be greatly appreciated.
    Thursday, December 6, 2012 12:17 AM
  • You say that you use the Stopwatch class for timing and you don't need a timer but rather a way to trigger at precise intervals. What is stopping you from using the Stopwatch ElapsedTicks property in comparison with the Frequency field to determine fairly precise intervals. I have a while loop doing this and updating 3-5 values each loop iteration and after exiting the loop it gives the timing diagnostics. This method, although consuming a considerable amount of CPU with the while loop, seems to be accurate at timing down to about 100 microseconds or less which is well under the requested accuracy of 1 ms. Of course this accuracy will diminish as you add more processes on top of the timing thread, but I think it will take quite a bit to reduce accuracy to the millisecond time-scale. Could you possibly use a method like this? Or is this really inefficient code? I am fairly new to programming.

    Edit: I just ran this on another computer and the accuracy is only about 20 ms :<. I realize now this is due to more activity inside the while loop. If there is only the update to tickNumb in the loop its accuracy is still well under 100 microseconds (more like 10 microseconds). I am still unsure how this will be effected by parallel threading since I can't figure out how to do so.

    //define a wait duration
    double stepDuration = 0.0001 //100 microseconds

    //high speed timer
    Stopwatch clock = new Stopwatch(); //gets clock rate, ticks per sec
    long freq = Stopwatch.Frequency;

    //number of ticks to elapse before exiting loop
    double ticksPerStep = (double)freq * stepDuration;

    long preTick, posTick;//holds current tick number clock.Restart();//start clock preTick = scanClock.ElapsedTicks;//tick before wait

    //wait until tick number passes number of ticks to wait while (clock.ElapsedTicks < ticksPerStep) { //wait------ }

    postTick = clock.ElapsedTicks;//tick after wait

    MessageBox.Show(" execution time: " + ((double)(postTick-preTick) / (double)Stopwatch.Frequency).ToString() + "\n\n elapsed ticks in wait loop: " + (postTick-preTick).ToString() + "\n\n ticks per step (compare with elapsed ticks): " + ticksPerStep + "\n\n clock frequency: " + Stopwatch.Frequency.ToString());

    I've found that this does work when put in to a BackgroundWorker in the DoWork function. Then if I want it to time another cycle concurrently I have a recursive call to RunWorkerAsync() and an exit condition in RunWorkerCompleted. And it indeed seems to have accurate timing to at least 100 microseconds.


    • Proposed as answer by Diggy Doggit Thursday, January 10, 2013 8:15 PM
    • Edited by Diggy Doggit Thursday, January 10, 2013 8:16 PM
    Monday, January 7, 2013 12:36 AM
  • You say that you use the Stopwatch class for timing and you don't need a timer but rather a way to trigger at precise intervals. What is stopping you from using the Stopwatch ElapsedTicks property in comparison with the Frequency field to determine fairly precise intervals. I have a while loop doing this and updating 3-5 values each loop iteration and after exiting the loop it gives the timing diagnostics. This method, although consuming a considerable amount of CPU with the while loop, seems to be accurate at timing down to about 100 microseconds or less which is well under the requested accuracy of 1 ms. Of course this accuracy will diminish as you add more processes on top of the timing thread, but I think it will take quite a bit to reduce accuracy to the millisecond time-scale. Could you possibly use a method like this? Or is this really inefficient code? I am fairly new to programming.

    Edit: I just ran this on another computer and the accuracy is only about 20 ms :<. I realize now this is due to more activity inside the while loop. If there is only the update to tickNumb in the loop its accuracy is still well under 100 microseconds (more like 10 microseconds). I am still unsure how this will be effected by parallel threading since I can't figure out how to do so.

    //define a wait duration
    double stepDuration = 0.0001 //100 microseconds

    //high speed timer
    Stopwatch clock = new Stopwatch(); //gets clock rate, ticks per sec
    long freq = Stopwatch.Frequency;

    //number of ticks to elapse before exiting loop
    double ticksPerStep = (double)freq * stepDuration;

    long preTick, posTick;//holds current tick number clock.Restart();//start clock preTick = scanClock.ElapsedTicks;//tick before wait

    //wait until tick number passes number of ticks to wait while (clock.ElapsedTicks < ticksPerStep) { //wait------ }

    postTick = clock.ElapsedTicks;//tick after wait

    MessageBox.Show(" execution time: " + ((double)(postTick-preTick) / (double)Stopwatch.Frequency).ToString() + "\n\n elapsed ticks in wait loop: " + (postTick-preTick).ToString() + "\n\n ticks per step (compare with elapsed ticks): " + ticksPerStep + "\n\n clock frequency: " + Stopwatch.Frequency.ToString());

    I've found that this does work when put in to a BackgroundWorker in the DoWork function. Then if I want it to time another cycle concurrently I have a recursive call to RunWorkerAsync() and an exit condition in RunWorkerCompleted. And it indeed seems to have accurate timing to at least 100 microseconds.


    Take a look at the Microsecond and Millisecond C# Timer by ken.loveday at codeproject.net: http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer
    Same idea but its use is similar to the .NET System.Timers.Timer class.

    It's still not ideal since Windows is not a real time operating system so intervals can still take to long but the MicroTimer class offers two options to handle this as good as possible. It also offers other nice features like the CallbackFunctionExecutionTime that gives you the time it took to execute.


    • Edited by d3b Wednesday, August 14, 2013 8:13 AM
    Tuesday, August 13, 2013 12:08 PM
  • Hi RodSTAR,

    3+ years now, and no, I still haven't found a solution. I've used the CLR timer with some ugly hacks, but without it being more precise.

    What hints do you have?

    Monday, September 9, 2013 4:04 PM
  • If so, how are videos played back? It would require a rather accurate timer, right?

    Videos are typically played in a DirectShow graph.  Study DirectShow to learn how. 
    Monday, September 9, 2013 5:13 PM
  • Hi Hobz,

    I am agree with you that no direct solution for C# developers. I had created a workaround. C# code and results are on:

    http://ideveloper-dotnet.blogspot.com/2013/07/real-time-timer-in-c.html

    Monday, April 14, 2014 11:18 AM
  • THANKYOU so much, that works quite well!!
    I was struggling at even 100ms periods, timing was all over the shop when GC/etc or something kicked in...

    Regards

    Paul


    PTSS

    Saturday, December 14, 2019 7:48 AM