用委托或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
类不需要Disposed
或Stopped
错误。 所以,最简单的代码是时间
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。 它可以自动计算平均值和标准偏差,并且很容易打印出结果。