views:

5411

answers:

9

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.

+6  A: 

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:

http://stackoverflow.com/questions/232848/wrapping-stopwatch-timing-with-a-delegate-or-lambda#232878

Mark Ingram
cool, but I don't care for the way this is tied to a particular class or base class; can it be done more generically?
Jeff Atwood
As in the MyObject class that the extension method is written for? It can easily be changed to extend the Object class or other class in the inheritance tree.
Mark Ingram
I was thinking more static, like not tied to ANY particular object or class.. time and timing is sort of universal
Jeff Atwood
Excellent, the 2nd version is more what I was thinking, +1, but I give accepted to Matt as he got to it first.
Jeff Atwood
+95  A: 

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).

Matt Hamilton
You might want to replace sw.Start() with sw.StartNew() to prevent accidentially increasing the elapsed time for every consecutive call of s.Time(), reusing the same Stopwatch instance.
VVS
You could get even more C# 3.0, and replace that old-fashed 'for' statement with foreach(var i in Enumerable.Range(0, iterations))
Jay Bazuzi
@Jay I agree that "foreach" with Enumerable.Range looks a bit more "modern", but my tests show that it's about four times slower than a "for" loop over a large count. YMMV.
Matt Hamilton
I'm not seeing a `StartNew()` method, but maybe VVS means to replace the `sw.Reset(); sw.Start();` with `sw.Restart();`? Sounds like a matter of preference to me.
StriplingWarrior
+1  A: 

You can overload a number of methods to cover various cases of parameters you might want to pass to the lambda:

public static Stopwatch MeasureTime<T>(int iterations, Action<T> action, T param)
{
    var sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < iterations; i++)
    {
        action.Invoke(param);
    }
    sw.Stop();

    return sw;
}

public static Stopwatch MeasureTime<T, K>(int iterations, Action<T, K> action, T param1, K param2)
{
    var sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < iterations; i++)
    {
        action.Invoke(param1, param2);
    }
    sw.Stop();

    return sw;
}

Alternatively, you can use the Func delegate if they must return a value. You can also pass in an array (or more) of parameters if each iteration must use a unique value.

Morten Christiansen
+4  A: 

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:

using System;

namespace CodeProfiler
{
    class Program
    {
        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();
        }
    }
}
Mark S. Rasmussen
+1  A: 

I like to use the CodeTimer classes from Vance Morrison (one of the performance dudes from .NET).

He made a post on on his blog titled "Measuring managed code quickly and easiliy: CodeTimers".

It includes cool stuff such as a MultiSampleCodeTimer. It does automatic calculation of the mean and standard deviation and its also very easy to print out your results.

Davy Landman
+2  A: 

Assuming you just need a quick timing of one thing this is easy to use.

  public static class Test {
    public static void Invoke() {
        using( SingleTimer.Start )
            Thread.Sleep( 200 );
        Console.WriteLine( SingleTimer.Elapsed );

        using( SingleTimer.Start ) {
            Thread.Sleep( 300 );
        }
        Console.WriteLine( SingleTimer.Elapsed );
    }
}

public class SingleTimer :IDisposable {
    private Stopwatch stopwatch = new Stopwatch();

    public static readonly SingleTimer timer = new SingleTimer();
    public static SingleTimer Start {
        get {
            timer.stopwatch.Reset();
            timer.stopwatch.Start();
            return timer;
        }
    }

    public void Stop() {
        stopwatch.Stop();
    }
    public void Dispose() {
        stopwatch.Stop();
    }

    public static TimeSpan Elapsed {
        get { return timer.stopwatch.Elapsed; }
    }
}
jyoung
A: 

For me the extension feels a little bit more intuitive on int, you no longer need to instantiate a Stopwatch or worry about resetting it.

So you have:

static class BenchmarkExtension {

    public static void Times(this int times, string description, Action action) {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < times; i++) {
            action();
        }
        watch.Stop();
        Console.WriteLine("{0} ... Total time: {1}ms ({2} iterations)", 
            description,  
            watch.ElapsedMilliseconds,
            times);
    }
}

With the sample usage of:

var randomStrings = Enumerable.Range(0, 10000)
    .Select(_ => Guid.NewGuid().ToString())
    .ToArray();

50.Times("Add 10,000 random strings to a Dictionary", 
    () => {
        var dict = new Dictionary<string, object>();
        foreach (var str in randomStrings) {
            dict.Add(str, null);
        }
    });

50.Times("Add 10,000 random strings to a SortedList",
    () => {
        var list = new SortedList<string, object>();
        foreach (var str in randomStrings) {
            list.Add(str, null);
        }
    });

Sample output:

Add 10,000 random strings to a Dictionary ... Total time: 144ms (50 iterations)
Add 10,000 random strings to a SortedList ... Total time: 4088ms (50 iterations)
Sam Saffron
+1  A: 

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
}
Mauricio Scheffer
+2  A: 

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.

Anthony Mastrean
The extension method versions make my mouth water. :)
bzlm