Measuring the execution time of a method is a common requirement. The classic approach is to use the Stopwatch class, but since Stopwatch is a reference type, every instantiation adds pressure on the Garbage Collector. In a single-threaded application, you can reuse a single instance to avoid excessive allocations. In a multi-threaded application, reusing instances is more complex. You could use a [ThreadStatic] field or an ObjectPool<Stopwatch>, but both options come with their own overhead.
If you only need basic timing and do not require features like pausing and resuming, you can create a struct that covers just what you need. Because it is a value type, it requires no heap allocation. The approach relies on long Stopwatch.GetTimestamp() to capture a start timestamp when the struct is created, then computes the difference when retrieving the elapsed time.
ValueStopwatch.cs (C#)
// Inspired from: https://github.com/dotnet/runtime/blob/26b6e4ea97a627ab800362b2c10f32ebecea041d/src/libraries/Common/src/Extensions/ValueStopwatch/ValueStopwatch.cs
using System.Diagnostics;
public readonly struct ValueStopwatch
{
private static readonly double s_timestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
private readonly long _startTimestamp;
private ValueStopwatch(long startTimestamp) => _startTimestamp = startTimestamp;
public static ValueStopwatch StartNew() => new ValueStopwatch(GetTimestamp());
public static long GetTimestamp() => Stopwatch.GetTimestamp();
public static TimeSpan GetElapsedTime(long startTimestamp, long endTimestamp)
{
var timestampDelta = endTimestamp - startTimestamp;
var ticks = (long)(s_timestampToTicks * timestampDelta);
return new TimeSpan(ticks);
}
public TimeSpan GetElapsedTime() => GetElapsedTime(_startTimestamp, GetTimestamp());
}
C#
var stopwatch = ValueStopwatch.StartNew();
// TODO measured action
Console.WriteLine(stopwatch.GetElapsedTime());
The code is available as a NuGet package and on GitHub.
Do you have a question or a suggestion about this post? Contact me!