Understanding Average Performance Counter in .NET

For the current project I am working on, I recently had to implement a way of easy adding and using Performance Counters in .NET. While working on the code base, I implemented various counters as examples how to use the new infrastructure and how to implement counters in the code base.

While investigating in performance counters, I’ve seen quite a series of posts and articles describing the usage of the AverageTimer32 and AverageTimer64 classes. However, all the examples there seemed to be wrong. One of these examples was a question I have answered on stackoverflow.com, leading to this post.

Basically, all the examples I have seen propose to throw a set of measurements into the mentioned expecting that the counter provides the average of these measurements. The AverageTimer32/64, however, does not calculate the average of all measurements you perform. Instead it provides the ration of your measurements to the number of operations you provide.

To understand how the AverageTimer32/64 works, it might be helpful to understand the formula behind it. This also answers why one needs an AverageBase to use an AverageTimer32/64.

The formula the AverageTimer32/64 is based on is as following:

((N1 - N0) / F) / (B1 - B0)

given
N1 current reading at time t (provided to the AverageTimer32/64)
N0 reading before, at t – 1 (provided to the AverageTimer32/64)
B1 current counter at t (provided to the AverageBase)
B0 counter before, at t – 1 (provided to the AverageBase)
F Factor to calculate ticks/seconds

In a nutshell the formula takes the current time in ticks and subtracts the previous one provided. The result divided by the factor F gives you the time you operation run since the last measurement taken at t-1. Usually, this factor should be 10.000.000 ticks per second.

Now you divide this by the current base counter minus the previous base counter provided. Usually, this might be one. As a result you have the average time of your operation for a single measurement.

Using the AverageBase you now can step over various measurement points. Think of a case where you can set the counter only every tenth operation you perform. Since your last measurement you would increment the AverageTimer32/64 by the new time measurement for all ten operations while incrementing the AverageBase by ten. Eventually, you will receive the average time for one operation (even if you have measured over all ten operation calls).

In most examples, a set of timespans are provided  for this counter to calculate an average value. Let this be a series of numbers like 10, 9, 8, 7 ,6 while increasing the AverageBase by 1 every time providing one of these figures.

For the second measurement you will receive the following result:

(9 – 10) / F / (1 – 0) = -1 / F / 1

With F being 1 for simplicity you will get -1 as result. Given measurements that provide most of the time similar results, for a large number of experiments you will end up with am average value near zero.

Based on the previous example, the correct values to submit, however should be 10, 19, 27, 34, 40.  Again the same example we will show a different result.

(19 – 10) / F / (1 – 0) = 9 / F / 1

With F being 1 again, you will have an average time of 9 for your second measurement. As you can see from the formula, the every value measured needs to be greater than the previous one to avoid the effect previously showed.

You might use a global Stopwatch to achieve this goal. Instead of starting it new, you might use use Start() – not Restart() for each measurement. As seen above, the counter will calculate the difference time internally. That way you will get correct measurements.

public void Compute()
{
_stopwatch.Start(); // do not restart the Stopwatch used for average counters


// code to be measured
// ...


_stopwatch.Stop();


_avgTimeCounter.IncrementBy(_stopwatch.ElapsedMilliseconds);
_avgTimeCounterBase.Increment();
}

PerformanceCounterDe

Even if called AverageTimer32/64, this type of counter is not strictly restricted to time. You can think of using this counter for a variety of measurements. For example 404 responses in relation to the total number of HTTP requests,  disk transfer rations  and so on.

One thought on “Understanding Average Performance Counter in .NET

  1. In “Instead it provides the ration of your measurements” you probably meant “ratio” (also in the last paragraph).

    I think you glossed over the fact that AverageTimer* expects “measurements” to be sequential time stamps, and as such providing values that may decrease over time will lead to undefined behavior.

    The problem with most examples (and also in the MSDN documentation, which is surprisingly hard to understand, and specifically for AverageTimer shows a misuse of the mechanism by setting the RawValue) is that they show doing a single run and then stopping. Assuredly, as the main use of PerformanceCounter is to feed the Windows Performance Monitor, there are a lot of things going on behind the scenes when you use PerformanceCounter (hence all the warnings you see everywhere about setting up the categories just right, creating counters in the correct order, etc) and the single-run use case is managed well by the Windows infrastructure that aggregates the results across multiple runs.

    For measuring performance consistently in the same process over time – for example when you have an always-on service whose performance you want to measure in real time – it becomes more complicated. For such cases, the mechanisms provided by Windows performance counters are next to useless (in my opinion) as average performance hardly tells you what is really going on inside. Something much more useful would be to measure average performance vs. 95% percentile performance vs max time – this gives you a much better understanding of a multi-transactional process, for example – an HTTP service that returns responses to clients. If your average performance is good, but your max timeout is very high and your 95% performance is closer to the max than to the average then it tells you that many of your users are getting unacceptably slow behavior, while if your average is on the high side but your 95% is close to the average, then it tells you that while all of your users are getting so-so performance, you don’t leave many users out in the cold outliers of performance.

Leave a Reply