2008-10-24 8 views
93

estoy escribiendo código como este, haciendo un poco de tiempo rápido y sucio:Envoltura cronometrada cronómetro con un delegado o lambda?

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 trozo de código de tiempo como una suposición-schmancy .NET 3.0 lambda en lugar de (Dios no lo quiera) cortándolo y pegándolo un par de veces y reemplazando el DoStuff(s) con DoSomethingElse(s)?

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

Respuesta

127

¿Qué hay de la ampliación de 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; 
    } 
} 

A continuación, llame así:

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

Se podría añadir otra sobrecarga que omite el parámetro "iteraciones" y llama a esta versión con un valor por defecto (como 1000).

+3

Es posible que desee reemplazar sw.Start() con sw.StartNew() para evitar aumentar accidentalmente el tiempo transcurrido para cada llamada consecutiva de s.Time(), reutilizando la misma instancia de Cronómetro. – VVS

+1

Podría obtener aún más C# 3.0, y reemplazar esa declaración 'for' por 'foreach' (var i en Enumerable.Range (0, iteraciones)) –

+11

@Jay Estoy de acuerdo con que "foreach" con Enumerable.Range se ve un poco más "moderno", pero mis pruebas muestran que es cuatro veces más lento que un ciclo "para" en un conteo grande. YMMV. –

12

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

que tendría la apariencia de llamada como:

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

A continuación, 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 que deriva de DependencyObject ahora pueden llamar 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 a estar atado a ninguna clase/objeto que podría 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 se podría utilizar como:

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

En caso de que falle, esta respuesta parece que tiene alguna capacidad decente "genérica":

Wrapping StopWatch timing with a delegate or lambda?

+0

genial, pero no me importa la forma en que esto está ligado a una clase o clase base en particular; se puede hacer de manera más genérica? –

+0

Como en la clase MyObject para la que está escrito el método de extensión? Se puede cambiar fácilmente para extender la clase Object u otra clase en el árbol de herencia. –

+0

Estaba pensando más estático, como no vinculado a NINGÚN objeto o clase en particular .. el tiempo y el tiempo es una especie de universal –

1

Puede sobrecargar un número de métodos para cubrir varios casos de parámetros es posible que desee pasar a la 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; 
} 

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

7

escribí una clase sencilla CodeProfiler hace algún tiempo que envuelve al perfil Cronómetro fácilmente un método que utiliza una acción: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

También va a permitir fácilmente crear perfiles para el multiproceso código.El siguiente ejemplo el perfil de la lambda acción 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(); 
} 
1

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

Hizo una publicación en su blog titulado "Measuring managed code quickly and easiliy: CodeTimers".

Incluye cosas geniales como 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.

4

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

Para mí la extensión se siente un poco más intuitiva int, ya no es necesario crear una instancia de un cronómetro o preocuparse por reiniciarlo.

Por lo que tiene:

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 ejemplo de uso:

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

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

Esto es lo que he estado usando:

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

Uso:

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

¡Esta es la mejor solución que he visto! Sin extensión (para que pueda usarse en muchas clases) ¡y muy limpio! – Calvin

+0

No estoy seguro de si obtuve el ejemplo de uso correctamente. Cuando intento usar 'Console.WriteLine (" ")' para probar bajo '// hago las cosas que quiero medir', entonces el compilador no está contento en absoluto. ¿Se supone que debes hacer expresiones y declaraciones normales allí? – Tim

+0

@Tim - Estoy seguro de que lo resolviste, pero la instrucción de uso tenía un corchete faltante – Alex

6

La clase StopWatch no necesita ser Disposed o Stopped en caso de error. Por lo tanto, el código más simple de tiempo alguna acción es

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

Muestra 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 StopWatch. Siempre puede crear otro método o extensión que maneje la ejecución de iteraciones N.

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

código de llamada Muestra

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

Éstos son las versiones 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 el código llamando muestra

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

he probado los métodos estáticos y métodos de extensión (peine ining iterations y benchmark) y el delta del tiempo de ejecución esperado y el tiempo de ejecución real es < = 1 ms.

+0

Las versiones del método de extensión me hacen agua la boca. :) – bzlm

Cuestiones relacionadas