Skip to content

Instantly share code, notes, and snippets.

@dwcullop
Last active May 15, 2019 23:05
Show Gist options
  • Save dwcullop/43369ac68a391383fc2d9e876ba9f9c1 to your computer and use it in GitHub Desktop.
Save dwcullop/43369ac68a391383fc2d9e876ba9f9c1 to your computer and use it in GitHub Desktop.
Simple High Resolution Timer for C# - Uses Win32 APIs to give you microsecond accuracy timer (dependent on CPU frequency)
using System;
using System.Runtime.InteropServices;
namespace DareWare.Utils
{
/// <summary>
/// High Resolution Timer Class. Uses the Win32 API to create a timer
/// that is as accurate as the system clock, down to microseconds
/// </summary>
public class HiResTimer : IComparable<HiResTimer>, IEquatable<HiResTimer>
{
#region Public Properties
/// <summary>
/// Indicates the System's Counter value when the timer was started (or reset)
/// </summary>
public Int64 StartTime { get; private set; } = HiResTimer.Current;
/// <summary>
/// Indicates the delta of the system's current counter value
/// and the counter value when this timer object was started (or reset)
/// </summary>
public Int64 CounterElapsed => ( HiResTimer.Current - this.StartTime );
/// <summary>
/// Returns the number of seconds since this timer object was started (or reset)
/// </summary>
public double Elapsed => GetSeconds( this.CounterElapsed );
#endregion
#region Public Methods
/// <summary>
/// Resets the timer object
/// </summary>
public void Reset() => this.StartTime = HiResTimer.Current;
public TimeSpan ToTimeSpan() => ToTimeSpan( this.Elapsed );
#region Object Overrides
public override string ToString() => ToTimeSpan().ToString();
public override bool Equals( object obj ) => Equals( obj as HiResTimer );
public override int GetHashCode() => this.StartTime.GetHashCode();
#endregion
#endregion
#region IComparable Methods
public int CompareTo( HiResTimer other ) => this.StartTime.CompareTo( other.StartTime );
#endregion
#region IEquatable Methods
public bool Equals( HiResTimer other ) => this.StartTime == other?.StartTime;
#endregion
#region Static Items
#region Static Constructor
/// <summary>
/// Performs the one-time lookup of the Frequency value
/// </summary>
static HiResTimer()
{
// Invoke the API to retrieve the frequency value
if ( QueryPerformanceFrequency( out Int64 frequency ) )
{
HiResTimer.Frequency = frequency;
}
else
{
// MSDN says this will never happen
throw new System.ComponentModel.Win32Exception();
}
}
#endregion
#region Static Public Properties
/// <summary>
/// Value of the System's Clock Frequency
/// </summary>
static public Int64 Frequency { get; private set; }
static public TimeSpan SinceBoot => ToTimeSpan( GetSeconds( Current ) );
/// <summary>
/// Retrieves the System's current counter value
/// </summary>
/// <exception cref="System.ComponentModel.Win32Exception">Win32Exception</exception>
static public Int64 Current
{
get
{
// Call the Win32 API
if ( QueryPerformanceCounter( out Int64 time ) )
{
return time;
}
else
{
throw new System.ComponentModel.Win32Exception();
}
}
}
#endregion
#region Private Static
static private double GetSeconds( Int64 elapsed ) => ( elapsed / (double)HiResTimer.Frequency );
static private long SecondsToTicks( double seconds ) => (long)( seconds * SECONDS_TO_TICKS );
static private TimeSpan ToTimeSpan( double seconds ) => new TimeSpan( SecondsToTicks(seconds) );
#endregion
#region Operators
public static bool operator ==( HiResTimer lhs, HiResTimer rhs ) => lhs.Equals( rhs );
public static bool operator !=( HiResTimer lhs, HiResTimer rhs ) => !lhs.Equals( rhs );
// Conversion Operators
public static implicit operator double( HiResTimer hrt ) => hrt.Elapsed;
public static implicit operator TimeSpan( HiResTimer hrt ) => hrt.ToTimeSpan();
#endregion
#region Private Static
// TimeSpan ticks are in 100 nanosecond units
private const double SECONDS_TO_TICKS = 1e9 / 100.0;
#endregion
#region P/Invoke Items
/// <summary>
/// Gets the systems current counter value
/// </summary>
/// <param name="count">Out param to hold the value</param>
/// <returns>Bool indicating success or failure</returns>
[DllImport( "kernel32.dll", SetLastError = true )]
private static extern bool QueryPerformanceCounter( out Int64 count );
/// <summary>
/// Gets the system's clock frequency from the OS. Divide the system's counter
/// values by this value to convert them into seconds. It only needs to be done
/// once because the value will not change until the OS is restarted.
/// </summary>
/// <param name="frequency">Out param to hold the value</param>
/// <returns>Bool indicating success or failure</returns>
[DllImport( "kernel32.dll", SetLastError = true )]
private static extern bool QueryPerformanceFrequency( out Int64 frequency );
#endregion
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment