QuickTickLib 2.1.0

dotnet add package QuickTickLib --version 2.1.0
                    
NuGet\Install-Package QuickTickLib -Version 2.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="QuickTickLib" Version="2.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="QuickTickLib" Version="2.1.0" />
                    
Directory.Packages.props
<PackageReference Include="QuickTickLib" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add QuickTickLib --version 2.1.0
                    
#r "nuget: QuickTickLib, 2.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package QuickTickLib@2.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=QuickTickLib&version=2.1.0
                    
Install as a Cake Addin
#tool nuget:?package=QuickTickLib&version=2.1.0
                    
Install as a Cake Tool

QuickTick

alternate text is missing from this package README image GitHub

QuickTick is a high-precision timer library for .NET 8.0 and .NET Framework 4.8, designed for scenarios where accurate and low-latency timing is required.

It is inspired by discussions in the .NET Runtime issue #67088 and is based on the high-resolution timer implemented by Microsoft's Go team, as detailed in this blog post.

QuickTick leverages IO Completion Ports (IOCP) and NT system calls on windows to achieve precise and efficient timing without needing to fiddle with the system clock rate. It enables the creation of a Timer and the use of Sleep and Delay functions with precision below 15.6 ms, ensuring accurate timing without impacting other parts of your application.

Supported OS

QuickTick is designed primarily to improve timer resolution on Windows systems, where the default system timer has a resolution of 15.6 ms. On Linux and most other .NET 8.0 supported platforms, this limitation does not exist, so QuickTick automatically falls back to a wrapper around the base .NET timers while keeping the same interface.

Windows Support

✅ Windows 11

✅ Windows Server 2019 and newer

✅ Windows 10, version 1803 or newer

QuickTick relies on specific system APIs that are only available starting with Windows 10 (1803). This is explicitly the CreateWaitableTimerExW function which received high resolution support in Windows 10 version 1803.

On older Windows versions, QuickTick cannot function and will throw a PlatformNotSupportedException.

Linux Support

✅ Fully supported. Tested on Ubuntu 24.04.3 LTS. Expected to work on all other current Linux distributions that support .NET 8.0

QuickTick falls back to the standard .NET timers, which already provide high precision. All the functions the library provide can be used without needing to change settings.

macOS Support

⚠️ Supported, meaning the timer will run under macOS but the base .Net functions the timer falls back to are not very precise.

See the macOS timing report for more details.

For best results, use HighResQuickTickTimer with adjusted settings. See macOS timing report Comment. This effectivly burns a whole CPU core to hold a precise timing. The timing accurancy of QuickTickTiming.Sleep() and QuickTickTiming.Delay() match the native .NET function but are inprecise and can't be improved easily.

Performance Reports

This are some performance reports of the QuickTickTiming.Sleep() Function aswell as for the QuickTickTimer and the HighResQuickTickTimer including a comparison to the HighResTimer by György Kőszeg found here

OS Priority Power Setting Report Comment
Windows 11 Normal Energy Saving 📄 ⚠️ Lower accuracy than high power, but still much better than default .NET functions
Windows 11 Highest Ultimate Power 📄 ✅ Stable high-resolution timing
Windows Server 2022 Highest High Performance 📄 ✅ Stable high-resolution timing
Ubuntu 24.04.3 LTS Highest N/A 📄 ✅ Stable high-resolution timing
macOS 15.5 Sequoia Highest N/A 📄 ⚠️ Default .NET timers limit precision to around ~10 ms. HighResQuickTickTimer can help with SleepThreshold ≈ 15 ms and YieldThreshold ≈ 1.5 ms

QuickTickTimer Class

Definition

Namespace: QuickTickLib

Provides a high-resolution timer using IO Completion Ports (IOCP) and NtAssociateWaitCompletionPacket for precise timing events under windows. Similar in use to the System.Timers.Timer class.

public class QuickTickTimer : IDisposable, IQuickTickTimer

Inheritance ObjectQuickTickTimer

Implements IDisposable, IQuickTickTimer

Example Usage

The following example shows the usage of the QuickTickTimer with an interval of 500 ms.

using QuickTickLib;

class Program
{
    static void Main()
    {
        using QuickTickTimer timer = new QuickTickTimer(500);
        timer.SkipMissedIntervals = false;
        timer.AutoReset = true;
        timer.Elapsed += Timer_Elapsed;
        timer.Start();

        Thread.Sleep(5000); // Run for 5 seconds
        timer.Stop();
    }

