Coder Perfect

Measurement of exact time for performance testing [duplicate]

Problem

What is the best precise way to check how long something took in code, such as a method call?

I believe the simplest and quickest method is as follows:

DateTime start = DateTime.Now;
{
    // Do some work
}
TimeSpan timeItTook = DateTime.Now - start;

However, how precise is this? Are there any other options?

Asked by Svish

Solution #1

Using the Stopwatch class is a better option:

using System.Diagnostics;
// ...

Stopwatch sw = new Stopwatch();

sw.Start();

// ...

sw.Stop();

Console.WriteLine("Elapsed={0}",sw.Elapsed);

Answered by Philippe Leybaert

Solution #2

Stopwatch is a good class to utilize here, as others have said. You can use the following way to wrap it up:

public static TimeSpan Time(Action action)
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    action();
    stopwatch.Stop();
    return stopwatch.Elapsed;
}

(Take note of the Stopwatch.) StartNew(). In terms of simplicity, I prefer this over constructing a Stopwatch and then executing Start().) Obviously, this comes with the cost of invoking a delegate, but that won’t be an issue in the vast majority of circumstances. Then you’d write:

TimeSpan time = StopwatchUtil.Time(() =>
{
    // Do some work
});

You could even make an ITimer interface for this, with StopwatchTimer, CpuTimer, and other implementations as needed.

Answered by Jon Skeet

Solution #3

Stopwatch, as others have stated, should be the appropriate instrument for this. However, there are a few things that could be done to improve it, as evidenced by this thread: Can this implementation be improved by benchmarking small code samples in C#?

I found some helpful advice from Thomas Maierhofer here.

Basically, his code is as follows:

//prevent the JIT Compiler from optimizing Fkt calls away
long seed = Environment.TickCount;

//use the second Core/Processor for the test
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);

//prevent "Normal" Processes from interrupting Threads
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

//prevent "Normal" Threads from interrupting this thread
Thread.CurrentThread.Priority = ThreadPriority.Highest;

//warm up
method();

var stopwatch = new Stopwatch()
for (int i = 0; i < repetitions; i++)
{
    stopwatch.Reset();
    stopwatch.Start();
    for (int j = 0; j < iterations; j++)
        method();
    stopwatch.Stop();
    print stopwatch.Elapsed.TotalMilliseconds;
}

Relying on Process is another option. TotalProcessTime is used to determine how long the CPU has been busy running the code/process in question, as demonstrated here. Because no other process affects the measurement, this can reflect a more realistic scenario. It accomplishes the following:

 var start = Process.GetCurrentProcess().TotalProcessorTime;
 method();
 var stop = Process.GetCurrentProcess().TotalProcessorTime;
 print (end - begin).TotalMilliseconds;

Here’s a bare-bones, thorough implementation of the same thing.

I wrote a helper class to perform both in an easy to use manner:

public class Clock
{
    interface IStopwatch
    {
        bool IsRunning { get; }
        TimeSpan Elapsed { get; }

        void Start();
        void Stop();
        void Reset();
    }



    class TimeWatch : IStopwatch
    {
        Stopwatch stopwatch = new Stopwatch();

        public TimeSpan Elapsed
        {
            get { return stopwatch.Elapsed; }
        }

        public bool IsRunning
        {
            get { return stopwatch.IsRunning; }
        }



        public TimeWatch()
        {
            if (!Stopwatch.IsHighResolution)
                throw new NotSupportedException("Your hardware doesn't support high resolution counter");

            //prevent the JIT Compiler from optimizing Fkt calls away
            long seed = Environment.TickCount;

            //use the second Core/Processor for the test
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);

            //prevent "Normal" Processes from interrupting Threads
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

            //prevent "Normal" Threads from interrupting this thread
            Thread.CurrentThread.Priority = ThreadPriority.Highest;
        }



        public void Start()
        {
            stopwatch.Start();
        }

        public void Stop()
        {
            stopwatch.Stop();
        }

        public void Reset()
        {
            stopwatch.Reset();
        }
    }



    class CpuWatch : IStopwatch
    {
        TimeSpan startTime;
        TimeSpan endTime;
        bool isRunning;



        public TimeSpan Elapsed
        {
            get
            {
                if (IsRunning)
                    throw new NotImplementedException("Getting elapsed span while watch is running is not implemented");

                return endTime - startTime;
            }
        }

        public bool IsRunning
        {
            get { return isRunning; }
        }



        public void Start()
        {
            startTime = Process.GetCurrentProcess().TotalProcessorTime;
            isRunning = true;
        }

        public void Stop()
        {
            endTime = Process.GetCurrentProcess().TotalProcessorTime;
            isRunning = false;
        }

        public void Reset()
        {
            startTime = TimeSpan.Zero;
            endTime = TimeSpan.Zero;
        }
    }



    public static void BenchmarkTime(Action action, int iterations = 10000)
    {
        Benchmark<TimeWatch>(action, iterations);
    }

    static void Benchmark<T>(Action action, int iterations) where T : IStopwatch, new()
    {
        //clean Garbage
        GC.Collect();

        //wait for the finalizer queue to empty
        GC.WaitForPendingFinalizers();

        //clean Garbage
        GC.Collect();

        //warm up
        action();

        var stopwatch = new T();
        var timings = new double[5];
        for (int i = 0; i < timings.Length; i++)
        {
            stopwatch.Reset();
            stopwatch.Start();
            for (int j = 0; j < iterations; j++)
                action();
            stopwatch.Stop();
            timings[i] = stopwatch.Elapsed.TotalMilliseconds;
            print timings[i];
        }
        print "normalized mean: " + timings.NormalizedMean().ToString();
    }

    public static void BenchmarkCpu(Action action, int iterations = 10000)
    {
        Benchmark<CpuWatch>(action, iterations);
    }
}

Just call

Clock.BenchmarkTime(() =>
{
    //code

}, 10000000);

or

Clock.BenchmarkCpu(() =>
{
    //code

}, 10000000);

The Clock’s final section is the most difficult. You can choose what kind of timing you want to display if you want to show the final timing. I created the NormalizedMean extension method, which returns the mean of the read timings after removing the noise. I calculate the deviation of each timing from the actual mean, then discard the values that were farther (only the slower ones) from the mean of deviation (called absolute deviation; note that this is different from the commonly heard standard deviation), and finally return the mean of the remaining values. For example, if the timed values are 1, 2, 3, 2, 100 (in ms or whatever), it discards 100 and returns the mean of the remaining numbers.

public static double NormalizedMean(this ICollection<double> values)
{
    if (values.Count == 0)
        return double.NaN;

    var deviations = values.Deviations().ToArray();
    var meanDeviation = deviations.Sum(t => Math.Abs(t.Item2)) / values.Count;
    return deviations.Where(t => t.Item2 > 0 || Math.Abs(t.Item2) <= meanDeviation).Average(t => t.Item1);
}

public static IEnumerable<Tuple<double, double>> Deviations(this ICollection<double> values)
{
    if (values.Count == 0)
        yield break;

    var avg = values.Average();
    foreach (var d in values)
        yield return Tuple.Create(d, avg - d);
}

Answered by nawfal

Solution #4

Make use of the Stopwatch object.

Answered by mmx

Solution #5

System.Diagnostics. This is when a stopwatch comes in handy.

Answered by Dimi Takis

Post is based on https://stackoverflow.com/questions/969290/exact-time-measurement-for-performance-testing