एक प्रतिनिधि या मेमने के साथ स्टॉपवॉच समय को लपेटना?


95

मैं इस तरह कोड लिख रहा हूं, थोड़ा जल्दी और गंदा समय कर रहा हूं:

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

निश्चित रूप से टाइमिंग कोड के इस बिट को एक फैंसी-स्कैमेंसी के रूप में कॉल करने का एक तरीका है। NET 3.0 lambda (God forbid) के बजाय इसे कुछ बार काटने और चिपकाने और बदलने के DoStuff(s)साथ DoSomethingElse(s)?

मुझे पता है कि यह किया जा सकता है, Delegateलेकिन मैं लंबोदर रास्ते के बारे में सोच रहा हूं।

जवाबों:


129

स्टॉपवॉच वर्ग के विस्तार के बारे में कैसे?

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

आप एक और अधिभार जोड़ सकते हैं जो "पुनरावृत्तियों" पैरामीटर को छोड़ देता है और इस संस्करण को कुछ डिफ़ॉल्ट मान (जैसे 1000) के साथ कॉल करता है।


3
आप एक ही स्टॉपवॉच आवृत्ति का पुन: उपयोग करते हुए s.Time () के प्रत्येक लगातार कॉल के लिए बीता हुआ समय बढ़ाने के लिए sw.StartNew () के साथ sw.Start () को बदलना चाह सकते हैं।
वीवीएस

11
@ जय मैं सहमत हूं कि Enumerable.Range के साथ "foreach" थोड़ा अधिक "आधुनिक" दिखता है, लेकिन मेरे परीक्षण बताते हैं कि यह एक बड़ी गिनती के लिए "लूप" की तुलना में लगभग चार गुना धीमा है। YMMV।
मैट हैमिल्टन

2
-1: यहां क्लास एक्सटेंशन का इस्तेमाल करने का कोई मतलब नहीं है। Timeएक स्थिर विधि के रूप में व्यवहार करता है, सभी मौजूदा स्थिति को छोड़ देता हैsw , इसलिए इसे एक उदाहरण विधि के रूप में पेश करना सिर्फ बनावटी लगता है।
०११:

2
@ मिल्ज्म मैं सराहना करता हूं कि आपने अपने डाउनवोट को समझाते हुए एक टिप्पणी छोड़ दी, लेकिन मुझे लगता है कि आप विस्तार विधियों के पीछे के विचार को गलत समझ रहे हैं।
मैट हैमिल्टन

4
@ मट हैमिल्टन: मुझे ऐसा नहीं लगता - वे मौजूदा कक्षाओं में (तार्किक रूप से) तरीके जोड़ने के लिए हैं। लेकिन, यह कोई उदाहरण विधि नहीं है Stopwatch.StartNew, जो किसी कारण से स्थिर है। सी # में मौजूदा कक्षाओं में स्थिर तरीकों को जोड़ने की क्षमता का अभाव है (एफ # के विपरीत), इसलिए मैं ऐसा करने के लिए आवेग को समझता हूं, लेकिन यह अभी भी मेरे मुंह में एक बुरा स्वाद छोड़ता है।
दिलदार

31

यहाँ मैं उपयोग कर रहा हूँ:

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
}

यह मैंने कभी देखा सबसे अच्छा समाधान है! कोई विस्तार नहीं (ताकि इसका उपयोग कई वर्गों पर किया जा सके) और बहुत साफ!
केल्विन

मुझे यकीन नहीं है कि मुझे सही तरीके से उपयोग उदाहरण मिला है। जब मैं Console.WriteLine("")परीक्षण के लिए कुछ का उपयोग करने की कोशिश करता हूं // do stuff that I want to measure, तो कंपाइलर बिल्कुल खुश नहीं होता है। क्या आप वहां सामान्य भाव और कथन करना चाहते हैं?
टिम

@ टिम - मुझे यकीन है कि आपने इसे पूरा कर लिया है, लेकिन उपयोग करने वाले बयान में एक लापता ब्रैकेट था
एलेक्स

12

आप जिस भी क्लास (या किसी बेस क्लास) का उपयोग कर रहे हैं, उसके लिए एक एक्सटेंशन विधि लिखने की कोशिश कर सकते हैं।