    private static void TimerElapsed(object? sender, QuickTickElapsedEventArgs elapsedArgs)
    {
        Console.WriteLine($"Now: '{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss.ffffff}'; " +
                          $"Time since last tick: '{elapsedArgs.TimeSinceLastInterval.TotalMilliseconds}'; " +
                          $"Skipped: '{elapsedArgs.SkippedIntervals}';");
    }
}

// The example displays output like the following:
    // Now: '2025-08-19 11:29:39.882346'; Time since last tick: '4,699'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.887346'; Time since last tick: '5,0038'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.892444'; Time since last tick: '5,0977'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.897339'; Time since last tick: '4,8969'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.902344'; Time since last tick: '5,0008'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.907342'; Time since last tick: '5,0019'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.912508'; Time since last tick: '5,166'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.917512'; Time since last tick: '5,0029'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.922342'; Time since last tick: '4,8312'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.927346'; Time since last tick: '5,0031'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.932344'; Time since last tick: '4,9945'; Skipped: '0';
    // Now: '2025-08-19 11:29:39.937572'; Time since last tick: '5,2314'; Skipped: '0';

Remarks

QuickTickTimer is a timer based on IO Completion Ports for high-precision timing. Using the windows function NtAssociateWaitCompletionPacket it can go below the default windows timer resolution of 15.6 ms without needing to set the system clock rate using TimeBeginPeriod. The timer therefore has no influence on the remaining program and calls like Thread.Sleep or Task.Delay are not affected.

The QuickTickTimer class is able to be used cross-platform. However on systems that are not windows it falls back to a fallback implementation built on base .net functions, that provides the same interface. For non windows platforms the accuracy of the functions soly rely on the accurancy of the base .Net functions. For some platforms like Linux this works great. But for others there might be limitatons just as windows has them with the 15.6 ms timing. macOS for example seems to have a minimum Sleep time of around 10 ms. (See Supported Platforms)

The elapsed event is fired on a completion thread. This thread is not the same as the thread that started the timer. Make sure the execution of you elapsed event does not take longer than the interval of the timer or consider using a decoupling mechanism to process the event on a different thread. This is also true for the fallback timer which altough based on System.Timers.Timer, which schedules to thread pool has a logic built in the fallback wrap that ques the events on a single event thread.

This class implements the IDisposable interface. When you are finished using the timer, you should dispose of it to release all associated resources.

The actual timing accuracy of the timer on Windows is mostly based on the systems thread scheduler aswell as the system's kernel timing. On average the system takes around 300 µs to signal the timer thread after the interval finished. The thread that waits for the timer and handles the event code normally runs with the ThreadPriority.Normal. This is normally fine and doesn’t need to be increased. Raising the priority is only recommended if the system is under heavy load and timing accuracy is noticeably affected. A better solution to inaccurate timing is checking your windows power settings and especially the core parking feature. Core parking can drastically worsen the times the timer thread needs to wake up when being signaled. You might want to turn that off.

The QuickTickTimerImplementation supports sub-millisecond intervals; however, the maximum effective resolution is approximately 0.5 milliseconds due to Windows kernel timer limitations.
On the test machine, the minimum observed interval was around 0.518 ms. You can still specify intervals that are not exact multiples of 0.5 ms — for example, 16.666... ms for a 60 Hz timer — and the implementation will attempt to maintain that average over time.

The timer strives to keep the average interval as close as possible to the requested value, but actual intervals may vary slightly depending on system load and other conditions.
Typically, deviations are within ±0.6 ms, though in some cases they may be larger.

For the fallback timer sub millisecond intervals aren't supported due to the nature of System.Timers.Timer rounding up to the next full millisecond interval. You can still specify 0.5 ms as an interval but the timer will just run with a timing of 1 ms instead.

Constructors

QuickTickTimer(double interval)
public QuickTickTimer(double interval)

Initializes a new instance of the QuickTickTimer class with the specified interval in milliseconds.

Parameters
  • interval (Double): The timer interval in milliseconds.
