Piszę kod w ten sposób, robiąc szybki i brudny czas:
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000; i++)
{
b = DoStuff(s);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Z pewnością istnieje sposób, aby wywołać ten kawałek kodu czasowego jako fantazyjne-schmancy .NET 3.0 lambda zamiast (nie daj Boże) wycinanie i wklejanie go kilka razy i zastępując DoStuff(s)
zDoSomethingElse(s)
?
Wiem, że można to zrobić jako, Delegate
ale zastanawiam się nad sposobem lambdy.
Time
zachowuje się jak metoda statyczna, odrzucając cały istniejący stan wsw
, więc wprowadzenie jej jako metody instancji wygląda po prostu sztucznie.Stopwatch.StartNew
, która z jakiegoś powodu jest statyczna. C # nie ma możliwości dodawania statycznych metod do istniejących klas (w przeciwieństwie do F #), więc rozumiem impuls, aby to zrobić, ale nadal pozostawia to zły smak w moich ustach.Oto, czego używałem:
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); } }
Stosowanie:
using (new DisposableStopwatch(t => Console.WriteLine("{0} elapsed", t))) { // do stuff that I want to measure }
źródło
Console.WriteLine("")
do testowania pod// do stuff that I want to measure
, kompilator wcale nie jest zadowolony. Czy masz tam robić normalne wyrażenia i instrukcje?Możesz spróbować napisać metodę rozszerzającą dla dowolnej używanej klasy (lub dowolnej klasy bazowej).
Wyglądałoby to tak:
Stopwatch sw = MyObject.TimedFor(1000, () => DoStuff(s));
Następnie metoda rozszerzenia:
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; }
Każdy obiekt pochodzący z DependencyObject może teraz wywołać TimedFor (..). Funkcję można łatwo dostosować, aby zwracała wartości za pomocą parametrów odniesienia.
-
Jeśli nie chcesz, aby funkcja była powiązana z jakąkolwiek klasą / obiektem, możesz zrobić coś takiego:
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; } }
Wtedy możesz go użyć tak:
Stopwatch sw = Timing.TimedFor(() => DoStuff(s), 1000);
W przeciwnym razie ta odpowiedź wygląda na przyzwoitą „ogólną” zdolność:
Zawijanie czasu StopWatch z delegatem lub lambdą?
źródło
StopWatch
Klasa nie musi byćDisposed
alboStopped
w przypadku błędu. Tak, to najprostszy kod do czasu niektóre działania jestpublic partial class With { public static long Benchmark(Action action) { var stopwatch = Stopwatch.StartNew(); action(); stopwatch.Stop(); return stopwatch.ElapsedMilliseconds; } }
Przykładowy kod wywołujący
public void Execute(Action action) { var time = With.Benchmark(action); log.DebugFormat(“Did action in {0} ms.”, time); }
Nie podoba mi się pomysł włączenia iteracji do
StopWatch
kodu. Zawsze możesz utworzyć inną metodę lub rozszerzenie, które obsługuje wykonywanieN
iteracji.public partial class With { public static void Iterations(int n, Action action) { for(int count = 0; count < n; count++) action(); } }
Przykładowy kod wywołujący
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); }
Oto wersje metod rozszerzenia
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); } }
I przykładowy kod wywołujący
public void Execute(Action action, int n) { var time = action.Iterations(n).Benchmark() log.DebugFormat(“Did action {0} times in {1} ms.”, n, time); }
Przetestowałem metody statyczne i metody rozszerzające (łącząc iteracje i benchmark), a delta oczekiwanego czasu wykonania i rzeczywistego czasu wykonania wynosi <= 1 ms.
źródło
Jakiś czas temu napisałem prostą klasę CodeProfiler, która opakowała Stopwatch, aby łatwo profilować metodę za pomocą Action: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way
Pozwoli to również łatwo profilować kod wielowątkowy. Poniższy przykład przedstawi profil lambda akcji z 1-16 wątkami:
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(); }
źródło
Zakładając, że potrzebujesz tylko szybkiego pomiaru czasu, jest to łatwe w użyciu.
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; } } }
źródło
Możesz przeciążać wiele metod, aby objąć różne przypadki parametrów, które możesz chcieć przekazać do 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; }
Alternatywnie możesz użyć delegata Func, jeśli musi zwrócić wartość. Możesz również przekazać tablicę (lub więcej) parametrów, jeśli każda iteracja musi używać unikatowej wartości.
źródło
Dla mnie rozszerzenie wydaje się nieco bardziej intuicyjne w int, nie musisz już tworzyć instancji Stopwatch ani martwić się o jego zresetowanie.
Więc masz:
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); } }
Przy przykładowym użyciu:
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); } });
Przykładowe dane wyjściowe:
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)
źródło
Lubię używać klas CodeTimer od Vance'a Morrisona (jednego z kolesi od wydajności z .NET).
Na swoim blogu zamieścił post zatytułowany „ Szybki i łatwy pomiar kodu zarządzanego: CodeTimers ”.
Zawiera fajne rzeczy, takie jak MultiSampleCodeTimer. Automatycznie oblicza średnią i odchylenie standardowe, a także bardzo łatwo drukuje wyniki.
źródło
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; } }
źródło