मुझे कॉल जैसा दिखेगा:

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 (..) कह सकती है। फ़ंक्शन को रेफरी मान के माध्यम से रिटर्न मान प्रदान करने के लिए आसानी से समायोजित किया जा सकता है।

-

यदि आप नहीं चाहते कि कार्यक्षमता किसी वर्ग / वस्तु से बंधे हो तो आप कुछ ऐसा कर सकते हैं:

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

असफल होने पर, यह उत्तर ऐसा लगता है कि इसमें कुछ सभ्य "सामान्य" क्षमता है:

एक प्रतिनिधि या मेमने के साथ स्टॉपवॉच समय को लपेटना?


शांत, लेकिन मुझे इस बात की परवाह नहीं है कि यह एक विशेष वर्ग या आधार वर्ग से जुड़ा हुआ है; क्या यह अधिक उदारतापूर्वक किया जा सकता है?
जेफ एटवुड

जैसे MyObject क्लास में विस्तार विधि के लिए लिखा गया है? विरासत के पेड़ में ऑब्जेक्ट क्लास या अन्य वर्ग का विस्तार करने के लिए इसे आसानी से बदला जा सकता है।
मार्क इनग्राम

मैं और अधिक स्थिर सोच रहा था, जैसे किसी विशेष वस्तु या वर्ग से बंधा हुआ नहीं .. समय और समय सार्वभौमिक है
जेफ एटवुड

बहुत बढ़िया, दूसरा संस्करण वह है जो मैं सोच रहा था, +1, लेकिन मैं मैट को स्वीकार करता हूं क्योंकि वह इसे पहले मिला था।
जेफ एटवुड

7

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

मैंने स्थैतिक तरीकों और विस्तार विधियों (पुनरावृत्तियों और बेंचमार्क को मिलाकर) और अपेक्षित निष्पादन समय और वास्तविक निष्पादन समय के डेल्टा का परीक्षण किया है <= 1 ms।


एक्सटेंशन विधि संस्करण मेरे मुंह का पानी बनाते हैं। :)
बज़ल्म

7

मैंने कुछ समय पहले एक साधारण CodeProfiler वर्ग लिखा था जो स्टॉपवॉच को एक एक्शन का उपयोग करके आसानी से प्रोफ़ाइल करने के लिए लिपटा था: http://www.improve.dk/blog/2008/04/16/profiling-code-the-easy-way

यह भी आसानी से आप कोड multithreaded प्रोफ़ाइल करने की अनुमति देगा। निम्न उदाहरण 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();
}

4

यह मानकर कि आपको बस एक समय की त्वरित आवश्यकता है, यह उपयोग करना आसान है।

  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

आप कई तरीकों को ओवरलोड कर सकते हैं, जिसमें पैरामीटर के विभिन्न मामलों को कवर करने के लिए जिन्हें आप लैम्ब्डा में पास करना चाहते हैं:

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

वैकल्पिक रूप से, आप फ़ंक प्रतिनिधि का उपयोग कर सकते हैं यदि उन्हें एक मूल्य वापस करना होगा। यदि आप प्रत्येक पुनरावृत्ति एक अद्वितीय मान का उपयोग करना चाहते हैं, तो आप एक सरणी (या अधिक) में पास कर सकते हैं।


2

मेरे लिए यह एक्सटेंशन इंट पर थोड़ा अधिक सहज महसूस करता है, अब आपको स्टॉपवॉच को तुरंत चलाने या इसे रीसेट करने की चिंता करने की आवश्यकता नहीं है।

मतलब आपके पास है:

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

के नमूने के उपयोग के साथ:

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

नमूना उत्पादन:

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)

1

मैं वांस मॉरिसन (कोड से प्रदर्शन दोस्तों में से एक) से CodeTimer वर्गों का उपयोग करना पसंद करता हूं।

उन्होंने अपने ब्लॉग पर एक पोस्ट किया जिसका शीर्षक था " मापने वाले कोड को जल्दी और आसानी से प्रबंधित करना: कोडमीटर्स "।

इसमें एक MultiSampleCodeTimer जैसे शांत सामान शामिल हैं। यह माध्य और मानक विचलन की स्वचालित गणना करता है और अपने परिणामों को प्रिंट करने के लिए बहुत आसान भी है।


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;
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.