How to measure elapsed time without allocating a Stopwatch

 
 
  • Gérald Barré

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!

Follow me:
Enjoy this blog?