代码之家  ›  专栏  ›  技术社区  ›  Jeff Atwood

用委托或lambda包装秒表计时?

  •  92
  • Jeff Atwood  · 技术社区  · 16 年前

    我在写这样的代码,做了一点快速和肮脏的时间安排:

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

    当然,有一种方法可以将这段计时代码称为一个奇特的schmancy.net 3.0 lambda,而不是(上帝禁止)将其剪切粘贴几次并替换 DoStuff(s) 具有 DoSomethingElse(s) ?

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

    9 回复  |  直到 7 年前
        1
  •  127
  •   Matt Hamilton    16 年前

    扩展秒表类怎么样?

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

    您可以添加另一个重载,忽略“iterations”参数并使用一些默认值(如1000)调用此版本。

        2
  •  29
  •   Alex    7 年前

    以下是我一直在使用的:

    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
    }
    
        3
  •  12
  •   Community CDub    8 年前

    您可以尝试为正在使用的任何类(或任何基类)编写扩展方法。

    我会让电话看起来像:

    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(..)。可以很容易地调整函数,通过引用参数提供返回值。

    ——

    如果不希望将功能绑定到任何类/对象,可以执行以下操作:

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

    如果不能做到这一点,这个答案看起来有一些不错的“通用”能力:

    Wrapping StopWatch timing with a delegate or lambda?

        4
  •  7
  •   Jeff Atwood    14 年前

    不久前,我编写了一个简单的CodeProfiler类,它包装了秒表,以便使用操作轻松分析方法: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

    它还可以让您轻松地分析多线程代码。下面的示例将分析带有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();
    }
    
        5
  •  6
  •   Anthony Mastrean PavanDevarakonda    15 年前

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

    我不喜欢将迭代包含到 秒表 代码。您总是可以创建另一个方法或扩展来处理执行 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);
    }
    

    我测试了静态方法和扩展方法(结合迭代和基准),预期执行时间和实际执行时间的增量为<=1毫秒。

        6
  •  4
  •   jyoung    16 年前

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

      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; }
        }
    }
    
        7
  •  2
  •   Sam Saffron James Allen    16 年前

    对于我来说,这个扩展在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)
    
        8
  •  1
  •   Morten Christiansen    16 年前

    可以重载多个方法来覆盖可能要传递给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委托。如果每次迭代都必须使用唯一的值,则还可以传入一个(或多个)参数数组。

        9
  •  1
  •   Davy Landman    16 年前

    我喜欢使用VanceMorrison(来自.NET的性能达人之一)的代码计时器类。

    他在他的博客上发表了一篇题为“的文章。 Measuring managed code quickly and easiliy: CodeTimers “。

    它包括一些很酷的东西,比如多采样编码器。它可以自动计算平均值和标准偏差,也可以很容易地打印出结果。