简体   繁体   中英

C# Performance Penalty from Adding Stop Watch?

将 C# Stop Watch 添加到对象会导致什么性能损失?

Should not be that significant in the context of C# programming. If it proves to be significant, reconsider your need/use of Stopwatch and C#.

You can always try to benchmark it yourself by implementing it 1000 times, timing it, and then dividing the results by 1000. It's difficult to say precisely how performance demanding this feature is, but you could compare it to some other simple operations and see how it relates to.

Since I desired to use some timing statistics in an object creation pool to optimize its performance on-the-fly, I needed to know if adding a Stopwatch to monitor the timing would adversely affect the performance of the object creation. I wanted to time a typical object creation (appr. 20-30 ms) to optimize the timeout for the Task creating them, but only if adding the Stopwatch did not noticeably slow down the loop.

So I wrote this test program to separately time the overhead of creation, Start() , Stop() and elapsed time retrieval of the Stopwatch .

using System;
using System.Diagnostics;

namespace SO9925598_Stopwatch_overhead
{
    class Program
    {
        private const int nLoops = 10000;
        private const double nLoopsDouble = nLoops;
        private const int waitTime = 2; //ms

        static void Main(string[] args)
        {
            Stopwatch stopwatchTimingTheTest = new Stopwatch();
            double frequencyConversionFactor = 1000.0 / Convert.ToDouble(Stopwatch.Frequency);  // ms per click
            Stopwatch stopwatchUnderTest;

            // Instantiation
            for (int i = 0; i < nLoops; i++)
            {
                stopwatchTimingTheTest.Start();
                stopwatchUnderTest = new Stopwatch();
                stopwatchTimingTheTest.Stop();
                stopwatchUnderTest = null;
            }
            long elapsed = stopwatchTimingTheTest.ElapsedTicks;
            double overhead = frequencyConversionFactor * (Convert.ToDouble(elapsed) / nLoopsDouble);
            Console.WriteLine($"Creation overhead {overhead} ms per Stopwatch instantiation. (Based on {nLoops} trials).");

            // Further tests can all use the same object
            stopwatchUnderTest = new Stopwatch();

            // Start
            stopwatchTimingTheTest.Reset();
            for (int i = 0; i < nLoops; i++)
            {
                stopwatchTimingTheTest.Start();
                stopwatchUnderTest.Start();
                stopwatchTimingTheTest.Stop();
                stopwatchUnderTest.Stop();
                elapsed = stopwatchUnderTest.ElapsedTicks;
            }
            elapsed = stopwatchTimingTheTest.ElapsedTicks;
            overhead = frequencyConversionFactor * (Convert.ToDouble(elapsed) / nLoopsDouble);
            Console.WriteLine($"Stopwatch.Start() overhead {overhead} ms.");

            // Stop
            stopwatchTimingTheTest.Reset();
            for (int i = 0; i < nLoops; i++)
            {
                stopwatchUnderTest.Start();
                stopwatchTimingTheTest.Start();
                stopwatchUnderTest.Stop();
                stopwatchTimingTheTest.Stop();
                elapsed = stopwatchUnderTest.ElapsedTicks;
            }
            elapsed = stopwatchTimingTheTest.ElapsedTicks;
            overhead = frequencyConversionFactor * (Convert.ToDouble(elapsed) / nLoopsDouble);
            Console.WriteLine($"Stopwatch.Stop() overhead {overhead} ms.");

            // Elapsed ticks
            stopwatchTimingTheTest.Reset();
            for (int i = 0; i < nLoops; i++)
            {
                stopwatchUnderTest.Start();
                stopwatchUnderTest.Stop();
                stopwatchTimingTheTest.Start();
                elapsed = stopwatchUnderTest.ElapsedTicks;
                stopwatchTimingTheTest.Stop();
            }
            elapsed = stopwatchTimingTheTest.ElapsedTicks;
            overhead = frequencyConversionFactor * (Convert.ToDouble(elapsed) / nLoopsDouble);
            Console.WriteLine($"Stopwatch.ElapsedTicks overhead {overhead} ms.");

            // Elapsed ticks
            stopwatchTimingTheTest.Reset();
            for (int i = 0; i < nLoops; i++)
            {
                stopwatchUnderTest.Start();
                stopwatchUnderTest.Stop();
                stopwatchTimingTheTest.Start();
                elapsed = stopwatchUnderTest.ElapsedMilliseconds;
                stopwatchTimingTheTest.Stop();
            }
            elapsed = stopwatchTimingTheTest.ElapsedTicks;
            overhead = frequencyConversionFactor * (Convert.ToDouble(elapsed) / nLoopsDouble);
            Console.WriteLine($"Stopwatch.ElapsedMilliseconds overhead {overhead} ms.");

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }
    }
}

These were the results:

Creation overhead 0,00011756 ms per Stopwatch instantiation. (Based on 10000 trials).
Stopwatch.Start() overhead 0,000256 ms.
Stopwatch.Stop() overhead 0,00023665 ms.
Stopwatch.ElapsedTicks overhead 0,00010946 ms.
Stopwatch.ElapsedMilliseconds overhead 0,00011758 ms.

The times are the average time per single call of the method based on 10000 samples.

The program was running on a machine with Intel Core i7-8850H CPU at 2.60 GHz with Windows 10 operating system.

Conclusion: for my application where the object creation is 20-30 ms the overhead of the Stopwatch is negligible.

  static void Main(string[] args)
        {
            Worker(1); // jit Warmup
            var stopWatchOfStopwatchStopwatch = System.Diagnostics.Stopwatch.StartNew();
            var stopWatchOfLoop = System.Diagnostics.Stopwatch.StartNew();
            Worker(100000000);
            stopWatchOfLoop.Stop();
            Console.WriteLine("Elapsed of inner SW: " + stopWatchOfLoop.Elapsed.ToString());
            stopWatchOfStopwatchStopwatch.Stop();
            Console.WriteLine("Elapsed of outer SW: " + stopWatchOfStopwatchStopwatch.Elapsed.ToString());

            var stopwatchOfcompareLoop = System.Diagnostics.Stopwatch.StartNew();
            Worker(100000000);
            stopwatchOfcompareLoop.Stop();
            Console.WriteLine("Elapsed of inner SW: " + stopWatchOfLoop.Elapsed.ToString());
        }
        static void Worker(int iterations)
        {
            for (int i = 0; i < iterations; i++)
            {
                Console.WriteLine("bla");
            }
        }

The difference is insignificant - but that pretty much depends on your performance goals :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM