Why is the graph spiky when using average?

279 views Asked by At

Im using messageInspectors in my WCF service to measure elapsed time on each service method like this :

public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
    if (_activated)
    {
        _startTime.Stop();
        PerformanceCounterHandler.Instance.SetAveragePerformanceCounter(operationName, _startTime.ElapsedTicks);
    }
}

public object BeforeCall(string operationName, object[] inputs)
{
    Guid correlationState;

    if (_activated)
    {
        correlationState = Guid.NewGuid();
        _startTime = new Stopwatch();
        _startTime.Start();
        return correlationState;
    }
    return null;
}

This is how the counters is registred

foreach (string methodName in ServiceMethodNames)
                {
                    counter = new CounterCreationData(methodName, methodName + " > Genomsnittlig tid(ms)", PerformanceCounterType.AverageTimer32);
                    col.Add(counter);
                    counter = new CounterCreationData(methodName + "_base", methodName + " > Genomsnittlig tid(ms)", PerformanceCounterType.AverageBase);
                    col.Add(counter);
                }

The method for setting the performance counter looks like this :

public Boolean SetAveragePerformanceCounter(string performanceCounterName, long value)
        {
            PerformanceCounter performCounter;

            if (_useOrbitPerformanceCounters && (performCounter = _performanceCounters.Values.Where(c=> c.CounterName == performanceCounterName).FirstOrDefault()) != null)
            {
                performCounter.IncrementBy(value);
                performanceCounterName = performanceCounterName + "_base";
                performCounter = _performanceCounters[performanceCounterName];
                performCounter.Increment();
                return true;
            }
            return false;
        }

The performance counter does however show spikes instead of average? If I change the view to report everything is 0 as long as I do nothing? I need to be able to see how the calltime average looks like right now even if there is no calls for the moment. What am I doing wrong?

PerformanceCounter

1

There are 1 answers

1
Thomas Lielacher On BEST ANSWER

The problem is, that the AverageTimer32 doesn't show some alltime average value. From the documentation:

An average counter that measures the time it takes, on average, to complete a process or operation. Counters of this type display a ratio of the total elapsed time of the sample interval to the number of processes or operations completed during that time. This counter type measures time in ticks of the system clock. Formula: ((N1 - N0)/F)/(B1 - B0), where N1 and N0 are performance counter readings, B1 and B0 are their corresponding AverageBase values, and F is the number of ticks per second.

The interesting part is the formula. The performance counter just shows the average between two performance counter readings. So if no requests where made, the resulting value is 0 because the numerator is 0.

Maybe it's somehow possible to calculate the value on your own and expose it through some other type of performance counter.

EDIT: I wrote a demo application to demonstrate a workaround, but it feels quite messy. I created an NumberOfItems32 performance counter.

var counterDataCollection = new CounterCreationDataCollection();

var averageRandomNumer = new CounterCreationData
{
    CounterType = PerformanceCounterType.NumberOfItems32,
    CounterName = averageRandomNumberCounterName,
    CounterHelp = "Views the average random number."
};
counterDataCollection.Add(averageRandomNumer);

PerformanceCounterCategory.Create(
    categoryName,
    "Displays the various performance counters of a test application",
    PerformanceCounterCategoryType.MultiInstance,
    counterDataCollection);

And set the value like so:

var averageRandomNumberPerfCounter = new PerformanceCounter(categoryName, averageRandomNumberCounterName, "firstInstance", false);
var random = new Random();
var currentAverage = 0d;
var numberOfReadings = 0L;

while (true)
{
    var nextRandom = random.Next(1, 101);

    // ATTENTION: real code should handle overflow properly
    numberOfReadings++;
    currentAverage = (((numberOfReadings - 1) * currentAverage) + nextRandom) / numberOfReadings;

    averageRandomNumberPerfCounter.RawValue = (long)currentAverage;

    Thread.Sleep(1000);
}

The disadvantage of this solution is obvious. Since the performance counter can only store long you lose the decimal places of your average value. Another workaround for this problem would be to scale your values, for example multiplying them with 10 and then choose an lower scaling in performance monitor.