10

I've used this tool and noticed that my Windows Server 2008 R2 Standard has a 15 ms resolution while Windows 8 has a 1 ms resolution timer.

I would prefer to set the Timer Resolution to 1 ms on Windows Server 2008 R2 because I'm running low-latency software on it.

I've found this msdn article, but it doesn't explain how to change the Timer resolution from a C# program. How do I do that?

Brian
  • 5,069
  • 7
  • 37
  • 47
Oleg Vazhnev
  • 23,239
  • 54
  • 171
  • 305
  • I believe the timer resolution is limited on certain architectures (i.e. you can't just set it lower). [Here](http://msdn.microsoft.com/en-us/magazine/cc163996.aspx) is an article on how to implement your own high-resolution timer for Windows (with code example). – NominSim Feb 25 '13 at 16:20
  • @NominSim did you read "Obtaining and Setting Timer Resolution" article refered in the question? – Oleg Vazhnev Feb 25 '13 at 16:21
  • Yes. If you read the article I linked to, it explains how the resolution is limited according to the architecture. You can get better resolution, but there are compromises that you have to make as well. (You can't just set an arbitrary resolution without the potential of losing some accuracy). – NominSim Feb 25 '13 at 16:37
  • 1
    This blog article, [Windows Timer Resolution: Megawatts Wasted](https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/) discusses some of the pros and cons of changing the timer using `timeBeginPeriod()` and `timeEndPeriod()` and also mentions the change in Windows 8 for timer resolution. – Richard Chambers Oct 09 '15 at 12:11

2 Answers2

17

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);
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 1
    should I call this function every time system restarted? or I should call it just once? – Oleg Vazhnev Feb 25 '13 at 16:22
  • 2
    Whenever you start the system. It's not persisted - but it *is* global (i.e. it will affect all processes)! – Matthew Watson Feb 25 '13 at 16:28
  • I've used this method - it affect Sleeps, and OS timer, however Threading.Timer keeps its 15ms resolution - more details in my question here: http://stackoverflow.com/questions/23215970/system-threading-timer-vs-system-threading-thread-sleep-resolution-net-timer – Jan Apr 22 '14 at 14:48
  • 1
    To be precise, the effect of TimeBeginPeriod lasts until you call TimeEndPeriod or until your process ends. So, you typically call it at process initialization time. If you don't need the timer frequency raised for a while then you should, ideally, call TimeEndPeriod to avoid wasting power, and then call TimeBeginPeriod when you need it raised again. – Bruce Dawson Oct 14 '15 at 22:13
11

Better code to implement this would be:

using System;
using System.Runtime.InteropServices;
using System.Threading;

internal sealed class TimePeriod : IDisposable
{
    private const string WINMM = "winmm.dll";

    private static TIMECAPS timeCapabilities;

    private static int inTimePeriod;

    private readonly int period;

    private int disposed;

    [DllImport(WINMM, ExactSpelling = true)]
    private static extern int timeGetDevCaps(ref TIMECAPS ptc, int cbtc);

    [DllImport(WINMM, ExactSpelling = true)]
    private static extern int timeBeginPeriod(int uPeriod);

    [DllImport(WINMM, ExactSpelling = true)]
    private static extern int timeEndPeriod(int uPeriod);

    static TimePeriod()
    {
        int result = timeGetDevCaps(ref timeCapabilities, Marshal.SizeOf(typeof(TIMECAPS)));
        if (result != 0)
        {
            throw new InvalidOperationException("The request to get time capabilities was not completed because an unexpected error with code " + result + " occured.");
        }
    }

    internal TimePeriod(int period)
    {
        if (Interlocked.Increment(ref inTimePeriod) != 1)
        {
            Interlocked.Decrement(ref inTimePeriod);
            throw new NotSupportedException("The process is already within a time period. Nested time periods are not supported.");
        }

        if (period < timeCapabilities.wPeriodMin || period > timeCapabilities.wPeriodMax)
        {
            throw new ArgumentOutOfRangeException("period", "The request to begin a time period was not completed because the resolution specified is out of range.");
        }

        int result = timeBeginPeriod(period);
        if (result != 0)
        {
            throw new InvalidOperationException("The request to begin a time period was not completed because an unexpected error with code " + result + " occured.");
        }

        this.period = period;
    }

    internal static int MinimumPeriod
    {
        get
        {
            return timeCapabilities.wPeriodMin;
        }
    }

    internal static int MaximumPeriod
    {
        get
        {
            return timeCapabilities.wPeriodMax;
        }
    }

    internal int Period
    {
        get
        {
            if (this.disposed > 0)
            {
                throw new ObjectDisposedException("The time period instance has been disposed.");
            }

            return this.period;
        }
    }

    public void Dispose()
    {
        if (Interlocked.Increment(ref this.disposed) == 1)
        {
            timeEndPeriod(this.period);
            Interlocked.Decrement(ref inTimePeriod);
        }
        else
        {
            Interlocked.Decrement(ref this.disposed);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct TIMECAPS
    {
        internal int wPeriodMin;

        internal int wPeriodMax;
    }
}

Use it via:

using (new TimePeriod(1))
{
    ////...
}
nick.lowe
  • 199
  • 1
  • 5