Exceptions
  • ArgumentOutOfRangeException: Thrown if interval is outside the valid range:
    • For QuickTick: must be ≥ 0.5 milliseconds and ≤ int.MaxValue milliseconds
    • For Fallback: must be ≥ 1 millisecond and ≤ int.MaxValue milliseconds
  • InvalidOperationException: Thrown if system API calls fail during initialization.
  • PlatformNotSupportedException: Throw if you try to run QuickTickLib under windows versions below version 10 Build 1803.
QuickTickTimer(TimeSpan interval)
public QuickTickTimer(TimeSpan interval)

Initializes a new instance of the QuickTickTimer class with the specified interval as a TimeSpan. Be aware that using TimeSpan.FromMilliseconds() allready rounds to full milliseconds so if you want tp create a timer with sub millisecond timing you would need to create the timeSpan with TimeSpan.FromMicroseconds()

Parameters
  • interval (TimeSpan): The timer interval.
Exceptions
  • ArgumentOutOfRangeException: Thrown if interval is outside the valid range:
    • For QuickTick: must be ≥ 0.5 milliseconds and ≤ int.MaxValue milliseconds
    • For Fallback: must be ≥ 1 millisecond and ≤ int.MaxValue milliseconds
  • InvalidOperationException: Thrown if system API calls fail during initialization.
  • PlatformNotSupportedException: Throw if you try to run QuickTickLib under windows versions below version 10 Build 1803.

Properties

IsQuickTickUsed
public bool IsQuickTickUsed { get; }

Gets a value indicating whether the high-resolution QuickTick implementation is being used.

Not part of the IQuickTickTimer interface; Only available from the class directly

  • true: The timer is backed by the QuickTickTimerImplementation, which offers higher precision and lower latency on windows.
  • false: The platform does not support QuickTick, and the timer falls back to the QuickTickTimerFallback implementation. (So true on Linux for example)
Interval
public double Interval { get; set; }

Gets or sets the time, in milliseconds, between timer events.

Exceptions
  • ArgumentOutOfRangeException: Thrown if value is outside the valid range:
    • For QuickTick: must be ≥ 0.5 milliseconds and ≤ int.MaxValue milliseconds
    • For Fallback: must be ≥ 1 millisecond and ≤ int.MaxValue milliseconds
AutoReset
public bool AutoReset { get; set; }

Gets or sets whether the timer should restart after each elapsed event. Default is true.

  • true: The timer restarts automatically.
  • false: The timer stops after firing once.
Priority
public ThreadPriority Priority { get; set; }

Gets or sets the thread priority for the thread handling the timer events and event code. Default is ThreadPriority.Normal.

SkipMissedIntervals
public bool SkipMissedIntervals { get; set; }

Gets or sets whether the timer should skip missed ticks. Default is false.

  • true: When the timer event fires and the last event is still being processed the event is ignored and the SkippedIntervals counter is increased.
  • false: When the timer event fires and the last event is still being processed the event is scheduled immediatly and will start as soon as the running event is finished. Can lead to burst of events if the system or the user event code did have a hickup.

Methods

Start()
public void Start()

Starts the timer.

Exceptions
  • InvalidOperationException: Thrown if system API calls fail when setting the timer.
Stop()
public void Stop()

Stops the timer if it is currently running.

Dispose()
public void Dispose()

Stops the timer and releases all associated system resources.

Events

Elapsed
public event QuickTickElapsedEventHandler Elapsed;

Occurs when the timer interval has elapsed.

Event Arguments
  • QuickTickElapsedEventArgs: Contains information about the current interval of the timer and the SkippedTicks.
    • TimeSinceLastInterval (TimeSpan): The time since the last event was triggered. If its the first interval it is the time since the start of the timer.
    • SkippedIntervals (long): The amount of skipped intervals since the timer was last started. Is always zero if SkipMissedIntervals is disabled. It is clamped to long.MaxValue and doesnt overflow.

HighResQuickTickTimer Class

Definition

Namespace: QuickTickLib

This is a high resolution timer based on the QuickTickTiming.Sleep function aswell as the HighResTimer by György Kőszeg found here. The timer provides the same interface as the normal QuickTickTimer but functions completly different internally. In short the timer is a loop that always checks the time and according to the remaining time to the next interval either sleeps, yields the thread or spin waits. It therefore needs more CPU than the regular timer, but also has a higher precision in the timing events.

For timers with intervals over about 2.5 ms this timer behaves like the HighResTimer by György Kőszeg but needs way less CPU. This timer also fully supports sub millisecond intervals on all systems as it doesn't rely on the System.Timers.Timer implementation.

