2008-10-24 2 views
93

ich Code wie diesen zu schreiben, ein wenig schnell und schmutzig Timing zu tun:Wrapping StopWatch Timing mit einem Delegierten oder Lambda?

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

Sicherlich gibt es eine Möglichkeit, dieses Stück Zeitcode als fancy-schmancy .NET 3.0 Lambda eher zu rufen als (Gott bewahre) Schneiden und Einfügen es ein paar Mal und ersetzen die DoStuff(s) mit DoSomethingElse(s)?

Ich weiß, dass es als Delegate getan werden kann, aber ich frage mich über die Lambda-Art.

Antwort

127

Wie wäre es mit der Erweiterung der Stoppuhrklasse?

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

Dann es so nennen:

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

Sie könnten eine weitere Überlastung hinzuzufügen, die die „Iterationen“ Parameter und ruft diese Version mit einigen Standardwert (wie 1000) auslässt.

+3

Sie könnten sw.Start() mit sw.StartNew() ersetzen, um die verstrichene Zeit für jeden aufeinanderfolgenden Aufruf von s.Time() versehentlich zu erhöhen und die gleiche Stoppuhr-Instanz wiederzuverwenden. – VVS

+1

Sie könnten noch mehr C# 3.0, und ersetzen Sie diese alt-fashed 'for' Anweisung mit foreach (var i in Enumerable.Range (0, Iterationen)) –

+11

@Jay Ich stimme zu, dass "foreach" mit Enumerable.Range ein wenig aussieht mehr "modern", aber meine Tests zeigen, dass es etwa vier mal langsamer als eine "for" -Schleife über eine große Anzahl ist. YMMV. –

12

Sie könnten versuchen, eine Erweiterungsmethode für jede von Ihnen verwendete Klasse (oder eine beliebige Basisklasse) zu schreiben.

würde ich den Anruf aussehen haben:

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

der Erweiterung Methode Dann:

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

Jedes Objekt von DependencyObject abgeleitet jetzt TimedFor nennen kann (..). Die Funktion kann leicht angepasst werden, um Rückgabewerte über Ref-Parameter zu erhalten.

-

Wenn Sie nicht die Funktionalität wollte jeder Klasse gebunden sein/Objekt, das Sie etwas tun könnte, wie:

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

Dann könnte es Ihnen gefällt verwenden:

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

Wenn dies nicht funktioniert, sieht diese Antwort so aus, als hätte sie eine anständige "generische" Fähigkeit:

Wrapping StopWatch timing with a delegate or lambda?

+0

cool, aber ich interessiere mich nicht für die Art, wie dies an eine bestimmte Klasse oder Basisklasse gebunden ist; Kann es generischer gemacht werden? –

+0

Wie in der MyObject-Klasse, für die die Erweiterungsmethode geschrieben wurde? Es kann leicht geändert werden, um die Object-Klasse oder eine andere Klasse in der Vererbungsstruktur zu erweitern. –

+0

Ich dachte mehr statische, wie nicht an irgendein bestimmtes Objekt oder Klasse gebunden .. Zeit und Timing ist eine Art universell –

1

Sie können eine Reihe von Methoden überlasten Sie verschiedene Fälle von Parametern decken könnte die Lambda übergeben möchten:

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

Alternativ können Sie die Func Delegat verwenden können, wenn sie einen Wert zurückgeben muss. Sie können auch ein Array (oder mehrere) von Parametern übergeben, wenn jede Iteration einen eindeutigen Wert verwenden muss.

7

Ich schrieb vor einiger Zeit eine einfache Codeprofiler-Klasse, die Stoppuhr eingewickelt, ein Verfahren unter Verwendung einer Aktion leicht zum Profil: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

Es wird Sie auch den Code multithreaded zum Profil leicht ermöglichen.Das folgende Beispiel soll die Wirkung lambda mit 1-16 Fäden Profil:

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

Ich mag die CodeTimer Klassen von Vance Morrison (einer der Leistungs Kunden von .NET) verwenden.

Er machte einen Beitrag auf seinem Blog mit dem Titel "Measuring managed code quickly and easiliy: CodeTimers".

Es enthält coole Sachen wie einen MultiSampleCodeTimer. Es berechnet automatisch den Mittelwert und die Standardabweichung und es ist auch sehr einfach, Ihre Ergebnisse auszudrucken.

4

Angenommen, Sie brauchen nur ein schnelles Timing für eine Sache, das ist einfach zu bedienen.

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

Für mich ist die Erweiterung ein bisschen mehr intuitiv auf int fühlt, müssen Sie nicht mehr eine Stoppuhr instanziiert oder Sorgen darüber zurückzusetzen.

Sie haben also:

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

Mit der Probe Nutzung:

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

Beispielausgabe:

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

Hier ist, was ich habe mit:

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

:

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

Dies ist die beste Lösung, die ich je gesehen habe! Keine Erweiterung (so dass es in vielen Klassen verwendet werden kann) und sehr sauber! – Calvin

+0

Ich bin mir nicht sicher, ob ich das Anwendungsbeispiel richtig verstanden habe. Wenn ich versuche, 'Console.WriteLine (" ")' zum Testen unter '// tue Sachen, die ich messen möchte 'zu benutzen, dann ist der Compiler überhaupt nicht glücklich. Solltest du dort normale Ausdrücke und Aussagen machen? – Tim

+0

@Tim - Ich bin sicher, Sie haben es ausgearbeitet, aber die Verwendung Aussage hatte eine fehlende Klammer – Alex

6

Die StopWatch Klasse braucht nicht Disposed oder Stopped auf Fehler zu sein. So ist die einfachste Code Zeit einige Aktion ist

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

Beispiel Telefonvorwahl

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

ich nicht auf die Idee gefällt der Iterationen in den StopWatch Code enthält. Sie können immer eine andere Methode oder Erweiterung erstellen, die die Ausführung von N Iterationen behandelt.

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

Beispielaufrufcode

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

Hier sind die Erweiterungsmethode Versionen

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

Und ruft Beispielcode

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

ich die statischen Methoden und Erweiterungsmethoden getestet (Kamm Iterationen und Benchmark) und das Delta der erwarteten Ausführungszeit und der tatsächlichen Ausführungszeit ist < = 1 ms.

+0

Die Erweiterung Methode Versionen machen mein Mund Wasser. :) – bzlm

Verwandte Themen