390

What is the most exact way of seeing how long something, for example a method call, took in code?

The easiest and quickest I would guess is this:

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

But how exact is this? Are there better ways?

Philippe Leybaert
  • 168,566
  • 31
  • 210
  • 223
Svish
  • 152,914
  • 173
  • 462
  • 620
  • 29
    You won't bet on .NET classes because you don't know how they work? Does that mean you're afraid to use the String class also? Anyway, the documentation of the Stopwatch class explicitly says that it is using the QueryPerformanceCounter() Win32 API function. – Philippe Leybaert Jun 09 '09 at 10:58
  • 1
    The String class has nothing to do with this. If Stopwatch exists in .NET how should I know it's better than QueryPerformanceCounter ? which is the best option that can exists !! – Sorin Jun 09 '09 at 11:27
  • 13
    @pixel3cs: downvoting a correct answer because you got criticized in a a comment is not very mature – Philippe Leybaert Jun 09 '09 at 11:36
  • 10
    @pixel3cs But you had time reading the Kernel32 api? – Svish Jun 09 '09 at 12:19

7 Answers7

749

A better way is to use the Stopwatch class:

using System.Diagnostics;
// ...

Stopwatch sw = new Stopwatch();

sw.Start();

// ...

sw.Stop();

Console.WriteLine("Elapsed={0}",sw.Elapsed);
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
Philippe Leybaert
  • 168,566
  • 31
  • 210
  • 223
  • 5
    If you need to know the resolution of the Stopwatch timings on a particular machine then you can use the Stopwatch.Frequency property. – LukeH Jun 09 '09 at 11:03
  • 64
    Also, the Stopwatch.StartNew() static method is a convenient way to both create and start the Stopwatch on a single line. – JessieArr Nov 04 '16 at 18:55
  • 5
    [MS Documentation](https://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch(v=vs.110).aspx) – UpTheCreek Jun 02 '17 at 09:20
196

As others have said, Stopwatch is a good class to use here. You can wrap it in a helpful method:

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

(Note the use of Stopwatch.StartNew(). I prefer this to creating a Stopwatch and then calling Start() in terms of simplicity.) Obviously this incurs the hit of invoking a delegate, but in the vast majority of cases that won't be relevant. You'd then write:

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

You could even make an ITimer interface for this, with implementations of StopwatchTimer, CpuTimer etc where available.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • @JonSkeet When invoking this utility (i.e. as per the second code sample) with in a loop, it appears the call to `action()` incurs an increased cost on the first iteration. Could you explain what is happening here, if that's possible within a comment?! Many thanks.. – Pero P. Oct 07 '11 at 20:15
  • @ppejovic: It's probably the cost of JIT compilation, or possibly the cost of initializing the class used by the lambda expression. Just how big is this "increased cost"? – Jon Skeet Oct 07 '11 at 20:16
  • @JonSkeet Well I'm actually leaving the lambda empty :), so the usage is identical to your sample. It's a difference between about 500+ ticks on the first iteration, and 4 on subsequent. If I'm right, JIT'ing is by default disabled in VS when debugging? I can post this as a question if easier.. cheers – Pero P. Oct 07 '11 at 20:21
  • 3
    @ppejovic: If you're *debugging* then you should ignore all performance results completely. But JIT compilation occurs whether or not you're debugging, just with different optimizations. – Jon Skeet Oct 07 '11 at 20:29
  • Dear Jon, I'm sorry but this method will not give the correct result in every usages. If you use "System.Threading.Tasks.Task" this static method will not measure elapsed time. An ex: TimeSpan timeElapsed = JonSkeetCode.Time(() => Task.Factory.StartNew(() => AddNumberAndPrintAll(5))); Console.WriteLine(timeElapsed); // if you run that two lines of code; code will reach Console.WriteLine line before the measurement. ( AddNumberAndPrintAll is some silly heavy-work method. ) – Lost_In_Library Feb 12 '14 at 14:05
  • 8
    @NAKRO: Well that's because the "work" you've said to do is just "start a new task". And that really *doesn't* take long. So yes, it *does* give the correct result, but you're not measuring what you really want to measure. If you want to measure how long it takes for the task to complete, then you need to wait for it to finish. – Jon Skeet Feb 12 '14 at 14:06
  • @JonSkeet Thank you very much for your quick answer. So, I can't use this method to measure "parallel jobs" ? -Or- Am I making a mistake with using Task.Factory.StartNew ? Thanks again... – Lost_In_Library Feb 12 '14 at 14:11
  • 5
    @NAKRO: Well you can, but you need to make sure that the "action" involved both starts all the tasks *and waits for them to finish*. – Jon Skeet Feb 12 '14 at 14:11
93

As others said, Stopwatch should be the right tool for this. There can be few improvements made to it though, see this thread specifically: Benchmarking small code samples in C#, can this implementation be improved?.

I have seen some useful tips by Thomas Maierhofer here

Basically his code looks like:

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

Another approach is to rely on Process.TotalProcessTime to measure how long the CPU has been kept busy running the very code/process, as shown here This can reflect more real scenario since no other process affects the measurement. It does something like:

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

A naked, detailed implementation of the samething can be found here.

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 last part of the Clock is the tricky part. If you want to display the final timing, its up to you to choose what sort of timing you want. I wrote an extension method NormalizedMean which gives you the mean of the read timings discarding the noise. I mean I calculate the the deviation of each timing from the actual mean, and then I discard the values which was farer (only the slower ones) from the mean of deviation (called absolute deviation; note that its not the often heard standard deviation), and finally return the mean of remaining values. This means, for instance, if timed values are { 1, 2, 3, 2, 100 } (in ms or whatever), it discards 100, and returns the mean of { 1, 2, 3, 2 } which is 2. Or if timings are { 240, 220, 200, 220, 220, 270 }, it discards 270, and returns the mean of { 240, 220, 200, 220, 220 } which is 220.

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);
}
Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 1
    Good details on controlling the environment and ignoring the spikes! Thanks. – asgerhallas Jun 07 '13 at 18:45
  • 3
    In the original example, the `long seed = Environment.TickCount;` was used as input to the algorithm under test, [in order to make it non-deterministic](https://www.codeproject.com/Articles/61964/Performance-Tests-Precise-Run-Time-Measurements-wi?msg=4548708#xx4548708xx) and prevent it from being evaluated at compile-time. That seed is not being used here. – piedar Nov 07 '19 at 16:55
  • what about the `values.Deviations()`method.. ? (maybe I can do it myself but it would be nice with the experts input) – Erik Thysell May 06 '20 at 07:08
  • I liked the proposed the solution of using the actual CPU time, only note that the `TotalProcessorTime` has a "low" resolution. See also [How we can reduce the resolution of myProcess.TotalProcessorTime?](https://stackoverflow.com/questions/29676704/how-we-can-reduce-the-resolution-of-myprocess-totalprocessortime) – RuudSieb Jan 19 '22 at 09:19
15

Use the Stopwatch class

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
13

System.Diagnostics.Stopwatch is designed for this task.

Dimi Takis
  • 4,924
  • 3
  • 29
  • 41
5

Stopwatch is fine, but loop the work 10^6 times, then divide by 10^6. You'll get a lot more precision.

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
2

I'm using this:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(myUrl);
System.Diagnostics.Stopwatch timer = new Stopwatch();

timer.Start();

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

statusCode = response.StatusCode.ToString();

response.Close();

timer.Stop();
guneysus
  • 6,203
  • 2
  • 45
  • 47
altansezerayan
  • 177
  • 1
  • 7