By design this timer uses a whole thread if you set a interval below around 2.5 ms as it then doesn't sleep and instead spin waits only.

public class HighResQuickTickTimer : IDisposable, IQuickTickTimer

Inheritance ObjectHighResQuickTickTimer

Implements IDisposable, IQuickTickTimer

Differences to QuickTickTimer

Priority

This timer always starts with ThreadPriority.Highest.

IsQuickTickUsed

Does not implement this property as it isn't part of the IQuickTickTimer interface. Also there is no fallback implementation for this timer. Just the way it sleeps is switched on the different operating systems.

SleepThreshold
public double SleepThreshold { get; set }

Defines the minimum time that must be available towards the next timer iteration for the thread to sleep. Default is 1.5 ms. Increasing this time can lead to better timing but increases CPU usage as the code will Yield or SpinWait instead. Must be at least 1.0 and at most int.MaxValue. YieldThreshold must always be less than or equal to SleepThreshold. Setting SleepThreshold to int.MaxValue will basically disable sleeping the thread and the timer will Yield or SpinWait the thread instead.

If a sleep is to be performed a function called MinimalSleep sleep is called. This sleeps for 1 ms using Thread.Sleep() under non windows systems and for 400 µs using a special QuickTickTiming.QuickTickSleep() function under windows. Testing for linux and windows showed, that in both cases the actual sleep time stays under 1.5 ms in over 99% of all cases which is why 1.5 ms is the default sleep time. An even safer value is 2.0 ms as in testing all minimal sleep timings stayed under 2.0 ms for both linux and windows. For systems like macOS where the accurancy of Thread.Sleep() is way worse than under linux a good value for the SleepThreshold is around 15 ms, altough it should be remembered, that this will basically use a full core.

Not part of the IQuickTickTimer interface; Only available from the class directly

YieldThreshold
public double YieldThreshold { get; set }

Defines the minimum time that must be available towards the next timer iteration to yield the thread. Default is 0.75 ms. Increasing this time can lead to better timing but increases CPU usage as the code will SpinWait instead. Must be at least 0.0 and at most the value of SleepThreshold. Setting YieldThreshold equal to SleepThreshold disables yielding (goes directly to spin wait). Setting YieldThreshold equal to 0.0 will basically disable spin waiting altough if no process is ready to run on this thread the behavior is almost the same.

Not part of the IQuickTickTimer interface; Only available from the class directly

QuickTickTiming Class

Definition

Namespace: QuickTickLib

Provides sleep and delay functions with high precision using IO Completion Ports (IOCP) and NtAssociateWaitCompletionPacket for precise timing events under windows. Running under different platforms it just falls back to the built in .net functions.

public static class QuickTickTiming

Inheritance ObjectQuickTickTiming

Methods

Sleep()
public static void Sleep(int millisecondsTimeout)

Blocks the current thread for the specified number of milliseconds. If the timeout time is less than or equal to 0 ms, the function will behave the same as Thread.Sleep() will in this situation.

Exceptions
  • ArgumentOutOfRangeException: Thrown if millisecondsTimeout less than zero.
  • InvalidOperationException: Thrown if system API calls fail during initialization.
  • PlatformNotSupportedException: Throw if you try to run QuickTickLib under windows versions below version 10 Build 1803.
Delay()
public static async Task Delay(int millisecondsDelay, CancellationToken cancellationToken = default)
public static async Task Delay(TimeSpan delay, CancellationToken cancellationToken = default)

Asynchronously blocks the current thread for the specified duration. It allows cancellation via the CancellationToken.

  • millisecondsDelay and delay specify the delay time.
  • If cancellationToken is triggered, the delay will be canceled early.
Exceptions
  • ArgumentOutOfRangeException: Thrown if millisecondsDelay or delay is less than zero.
  • InvalidOperationException: Thrown if system API calls fail during initialization.
  • TaskCanceledException: If the cancellation token was cancelled during the delay phase.
  • ObjectDisposedException: The provided cancellationToken has already been disposed.
  • PlatformNotSupportedException: Throw if you try to run QuickTickLib under windows versions below version 10 Build 1803.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.8

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.0 90 9/6/2025
2.0.0 198 8/25/2025
1.2.0 135 5/30/2025
1.1.0 559 4/12/2025
1.0.0 237 3/5/2025
0.0.1 123 2/27/2025