how to set timer resolution from C# to 1 ms?
You can try this:
public static class WinApi
{
/// <summary>TimeBeginPeriod(). See the Windows API documentation for details.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), SuppressUnmanagedCodeSecurity]
[DllImport("winmm.dll", EntryPoint="timeBeginPeriod", SetLastError=true)]
public static extern uint TimeBeginPeriod(uint uMilliseconds);
/// <summary>TimeEndPeriod(). See the Windows API documentation for details.</summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1401:PInvokesShouldNotBeVisible"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), SuppressUnmanagedCodeSecurity]
[DllImport("winmm.dll", EntryPoint="timeEndPeriod", SetLastError=true)]
public static extern uint TimeEndPeriod(uint uMilliseconds);
}
And use it like this:
WinApi.TimeBeginPeriod(1);
And to go back to how it was:
WinApi.TimeEndPeriod(1);
How to setup timer resolution to 0.5 ms?
NtSetTimerResolution
Example code:
#include <windows.h>
extern "C" NTSYSAPI NTSTATUS NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
...
ULONG currentRes;
NtSetTimerResolution(5000, TRUE, ¤tRes);
Link with ntdll.lib
.
Why are .NET timers limited to 15 ms resolution?
Perhaps the document linked here explains it a bit. It's kinda dry so I only browsed it quickly :)
Quoting the intro:
The system timer resolution determines
how frequently Windows performs two
main actions:
- Update the timer tick
count if a full tick has elapsed.- Check whether a scheduled timer object
has expired.
A timer tick is a notion of elapsed
time that Windows uses to track the
time of day and thread quantum times.
By default, the clock interrupt and
timer tick are the same, but Windows
or an application can change the clock
interrupt period.The default timer
resolution on Windows 7 is 15.6
milliseconds (ms). Some applications
reduce this to 1 ms, which reduces the
battery run time on mobile systems by
as much as 25 percent.
Originally from: Timers, Timer Resolution, and Development of Efficient Code (docx).
Timer minimum limitation
1 ms or 0.5 ms?
Hans is right, the multimedia timer interface is able to provide down to 1 ms resolution.
See About Multimedia Timers (MSDN), Obtaining and Setting Timer Resolution (MSDN), and this answer for more details about timeBeginPeriod
. Note: Don't forget to call the timeEndPeriod to switch back to the default timer resolution when done.
How to do:
#define TARGET_RESOLUTION 1 // 1-millisecond target resolution
TIMECAPS tc;
UINT wTimerRes;
if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
{
// Error; application can't continue.
}
wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
timeBeginPeriod(wTimerRes);
// do your stuff here at approx. 1 ms timer resolution
timeEndPeriod(wTimerRes);
Note: This procedure is available to other processes as well and the obtained resolution applies system wide. The highest resolution requested by any process will be active, mind the consequences.
However, you may obtain 0.5 ms resolution by means of the hidden API NtSetTimerResolution()
.NtSetTimerResolution
is exported by the native Windows NT library NTDLL.DLL. See How to set timer resolution to 0.5ms ? on MSDN. Nevertheless, the true achievable resolution is determined by the underlying hardware. Modern hardware does support 0.5 ms resolution.
Even more details are found in Inside Windows NT High Resolution Timers.
How to do:
#define STATUS_SUCCESS 0
#define STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000245
// after loading NtSetTimerResolution from ntdll.dll:
ULONG RequestedResolution = 5000;
ULONG CurrentResolution = 0;
// 1. Requesting a higher resolution
if (NtSetTimerResolution(RequestedResolution,TRUE,&CurrentResolution) != STATUS_SUCCESS) {
// The call has failed
}
printf("CurrentResolution [100 ns units]: %d\n",CurrentResolution);
// this will show 5000 on more modern platforms (0.5ms!)
// do your stuff here at 0.5 ms timer resolution
// 2. Releasing the requested resolution
switch (NtSetTimerResolution(RequestedResolution,FALSE,&CurrentResolution) {
case STATUS_SUCCESS:
printf("The current resolution has returned to %d [100 ns units]\n",CurrentResolution);
break;
case STATUS_TIMER_RESOLUTION_NOT_SET:
printf("The requested resolution was not set\n");
// the resolution can only return to a previous value by means of FALSE
// when the current resolution was set by this application
break;
default:
// The call has failed
}
Note: The functionality of NtSetTimerResolution
is basically mapped to the functions timeBeginPeriod
and timeEndPeriod
by using the bool value Set
(see Inside Windows NT High Resolution Timers for more details about the scheme and all its implications). However, the multimedia suite limits the granularity to milliseconds and NtSetTimerResolution
allows to set sub-millisecond values.
Precise capture loop
You see a 15 ms delays when sleeping for 15ms and a 30 ms delays when sleeping for 16ms because SpinWait uses under the hood Environment.TickCount which relies on the system clock which has apparently on your system a 15ms resolution.
You can set the system wide timer resolution by using timeBeginPeriod.
See
- https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod
and
- https://randomascii.wordpress.com/2020/10/04/windows-timer-resolution-the-great-rule-change/
for more in depth information about the clock resolution. You can check your current sytem wide timer resolution with clockres from sysinternals.
- https://learn.microsoft.com/en-us/sysinternals/downloads/clockres
See example output:
C:>clockres
Clockres v2.1 - Clock resolution display utility
Copyright (C) 2016 Mark Russinovich
Sysinternals
Maximum timer interval: 15.625 ms
Minimum timer interval: 0.500 ms
**Current timer interval: 15.625 ms**
When a WPF application is running (e.g. Visual Studio)
Maximum timer interval: 15.625 ms
Minimum timer interval: 0.500 ms
**Current timer interval: 1.000 ms**
then you get a 1ms resolution because every WPF application changes the clock resolution to 1ms. This is also used by some guys as workaround to "fix" the issue.
Repeat two tasks every 0.1 ms and 0.3 ms (Systems.Timer.Timer not precise enough)
It is ugly, but you may have some chance using the stopwatch ElapsedTicks.
Stopwatch w = new Stopwatch()
long nextTick = 0;
long ticksPerMicroSecond = 1000000L / Stopwatch.Frequency; // Number of ticks per microsec.
long periodInTicks = ticksPerMicroSecond / 100; // Ticks per 0.1ms
while(true)
{
long currentTick = w.ElapsedTicks.
if (currentTick > nextTick)
{
nextTick = currentTick + periodInTicks;
// Do something here.
}
}
It is as ugly as it can get, and you get a while(true) loop, that will probably be using 100% cpu for free (better run this on a multi core, then).
You may want to benchmark the thing a bit (look at examples in Stopwatch.Frequency msdn doc), to see how precise your timer can be, and see the drift you get from being late with your inner function. You may also want to enable background gc to avoid GC blocking your thread. While this shows it is dable in code, I'm really not sure you can get your function run in less than 0.1ms.
High resolution timer in C#
There is nothing built into the .NET framework that I am aware of. Windows has a mechanism for high resolution timer events via the Multimedia Timer API. Below is a quick example I whipped up which seems to do the job. There are also seems to be a good example here.
I will note that this API changes system wide settings that can degrade system performance, so buyer beware. For testing purposes, I would recommend keeping track of how often the timer is firing to verify the timing is similar to the device you are trying to simulate. Since windows is not a real-time OS, the load on your system may cause the MM timer be delayed resulting in gaps of 100 ms that contain 100 events in quick succession, rather than 100 events spaced 1 ms apart. Some additional reading on MM timers.
class Program
{
static void Main(string[] args)
{
TestThreadingTimer();
TestMultimediaTimer();
}
private static void TestMultimediaTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new MultimediaTimer() { Interval = 1 })
{
timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds);
s.Start();
timer.Start();
Console.ReadKey();
timer.Stop();
}
}
private static void TestThreadingTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new Timer(o => Console.WriteLine(s.ElapsedMilliseconds), null, 0, 1))
{
s.Start();
Console.ReadKey();
}
}
}
public class MultimediaTimer : IDisposable
{
private bool disposed = false;
private int interval, resolution;
private UInt32 timerId;
// Hold the timer callback to prevent garbage collection.
private readonly MultimediaTimerCallback Callback;
public MultimediaTimer()
{
Callback = new MultimediaTimerCallback(TimerCallbackMethod);
Resolution = 5;
Interval = 10;
}
~MultimediaTimer()
{
Dispose(false);
}
public int Interval
{
get
{
return interval;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
interval = value;
if (Resolution > Interval)
Resolution = value;
}
}
// Note minimum resolution is 0, meaning highest possible resolution.
public int Resolution
{
get
{
return resolution;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
resolution = value;
}
}
public bool IsRunning
{
get { return timerId != 0; }
}
public void Start()
{
CheckDisposed();
if (IsRunning)
throw new InvalidOperationException("Timer is already running");
// Event type = 0, one off event
// Event type = 1, periodic event
UInt32 userCtx = 0;
timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1);
if (timerId == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
public void Stop()
{
CheckDisposed();
if (!IsRunning)
throw new InvalidOperationException("Timer has not been started");
StopInternal();
}
private void StopInternal()
{
NativeMethods.TimeKillEvent(timerId);
timerId = 0;
}
public event EventHandler Elapsed;
public void Dispose()
{
Dispose(true);
}
private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
var handler = Elapsed;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
private void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException("MultimediaTimer");
}
private void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (IsRunning)
{
StopInternal();
}
if (disposing)
{
Elapsed = null;
GC.SuppressFinalize(this);
}
}
}
internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);
internal static class NativeMethods
{
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType);
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
internal static extern void TimeKillEvent(UInt32 uTimerId);
}
High resolution timer
I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255
It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!
Related Topics
Is It Necessary to Manually Close and Dispose of SQLdatareader
Windows.Forms.Timer or System.Threading.Timer
How to Convert Datatable to JSON String Using JSON.Net
Mysqlcommand Command.Parameters.Add Is Obsolete
Creating Wcf Channelfactory<T>
Large Switch Statements: Bad Oop
Setting Up Hook on Windows Messages
Single App.Config Multi-Project C#
Open Xml Reading from Excel File
Deleting Specific Rows from Datatable
"Updatesourcetrigger=Propertychanged" Equivalent for a Windows Phone 7 Textbox
Icecast 2: Protocol Description, Streaming to It Using C#
Outofmemoryexception While Populating Memorystream: 256Mb Allocation on 16Gb System
Test If String Is a Guid Without Throwing Exceptions