Envolviendo cronometraje cronómetro con un delegado o lambda?

Estoy escribiendo código como este, haciendo un poco de tiempo rápido y sucio:

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

Seguramente hay una manera de llamar a este bit de código de tiempo como una lambda de .NET 3.0 de lujo-schmancy en lugar de (Dios no lo quiera) cortarlo y pegarlo un par de veces y reemplazar las DoStuff(s) con DoSomethingElse(s) ?

Sé que se puede hacer como Delegate pero me pregunto acerca de la manera lambda.

¿Qué hay de extender la clase de cronómetro?

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

Entonces llámalo así:

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

Podría agregar otra sobrecarga que omita el parámetro "iteraciones" y llame a esta versión con algún valor predeterminado (como 1000).

Esto es lo que he estado usando:

 public class DisposableStopwatch: IDisposable { private readonly Stopwatch sw; private readonly Action f; public DisposableStopwatch(Action f) { this.f = f; sw = Stopwatch.StartNew(); } public void Dispose() { sw.Stop(); f(sw.Elapsed); } } 

Uso:

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

Podría intentar escribir un método de extensión para cualquier clase que esté utilizando (o cualquier clase base).

Haría que la llamada parezca:

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

Entonces el método de extensión:

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

Cualquier objeto derivado de DependencyObject ahora puede llamar a TimedFor (..). La función se puede ajustar fácilmente para proporcionar valores de retorno a través de parámetros de referencia.

-

Si no desea que la funcionalidad esté vinculada a cualquier clase / objeto, puede hacer algo como:

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

Entonces podrías usarlo así:

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

En su defecto, esta respuesta parece que tiene alguna capacidad decente "genérica":

Envolviendo cronometraje cronómetro con un delegado o lambda?

Hace un tiempo, escribí una clase simple de CodeProfiler que incluía a Cronómetro para crear fácilmente un perfil de un método usando una Acción: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

También le permitirá fácilmente crear un perfil del código multiproceso. El siguiente ejemplo perfilará la acción lambda con 1-16 hilos:

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

La clase StopWatch no necesita ser Disposed o Stopped en caso de error. Entonces, el código más simple para cronometrar alguna acción es

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

Ejemplo de código de llamada

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

No me gusta la idea de incluir las iteraciones en el código de StopWatch . Siempre puede crear otro método o extensión que maneje la ejecución de N iteraciones.

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

Ejemplo de código de llamada

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

Aquí están las versiones del método de extensión

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

Y muestra el código de llamada

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

Probé los métodos estáticos y los métodos de extensión (combinando iteraciones y punto de referencia) y el delta del tiempo de ejecución esperado y el tiempo de ejecución real es < = 1 ms.

Suponiendo que solo necesita una sincronización rápida de una cosa, es fácil de usar.

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

Para mí, la extensión se siente un poco más intuitiva en int, ya no necesita instanciar un cronómetro o preocuparse por reiniciarlo.

Así que tienes:

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

Con el uso de muestra de:

 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(); foreach (var str in randomStrings) { dict.Add(str, null); } }); 50.Times("Add 10,000 random strings to a SortedList", () => { var list = new SortedList(); foreach (var str in randomStrings) { list.Add(str, null); } }); 

Muestra de salida:

 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) 

Puede sobrecargar una cantidad de métodos para cubrir varios casos de parámetros que le gustaría pasar a la lambda:

 public static Stopwatch MeasureTime(int iterations, Action 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(int iterations, Action 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; } 

Alternativamente, puede usar el delegado Func si deben devolver un valor. También puede pasar una matriz (o más) de parámetros si cada iteración debe usar un valor único.

Me gusta usar las clases CodeTimer de Vance Morrison (uno de los tipos de rendimiento de .NET).

Hizo una publicación en su blog titulada ” Midiendo el código administrado rápida y fácilmente: CodeTimers “.

Incluye cosas geniales como un MultiSampleCodeTimer. Hace un cálculo automático de la media y la desviación estándar y también es muy fácil imprimir sus resultados.