Written by 16:04 Classes, Languages & Coding

Under the Hood of Stopwatch

Introduction

As all developers, I often need to measure the execution time of my own (and not only my own) code. When I was a beginning programmer, I used the DateTime structure for this purpose. Time have passed and I learned about the Stopwatch class and began using it extensively. I think most of you had a similar experience. It’s not like I didn’t wonder about how Stopwatch works, at that time, it was simply enough for me to know that Stopwatch measures the elapsed time more precise than DateTime. Now the time has come to explain to myself as well as to the readers how the Stopwatch class actually works and clarify its pros and cons in comparison with DateTime.

Using DateTime

Using the DateTime structure for measuring code execution time is pretty simple

var before = DateTime.Now;
SomeOperation();
var spendTime = DateTime.Now - before;

The DateTime.Now property returns the current local date and time. Instead of the DateTime.Now property, you can use DateTime.UtcNow. The property returns the current date and time in the UTC (Universal Time Coordinate) format.

var before = DateTime.UtcNow;
SomeOperation();
var spendTime = DateTime.UtcNow - before;

Few Words on the DateTime Structure

Perhaps, only a few of you wondered what the DateTime structure is really like. The value of the DateTime structure is measured in 100-nanosecond units, that are called ticks, and exact data is represented as a number of ticks starting from 00:00-01-01 AD.

For instance, the number 628539264000000000 stands for 00:00:00, October 6, 1992.

The DateTime contains the only field that contains the number of the past ticks:

private UInt64 dateData;

It is also worth mentioning that as from NET 2.0, two high-order bits of this field indicate the DateTime type: Unspecified – not specified, Utc – coordinate time, Local – local time, and the rest 62 bits – the amount of ticks. We can easily query these two bits with the Kind property.

DateTime Cons

Using the DateTime.Now property for measuring time spans is not a good idea, and that is why:

public static DateTime Now
{
   get
       {
          DateTime utc = DateTime.UtcNow;
          Boolean isAmbiguousLocalDst = false;
          Int64 offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks;
          long tick = utc.Ticks + offset;
          if (tick > DateTime.MaxTicks)
           {
             return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
           }
          if (tick < DateTime.MinTicks)
           {
             return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
           }
             return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
       }
}

Calculating of the DateTime.Now, the property is based on DateTime.UtcNow, i.e. the coordinated time is calculated first, and then the time zone offset is applied to it.

That is why, using DateTime.UtcNow is more correct since it is calculated much faster:

public static DateTime UtcNow
{
  get
    {
       long ticks = 0;
       ticks = GetSystemTimeAsFileTime();
       return new DateTime(((UInt64)(ticks + FileTimeOffset)) | KindUtc);
    }
}

The problem of the DateTime.Now or DateTime.UtcNow usage lies in the fact that their accuracy is fixed. As I wrote above,

1 tick = 100 nanoseconds = 0.1 microseconds = 0.0001 milliseconds = 0.0000001 seconds

Thus, measuring a time span the length of which is less than the length of one tick is simply impossible. Of course, it is unlikely that you will need it, but still, it is a worth knowing thing.

Using the Stopwatch class

The Stopwatch class was introduced in .NET 2.0 and since that time it has never been changed anyhow. It is represented as a set of methods and means that can be used for the exact measurement of the elapsed time.

The public API of the Stopwatch class looks in the following way:

Properties

1. Elapsed – returns total elapsed time;

2. ElapsedMilliseconds – returns total elapsed time in milliseconds;

3. ElapsedTicks – returns total elapsed time in ticks of timer;

4. IsRunning – returns value showing whether the Stopwatch timer is run.

Methods

  1. Reset – stops time span measurement and nulls the elapsed time;
  2. Restart — stops time span measurement, nulls the elapsed time and begins measurement of
    elapsed time;
  3. Start – runs and continues measurement of elapsed time for a span;
  4. StartNew — initializes a new Stopwatch instance, sets the zero value to elapsed time and runs
    measurement of elapsed time;
  5. Stop — stops measurement of the elapsed time for a span.

Fields

  1. Frequency – returns timer frequency as a quantity of ticks per second;
  2. IsHighResolution – specifies, whether timer depends on the high-resolution performance counter.

The code using the Stopwatch class for measurement of the SomeOperation method execution time may look in the following way:

var sw = new Stopwatch(); sw.Start(); SomeOperation(); sw.Stop();

The first two lines may be written more succinctly:

var sw = Stopwatch.StartNew(); SomeOperation(); sw.Stop();

Implementation of Stopwatch

The Stopwatch class is based on HPET (High Precision Event Timer). This timer was introduced by Microsoft for putting the issues of time measurement to rest. The frequency of this timer (min. 10 MHz) is not modified during the system work. For each system, Microsoft determines on its own which devices to use for timer implementation.

The Stopwatch class contains the following fields:

private const long TicksPerMillisecond = 10000;
private const long TicksPerSecond = TicksPerMillisecond * 1000;
        
private bool isRunning;
private long startTimeStamp;
private long elapsed;

private static readonly double tickFrequency;

TicksPerMillisecond — defines the quantity of the DateTime ticks per 1 millisecond

TicksPerSecond – defines the quantity of the DateTime ticks per 1 second

isRunning — defines, whether the current instance is run (whether the Start method was called)

startTimeStamp – number of ticks at the time of run

elapsed — total number of the elapsed ticks

tickFrequency – simplifies conversion of the Stopwatch ticks into the DateTime ticks.

The static constructor checks the availability of the HPET timer and, in the case of its absence, the Stopwatch frequency is set equal to the DateTime frequency.

