Bir temsilci veya lambda ile StopWatch zamanlamasını sarma?


96

Bunun gibi kod yazıyorum, biraz hızlı ve kirli zamanlama yapıyorum:

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

Şüphesiz kesme ve bunu bir kaç kez yapıştırarak ve değiştirme (Allah korusun) yerine bir fantezi-schmancy .NET 3.0 lambda olarak zamanlama bu kod bit aramak için bir yol var DoStuff(s)olan DoSomethingElse(s)?

Bunun yapılabileceğini biliyorum Delegateama lambda yolunu merak ediyorum.

Yanıtlar:


129

Kronometre sınıfını genişletmeye ne dersiniz?

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

Öyleyse şöyle deyin:

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

"Yinelemeler" parametresini atlayan ve bu sürümü bazı varsayılan değerlerle (1000 gibi) çağıran başka bir aşırı yük ekleyebilirsiniz.


3
Her ardışık s.Time () çağrısı için geçen sürenin kazara artmasını önlemek ve aynı Kronometre örneğini yeniden kullanmak için sw.Start () öğesini sw.StartNew () ile değiştirmek isteyebilirsiniz.
— VVS

11
@Jay Enumerable.Range ile "foreach" in biraz daha "modern" göründüğüne katılıyorum, ancak testlerim bunun "for" döngüsünden yaklaşık dört kat daha yavaş olduğunu gösteriyor. YMMV.
— Matt Hamilton

2
-1: Burada bir sınıf uzantısı kullanmak mantıklı değil. Timestatik bir yöntem gibi davranır, içindeki tüm mevcut durumu atar sw, bu nedenle onu bir örnek yöntem olarak tanıtmak sadece hileli görünür.
— ildjarn

2
@ildjam Olumsuz oyunuzu açıklayan bir yorum bıraktığınız için minnettarım, ancak uzatma yöntemlerinin arkasındaki fikri yanlış anladığınızı düşünüyorum.
— Matt Hamilton

4
@Matt Hamilton: Sanmıyorum - var olan sınıflara (mantıksal olarak) örnek yöntemleri eklemek içindir. Ancak bu, Stopwatch.StartNewbir nedenden ötürü statik olan bir örnek yöntem değildir . C #, mevcut sınıflara statik yöntemler ekleme yeteneğinden yoksundur (F #'ın aksine), bu nedenle bunu yapma dürtüsünü anlıyorum, ancak yine de ağzımda kötü bir tat bırakıyor.
— ildjarn

33

İşte kullandığım şey:

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

Kullanım:

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

Bu şimdiye kadar gördüğüm en iyi çözüm! Uzantı yok (böylece birçok sınıfta kullanılabilir) ve çok temiz!
— Calvin

Kullanım örneğini doğru aldığımdan emin değilim. Bazılarını Console.WriteLine("")altında test etmek için kullanmaya çalıştığımda // do stuff that I want to measure, derleyici hiç memnun değil. Orada normal ifadeler ve ifadeler yapmanız gerekiyor mu?
— Tim

@Tim - Eminim çözmüşsünüzdür, ancak using ifadesinde eksik bir parantez vardı
— Alex

12

Kullandığınız sınıf (veya herhangi bir temel sınıf) için bir uzantı yöntemi yazmayı deneyebilirsiniz.

Aramanın şöyle görünmesini isterdim:

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

Ardından uzantı yöntemi:

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

DependencyObject'ten türetilen herhangi bir nesne artık TimedFor (..) 'u çağırabilir. İşlev, ref parametreleri aracılığıyla dönüş değerleri sağlamak için kolayca ayarlanabilir.

-

İşlevselliğin herhangi bir sınıfa / nesneye bağlanmasını istemediyseniz, aşağıdaki gibi bir şey yapabilirsiniz:

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

O zaman bunu şu şekilde kullanabilirsiniz:

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

Başarısız olsa da, bu yanıt iyi bir "genel" yeteneğe sahip gibi görünüyor:

Bir temsilci veya lambda ile StopWatch zamanlamasını sarma?


havalı, ancak bunun belirli bir sınıfa veya temel sınıfa bağlanma şekli umrumda değil; daha genel bir şekilde yapılabilir mi?
— Jeff Atwood

Uzantı yönteminin yazıldığı MyObject sınıfında olduğu gibi? Miras ağacındaki Object sınıfını veya diğer sınıfı genişletmek için kolayca değiştirilebilir.
— Mark Ingram

HİÇBİR belirli bir nesneye veya sınıfa bağlı olmamak gibi daha durağan düşünüyordum .. zaman ve zamanlama bir tür evrenseldir
— Jeff Atwood

Mükemmel, 2. versiyon daha çok düşündüğüm şey, +1, ama Matt'e ilk geldiğinde kabul ettim.
— Jeff Atwood

7

StopWatchSınıf olmak gerekmez Disposedveya Stoppedhata. Bu nedenle, bazı eylemleri zamanlamak için en basit kod ,

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

Örnek arama kodu

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

Yinelemeleri StopWatchkoda dahil etme fikri hoşuma gitmiyor . Her zaman Nyinelemelerin yürütülmesini işleyen başka bir yöntem veya uzantı oluşturabilirsiniz .

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

Örnek arama kodu

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

İşte uzantı yöntemi sürümleri

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

Ve örnek arama kodu

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

Statik yöntemleri ve uzantı yöntemlerini (yinelemeleri ve karşılaştırmayı birleştiren) test ettim ve beklenen yürütme süresi ve gerçek yürütme süresinin deltası <= 1 ms.


Uzatma yöntemi versiyonları ağzımı sulandırıyor. :)
— bzlm

7

Bir süre önce, bir Eylemi kullanarak bir yöntemi kolayca profillemek için Kronometre'yi sarmalayan basit bir CodeProfiler sınıfı yazdım: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

Ayrıca, çok iş parçacıklı kodun profilini kolayca oluşturmanıza olanak sağlar. Aşağıdaki örnek, eylem lambda'nın 1-16 iş parçacığı ile profilini oluşturacaktır:

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

4

Tek bir şeyin hızlı bir zamanlamasına ihtiyacınız olduğunu varsayarsak, bunun kullanımı kolaydır.

  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

Lambda'ya iletmek isteyebileceğiniz çeşitli parametre durumlarını kapsayan bir dizi yöntemi aşırı yükleyebilirsiniz:

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

Alternatif olarak, bir değer döndürmeleri gerekiyorsa Func temsilcisini kullanabilirsiniz. Her yinelemenin benzersiz bir değer kullanması gerekiyorsa, bir dizi (veya daha fazla) parametre de iletebilirsiniz.


2

Benim için uzantı int'te biraz daha sezgisel geliyor, artık bir Kronometreyi başlatmanıza veya sıfırlama konusunda endişelenmenize gerek yok.

Yani sizde:

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

Örnek kullanım ile:

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

Örnek çıktı:

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)


0
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;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.