C# 用委托或 lambda 包装秒表计时?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/232848/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Wrapping StopWatch timing with a delegate or lambda?
提问by Jeff Atwood
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)
?
肯定有一个方法来调用此位的计时码作为花式schmancy .NET 3.0的λ,而不是(上帝保佑)剪切和粘贴了几次和更换DoStuff(s)
用DoSomethingElse(s)
?
I know it can be done as a Delegate
but I'm wondering about the lambda way.
我知道它可以作为一个来完成,Delegate
但我想知道 lambda 的方式。
采纳答案by Matt Hamilton
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).
您可以添加另一个忽略“迭代”参数的重载,并使用一些默认值(如 1000)调用此版本。
回答by Mark Ingram
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.
从 DependencyObject 派生的任何对象现在都可以调用 TimedFor(..)。可以轻松调整该函数以通过 ref 参数提供返回值。
--
——
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:
否则,这个答案看起来有一些不错的“通用”能力:
回答by Morten Christiansen
You can overload a number of methods to cover various cases of parameters you might want to pass to the lambda:
您可以重载许多方法来涵盖您可能希望传递给 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;
}
Alternatively, you can use the Func delegate if they must return a value. You can also pass in an array (or more) of parameters if each iteration must use a unique value.
或者,如果必须返回值,您可以使用 Func 委托。如果每次迭代都必须使用唯一值,您还可以传入一个(或多个)参数数组。
回答by Mark S. Rasmussen
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
前段时间我写了一个简单的 CodeProfiler 类,它包装了 Stopwatch 以使用 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:
它还可以轻松地让您分析多线程代码。以下示例将使用 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();
}
回答by Davy Landman
I like to use the CodeTimer classes from Vance Morrison (one of the performance dudes from .NET).
我喜欢使用 Vance Morrison 的 CodeTimer 类(来自 .NET 的性能达人之一)。
He made a post on on his blog titled "Measuring managed code quickly and easiliy: CodeTimers".
他在他的博客上发表了一篇题为“快速轻松地测量托管代码:CodeTimers”的帖子。
It includes cool stuff such as a MultiSampleCodeTimer. It does automatic calculation of the mean and standard deviation and its also very easy to print out your results.
它包括很酷的东西,例如 MultiSampleCodeTimer。它会自动计算平均值和标准偏差,而且打印结果也很容易。
回答by jyoung
Assuming you just need a quick timing of one thing this is easy to use.
假设您只需要快速完成一件事,这很容易使用。
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; }
}
}
回答by Sam Saffron
For me the extension feels a little bit more intuitive on int, you no longer need to instantiate a Stopwatch or worry about resetting it.
对我来说,扩展在 int 上感觉更直观一点,您不再需要实例化秒表或担心重置它。
So you have:
所以你有了:
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);
}
}
With the sample usage of:
使用示例:
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);
}
});
Sample output:
示例输出:
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)
回答by Mauricio Scheffer
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
}
回答by Anthony Mastrean
The StopWatch
class does not need to be Disposed
or Stopped
on error. So, the simplest code to timesome actionis
本StopWatch
类并不需要是Disposed
或Stopped
出错。因此,为某些操作计时的最简单代码是
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.
我不喜欢将迭代包含在StopWatch
代码中的想法。您始终可以创建另一个处理执行N
迭代的方法或扩展。
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.
我测试了静态方法和扩展方法(结合迭代和基准测试),预期执行时间和实际执行时间的增量 <= 1 ms。
回答by Alper Ebicoglu
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;
}
}