[c#] 델리게이트 또는 람다로 스톱워치 타이밍을 래핑 하시겠습니까?

나는 약간 빠르고 더러운 타이밍을 사용하여 이와 같은 코드를 작성하고 있습니다.

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

확실히이 타이밍 코드를 몇 번 자르고 붙여넣고 DoStuff(s)DoSomethingElse(s)?

나는 그것이 할 수 있다는 것을 알고 Delegate있지만 람다 방식에 대해 궁금합니다.



답변

스톱워치 클래스를 확장하는 것은 어떻습니까?

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

그런 다음 다음과 같이 호출하십시오.

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

“iterations”매개 변수를 생략하고 일부 기본값 (예 : 1000)으로이 버전을 호출하는 또 다른 오버로드를 추가 할 수 있습니다.


답변

내가 사용한 것은 다음과 같습니다.

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

용법:

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


답변

사용중인 클래스 (또는 기본 클래스)에 대해 확장 메서드를 작성해 볼 수 있습니다.

전화는 다음과 같습니다.

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

그런 다음 확장 방법 :

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에서 파생 된 모든 개체는 이제 TimedFor (..)를 호출 할 수 있습니다. 함수는 ref 매개 변수를 통해 반환 값을 제공하도록 쉽게 조정할 수 있습니다.

기능이 클래스 / 객체에 연결되는 것을 원하지 않는 경우 다음과 같이 할 수 있습니다.

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

그런 다음 다음과 같이 사용할 수 있습니다.

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

실패하면이 답변은 괜찮은 “일반적인”능력을 가지고있는 것처럼 보입니다.

델리게이트 또는 람다로 스톱워치 타이밍을 래핑 하시겠습니까?


답변

StopWatch클래스 일 필요는 없습니다 Disposed또는 Stopped오류에. 그래서, 간단한 코드에 시간이 몇 가지 조치가 있다

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

샘플 호출 코드

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

StopWatch코드에 반복을 포함하는 아이디어가 마음에 들지 않습니다 . N반복 실행을 처리하는 다른 메서드 또는 확장을 언제든지 만들 수 있습니다 .

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

샘플 호출 코드

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

다음은 확장 메서드 버전입니다.

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

그리고 샘플 호출 코드

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

정적 메서드와 확장 메서드 (반복 및 벤치 마크 결합)를 테스트했으며 예상 실행 시간과 실제 실행 시간의 델타는 <= 1ms입니다.


답변

Action을 사용하여 메서드를 쉽게 프로파일 링하기 위해 Stopwatch를 래핑 한 간단한 CodeProfiler 클래스를 얼마 전에 작성했습니다.
http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

또한 다중 스레드 코드를 쉽게 프로파일 링 할 수 있습니다. 다음 예제는 1-16 개의 스레드로 작업 람다를 프로파일 링합니다.

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


답변

한 가지 빠른 타이밍이 필요하다고 가정하면 사용하기 쉽습니다.

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


답변

여러 메서드를 오버로드하여 람다에 전달할 수있는 다양한 매개 변수 사례를 처리 할 수 ​​있습니다.

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

또는 값을 반환해야하는 경우 Func 대리자를 사용할 수 있습니다. 각 반복이 고유 한 값을 사용해야하는 경우 매개 변수의 배열 (또는 그 이상)을 전달할 수도 있습니다.