Domanda

Sto scrivendo codice in questo modo, facendo un po 'di tempismo veloce e sporco:

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

Sicuramente c'è un modo per chiamare questo bit di codice di temporizzazione come un lambda fantasioso di .NET 3.0 piuttosto che (Dio non voglia) tagliarlo e incollarlo un paio di volte e sostituire il DoStuff(s) con DoSomethingElse(s)

So che può essere fatto come Delegate ma mi chiedo come sia la lambda.

È stato utile?

Soluzione

Che ne dici di estendere la classe Cronometro?

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

Quindi chiamalo così:

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

Potresti aggiungere un altro sovraccarico che omette la " iterazioni " parametro e chiama questa versione con un valore predefinito (come 1000).

Altri suggerimenti

Ecco cosa ho usato:

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

Utilizzo:

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

Potresti provare a scrivere un metodo di estensione per qualunque classe tu stia usando (o qualsiasi classe base).

Vorrei che la chiamata fosse simile:

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

Quindi il metodo di estensione:

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

Qualsiasi oggetto derivante da DependencyObject ora può chiamare TimedFor (..). La funzione può essere facilmente regolata per fornire valori di ritorno tramite parametri di riferimento.

-

Se non volevi che la funzionalità fosse legata a qualsiasi classe / oggetto, potresti fare qualcosa del tipo:

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

Quindi puoi usarlo come:

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

In mancanza, questa risposta sembra avere qualche " decente; generico " capacità:

Avvolgimento del cronometro StopWatch con un delegato o lambda?

La classe StopWatch non deve essere Disposed o Stopped in errore. Quindi, il codice più semplice per tempo alcune azioni è

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

Codice chiamante di esempio

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

Non mi piace l'idea di includere le iterazioni nel codice N. Puoi sempre creare un altro metodo o estensione che gestisca l'esecuzione di <=> iterazioni.

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

Codice chiamante di esempio

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

Ecco le versioni del metodo di estensione

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

E codice di chiamata di esempio

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

Ho testato i metodi statici e i metodi di estensione (combinando iterazioni e benchmark) e il delta del tempo di esecuzione previsto e del tempo di esecuzione reale è < = 1 ms.

Qualche tempo fa ho scritto una semplice classe CodeProfiler che includeva il cronometro per profilare facilmente un metodo usando un'azione: http://www.improve.dk/ blog / 2008/04/16 / profiling-code-the-facile-way

Ti consentirà anche di profilare facilmente il codice multithread. L'esempio seguente profilerà l'azione lambda con 1-16 thread:

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

Supponendo che tu abbia solo bisogno di un tempismo rapido di una cosa che è facile da usare.

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

Puoi sovraccaricare un numero di metodi per coprire vari casi di parametri che potresti voler passare alla 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;
}

In alternativa, è possibile utilizzare il delegato Func se devono restituire un valore. Puoi anche passare un array (o più) di parametri se ogni iterazione deve usare un valore univoco.

Per me l'estensione sembra un po 'più intuitiva su int, non è più necessario creare un'istanza di un cronometro o preoccuparsi di ripristinarlo.

Quindi hai:

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 l'utilizzo di esempio di:

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

Output di esempio:

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)

Mi piace usare le classi CodeTimer di Vance Morrison (uno dei tizi delle prestazioni di .NET).

Ha pubblicato un post sul suo blog intitolato " Misurazione rapida e semplice del codice gestito: CodeTimers " ;.

Include cose interessanti come un MultiSampleCodeTimer. Fa il calcolo automatico della media e della deviazione standard ed è anche molto facile stampare i risultati.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top