Wrapping StopWatch timing with a delegate or lambda?

18,571

Solution 1

How about extending the Stopwatch class?

public static class StopwatchExtensions
{
    public static long Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Reset();
        sw.Start(); 
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.ElapsedMilliseconds;
    }
}

Then call it like this:

var s = new Stopwatch();
Console.WriteLine(s.Time(() => DoStuff(), 1000));

You could add another overload which omits the "iterations" parameter and calls this version with some default value (like 1000).

Solution 2

Here's what I've been using:

public class DisposableStopwatch: IDisposable {
    private readonly Stopwatch sw;
    private readonly Action<TimeSpan> f;

    public DisposableStopwatch(Action<TimeSpan> f) {
        this.f = f;
        sw = Stopwatch.StartNew();
    }

    public void Dispose() {
        sw.Stop();
        f(sw.Elapsed);
    }
}

Usage:

using (new DisposableStopwatch(t => Console.WriteLine("{0} elapsed", t))) {
  // do stuff that I want to measure
}

Solution 3

You could try writing an extension method for whatever class you're using (or any base class).

I would have the call look like:

Stopwatch sw = MyObject.TimedFor(1000, () => DoStuff(s));

Then the extension method:

public static Stopwatch TimedFor(this DependencyObject source, Int32 loops, Action action)
{
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < loops; ++i)
{
    action.Invoke();
}
sw.Stop();

return sw;
}

Any object deriving from DependencyObject can now call TimedFor(..). The function can easily be adjusted to provide return values via ref params.

--

If you didn't want the functionality to be tied to any class / object you could do something like:

public class Timing
{
  public static Stopwatch TimedFor(Action action, Int32 loops)
  {
    var sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < loops; ++i)
    {
      action.Invoke();
    }
    sw.Stop();

    return sw;
  }
}

Then you could use it like:

Stopwatch sw = Timing.TimedFor(() => DoStuff(s), 1000);

Failing that, this answer looks like it has some decent "generic" ability:

Wrapping StopWatch timing with a delegate or lambda?

Solution 4

I wrote a simple CodeProfiler class some time ago that wrapped Stopwatch to easily profile a method using an Action: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

It'll also easily allow you to profile the code multithreaded. The following example will profile the action lambda with 1-16 threads:

static void Main(string[] args)
{
    Action action = () =>
    {
        for (int i = 0; i < 10000000; i++)
            Math.Sqrt(i);
    };

    for(int i=1; i<=16; i++)
        Console.WriteLine(i + " thread(s):\t" + 
            CodeProfiler.ProfileAction(action, 100, i));

    Console.Read();
}

Solution 5

The StopWatch class does not need to be Disposed or Stopped on error. So, the simplest code to time some action is

public partial class With
{
    public static long Benchmark(Action action)
    {
        var stopwatch = Stopwatch.StartNew();
        action();
        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }
}

Sample calling code

public void Execute(Action action)
{
    var time = With.Benchmark(action);
    log.DebugFormat(“Did action in {0} ms.”, time);
}

I don't like the idea of including the iterations into the StopWatch code. You can always create another method or extension that handles executing N iterations.

public partial class With
{
    public static void Iterations(int n, Action action)
    {
        for(int count = 0; count < n; count++)
            action();
    }
}

Sample calling code

public void Execute(Action action, int n)
{
    var time = With.Benchmark(With.Iterations(n, action));
    log.DebugFormat(“Did action {0} times in {1} ms.”, n, time);
}

Here are the extension method versions

public static class Extensions
{
    public static long Benchmark(this Action action)
    {
        return With.Benchmark(action);
    }

    public static Action Iterations(this Action action, int n)
    {
        return () => With.Iterations(n, action);
    }
}

And sample calling code

public void Execute(Action action, int n)
{
    var time = action.Iterations(n).Benchmark()
    log.DebugFormat(“Did action {0} times in {1} ms.”, n, time);
}

I tested the static methods and extension methods (combining iterations and benchmark) and the delta of expected execution time and real execution time is <= 1 ms.

Share:
18,571
Peter Burns
Author by

Peter Burns

Stack Overflow Valued Associate #00001 Wondering how our software development process works? Take a look! Find me on twitter, or read my blog. Don't say I didn't warn you because I totally did. However, I no longer work at Stack Exchange, Inc. I'll miss you all. Well, some of you, anyway. :)

Updated on June 19, 2022

Comments

  • Peter Burns
    Peter Burns almost 2 years

    I'm writing code like this, doing a little quick and dirty timing:

    var sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < 1000; i++)
    {
        b = DoStuff(s);
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
    

    Surely there's a way to call this bit of timing code as a fancy-schmancy .NET 3.0 lambda rather than (God forbid) cutting and pasting it a few times and replacing the DoStuff(s) with DoSomethingElse(s)?

    I know it can be done as a Delegate but I'm wondering about the lambda way.