How to measure elapsed time without allocating a Stopwatch

  • Gérald Barré

There are many cases where you need to compute the execution time of a method. The classic way is to use the Stopwatch class. Stopwatch is a class, so instantiating it may increase the pressure on the Garbage Collector. In the case of a single-threaded application, you can reuse the instance to avoid creating lots of instances. However, in a multi-threaded application, it may be more complicated to reuse the instances. You can use a [ThreadStatic] field or an ObjectPool<Stopwatch>, but this comes with a cost.

If you don't need all the features of the Stopwatch, such as stopping and restarting the stopwatch, you can create a struct that provides just the bare minimum. This way you can measure time easily without any allocation. The idea is to use long Stopwatch.GetTimestamp() when initializing the struct and when computing the elapsed time. Then, you can compute the difference to get the elapsed time.

// 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());
}
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?Buy Me A Coffee