Last active
May 15, 2019 23:05
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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