static Stopwatch() 
{                       
  bool succeeded = SafeNativeMethods.QueryPerformanceFrequency(out Frequency);            
    if(!succeeded) 
     {
        IsHighResolution = false; 
        Frequency = TicksPerSecond;
        tickFrequency = 1;
      }
     else 
     {
        IsHighResolution = true;
        tickFrequency = TicksPerSecond;
        tickFrequency /= Frequency;
     }   
}

The basic operating scenario of this class was shown above: call of the Start method, method time of which must be measured, and then call of the Stop method.

Implementation of the Start method is pretty simple – it remembers the initial quantity of ticks:

public void Start()
{
   if (!isRunning)
     {
       startTimeStamp = GetTimestamp();
       isRunning = true;
     }
}}

It’s worth mentioning, that the call of the Start method on the instance that is already being measured falls flat.

The Stop method is designed in the same simple way:

public void Stop()
{
   if (isRunning)
     {
       long endTimeStamp = GetTimestamp();
       long elapsedThisPeriod = endTimeStamp - startTimeStamp;
       elapsed += elapsedThisPeriod;
       isRunning = false;

       if (elapsed < 0)
        {
          // When measuring small time periods the StopWatch.Elapsed* 
          // properties can return negative values.  This is due to 
          // bugs in the basic input/output system (BIOS) or the hardware
          // abstraction layer (HAL) on machines with variable-speed CPUs
          // (e.g. Intel SpeedStep).

          elapsed = 0;
         }
      }
}

The call of the Stop method on the stopped instance falls flat as well.

Both methods use the call of GetTimestamp() that returns the quantity of ticks at the moment of the call:

public static long GetTimestamp()
{
   if (IsHighResolution)
     {
       long timestamp = 0;
       SafeNativeMethods.QueryPerformanceCounter(out timestamp);
       return timestamp;
     }
     else
      {
         return DateTime.UtcNow.Ticks;
      }
 }

If HPET exists, the Stopwatch ticks differ from the DateTime ticks.

The following code:

Console.WriteLine(Stopwatch.GetTimestamp());
Console.WriteLine(DateTime.UtcNow.Ticks);

looks in the following way on my computer:

5201678165559
635382513439102209

Using the Stopwatch ticks for the creation of DateTime or TimeSpan is incorrect. The record

var time = new TimeSpan(sw.ElaspedTicks);

will obviously lead to the incorrect results.

To get the DateTime ticks, not Stopwatch ones, we need to use the Elapsed properties and ElapsedMilliseconds or make the manual conversion. To convert the Stopwatch ticks into the DateTime ticks, the following method is used in the class:

private long GetElapsedDateTimeTicks()
 {
   long rawTicks = GetRawElapsedTicks();// get Stopwatch ticks
     if (IsHighResolution)
      {
        // convert high resolution perf counter to DateTime ticks
        double dticks = rawTicks;
        dticks *= tickFrequency;
        return unchecked((long)dticks);
      }
      else
      {
        return rawTicks;
      }
}

The properties code looks exactly as is it was expected:

public TimeSpan Elapsed
{
   get { return new TimeSpan(GetElapsedDateTimeTicks()); }
}

public long ElapsedMilliseconds
{
  get { return GetElapsedDateTimeTicks() / TicksPerMillisecond; }
}

Stopwatch Cons

The note to the given class with MSDN states the following: for multiprocessor computers, it does not matter on which processor the stream is executed. However, because of the BIOS errors or in the Hardware Abstraction Layer (HAL), we can get different time calculation results on different processors.

To prevent it, there is the if (elapsed < 0) condition in the Stop method.

I found lots of articles, authors of which faced the problems because of the incorrect work of HPET.

In the case of the HPET Stopwatch absence, the DateTime ticks are used. Therefore, its advantage over the explicit usage of DateTime disappears. Besides, time elapsed on the method calls and checks made by Stopwatch should be taken into account, especially it happens in cycle.

Stopwatch in mono

I became aware of the Stopwatch implementation in mono because I couldn’t rely on the native Microsoft functions for working with HPET.

public static readonly long Frequency = 10000000;
public static readonly bool IsHighResolution = true;

Stopwatch in mono always uses the DateTime ticks, and therefore it does not have any advantage over explicit usage of DateTume, except better readability.

Environment.TickCount

It is worth mentioning the Environment.TickCount property, that returns time, elapsed since the moment of system loading (in milliseconds).

The value of this property is taken from timer and is stored as a 32-bit signed integer. Thus, if the system works continuously, the value of the TickCount property will be increasing from 0 till the Int32.MaxValue value within approximately 24.9 days, and then it will be dropped to the Int32.MinValue value, which is a negative number, and will begin increasing to null within the next 24,9 days.

The usage of this property corresponds to the call of the GetTickCount() system function, that is very fast since it simply returns the value corresponding to the value of the corresponding counter. However, its accuracy is quite low (10 milliseconds), since breaks that are generated by the computer real-time clock are used for increasing counter.

Conclusion

The Windows operating system includes many timers (functions allowing to measure time spans). Ones of them are accurate, but not fast (timeGetTime), others are fast but not accurate (GetTickCount, GetSystemTime), and still others are both, fast and accurate, as Microsoft states. The latter ones include the HPET timer and functions allowing to work with it: QueryPerformanceFrequency, QueryPerformanceCounter. The Stopwatch class is actually a manageable wrapper over HPET. The usage of this class has its pros (more accurate measurement of time spans) and cons (errors in BIOS, HALs can lead to incorrect results), and in the case of HPET absence, its advantages fade away.

To use or not to use the Stopwatch class – it’s strictly up to you. However, I suppose that its pros prevail over its cons.

What is your way to measure code performance?

Tags: , , Last modified: October 18, 2021
Close