用委托或lambda包装StopWatch时间?

我正在写这样的代码,做一些快速和肮脏的时间:

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

当然,有一种方法可以把这个时间代码称为一个奇怪的.NET 3.0 lambda,而不是(上帝禁止)剪切和粘贴它几次,并用DoSomethingElse(s)代替DoStuff(s)

我知道这可以作为Delegate完成,但我想知道lambda的方式。

如何扩展秒表类?

 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; } } 

然后像这样调用它:

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

你可以添加另一个重载,省略“迭代”参数,并调用这个版本的默认值(如1000)。

这是我一直在使用的:

 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); } } 

用法:

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

你可以尝试为你使用的任何类(或任何基类)写一个扩展方法。

我会打电话看起来像:

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

那么扩展方法:

 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; } 

任何派生自DependencyObject的对象现在都可以调用TimedFor(..)。 该函数可以很容易地调整,以通过ref params提供返回值。

如果你不想把这个function绑定到任何类/对象上,你可以这样做:

 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; } } 

那么你可以像这样使用它:

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

否则,这个答案看起来像有一些体面的“通用”的能力:

用委托或lambda包装StopWatch时间?

我前段时间编写了一个简单的CodeProfiler类,它包装了Stopwatch,以便使用Action轻松地分析一个方法: http : //www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

它也可以轻松地让你分析代码multithreading。 以下示例将使用1-16个线程分析操作lambda:

 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(); } 

StopWatch类不需要DisposedStopped错误。 所以,最简单的代码是时间

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

示例调用代码

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

我不喜欢将迭代包含在StopWatch代码中的想法。 您始终可以创build另一个处理执行N次迭代的方法或扩展。

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

示例调用代码

 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); } 

这里是扩展方法的版本

 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); } } 

和示例调用代码

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

我testing了静态方法和扩展方法(结合迭代和基准testing),预计执行时间和实际执行时间的差值为<= 1 ms。

假设你只需要一个快速的时间,这是很容易使用。

  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; } } } 

对于我来说,扩展感觉上int更直观,你不再需要实例化秒表或担心重置它。

所以你有:

 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); } } 

随着以下示例的使用:

 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); } }); 

示例输出:

 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) 

你可以重载一些方法来覆盖你可能要传递给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; } 

或者,您可以使用Func委托,如果他们必须返回一个值。 如果每次迭代都必须使用唯一值,那么也可以传入一个或多个参数。

我喜欢使用来自Vance Morrison(来自.NET的高性能软件之一)的CodeTimer类。

他在他的博客上发表了一篇题为“ 快速,便捷地测量托pipe代码:CodeTimers ”的博文 。

它包括很酷的东西,如MultiSampleCodeTimer。 它可以自动计算平均值和标准偏差,并且很容易打印出结果。