Wrapping StopWatch timing with a delegate or lambda?
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.
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:
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.
Solution 5:
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();
}