我正在编写这样的代码,做一些快速而肮脏的时机:
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而不是(God禁止)剪切和粘贴它几次并替换 DoStuff(s)
使用 DoSomethingElse(s)
?
我知道它可以作为Delegate
完成,但我想知道lambda方式。
答案 0 :(得分:127)
如何扩展秒表课程?
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)调用此版本。
答案 1 :(得分:30)
以下是我一直在使用的内容:
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
}
答案 2 :(得分:12)
您可以尝试为您正在使用的任何类(或任何基类)编写扩展方法。
我打电话的样子如下:
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参数轻松调整该函数以提供返回值。
-
如果您不希望将功能绑定到任何类/对象,您可以执行以下操作:
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);
如果做不到这一点,这个答案看起来有一些不错的“通用”能力:
答案 3 :(得分:7)
前段时间我编写了一个简单的CodeProfiler类,它使用Action包裹了秒表以轻松地分析方法: 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();
}
答案 4 :(得分:7)
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
代码中的想法。您始终可以创建另一个处理执行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 ms。
答案 5 :(得分:4)
假设您只需要快速计时,这很容易使用。
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; }
}
}
答案 6 :(得分:2)
您可以重载多种方法,以涵盖您可能想要传递给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委托。如果每次迭代必须使用唯一值,您也可以传入一个或多个参数数组。
答案 7 :(得分:2)
对我而言,扩展在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)
我喜欢使用Vance Morrison的CodeTimer类(来自.NET的性能家伙之一)。
他在他的博客上发表了一篇名为“Measuring managed code quickly and easiliy: CodeTimers”的帖子。
它包含很酷的东西,比如MultiSampleCodeTimer。它可以自动计算平均值和标准偏差,也可以很容易地打印出结果。
答案 9 :(得分:0)
public static class StopWatchExtensions
{
public static async Task<TimeSpan> LogElapsedMillisecondsAsync(
this Stopwatch stopwatch,
ILogger logger,
string actionName,
Func<Task> action)
{
stopwatch.Reset();
stopwatch.Start();
await action();
stopwatch.Stop();
logger.LogDebug(string.Format(actionName + " completed in {0}.", stopwatch.Elapsed.ToString("hh\\:mm\\:ss")));
return stopwatch.Elapsed;
}
}