tags:

views:

681

answers:

4

I'm trying to measure the time it takes to execute a piece of code on my production server. I'd like to monitor this information in real time, so I decided to give Performance Analyser a whizz. I understand from MSDN that I need to create both an AverageTimer32 and an AverageBase performance counter, which I duly have. I increment the counter in my program, and I can see the CallCount go up and down, but the AverageTime is always zero. What am I doing wrong? Thanks!

Here's a snippit of code :

long init_call_time = Environment.TickCount;

// ***
// Lots and lots of code...
// ***

// Count number of calls
PerformanceCounter perf = new PerformanceCounter("Cat", "CallCount", "Instance", false);
perf.Increment();
perf.Close();

// Count execution time
PerformanceCounter perf2 = new PerformanceCounter("Cat", "CallTime", "Instance", false);
perf2.NextValue();
perf2.IncrementBy(Environment.TickCount - init_call_time);
perf2.Close();

// Average base for execution time
PerformanceCounter perf3 = new PerformanceCounter("Cat", "CallTimeBase", "Instance",     false);
perf3.Increment();
perf3.Close();

perf2.NextValue();
+1  A: 

First off, attaching to performance counters is quite expensive, so you should try and keep global instances live to them instead of opening and closing them each time.

You seem to have the right idea, thats pretty much what we do in our performance monitoring code. However we do not do .NextValue immediately prior to updating these - so I'd try stopping doing that initially.

Are you also sure that Environment.TickCount - init_call_time isnt such a small period of time that it evaluates to 0? Environment.TickCount doesnt have great resolution, the System.Diagnostics.Stopwatch class has much better accuracy.

Kieran Benton
>> you should try and keep global instancesHow would you suggest doing this in a web application? Should I perhaps add some code to Application_Start to instantiate the counters and store them in an Application variable?
Patrick J Collins
The average execution time is about 1.5 secs - 3 secs depending, so I assume that is long enough for Environment.TickCount, however I will try the stop watch.
Patrick J Collins
+1  A: 

Assuming you are not multi threaded then this should be

// cached somewhere
var perf2 = new PerformanceCounter("Cat", "CallTime", "Instance", false);
var sw = new Stopwatch();

// where ever you are setting init_call_time
sw.Start();

// then when the task has finished
sw.Stop();
perf2.RawValue = sw.ElapsedMilliseconds; // or tick, whatever you want to use

If you are in a multi threaded situation then:

// cached somewhere
var perf2 = new PerformanceCounter("Cat", "CallTime", "Instance", false);

[ThreadStatic]
Stopwatch sw;

// where ever you are setting init_call_time
if (sw == null)
    sw = new Stopwatch();
sw.Start();

// then when the task has finished
sw.Stop();
perf2.IncrementBy(sw.ElapsedMilliseconds); // or tick, whatever you want to use
sw.Reset();
ShuggyCoUk
This is an ASP.net application hosted in IIS, so is setting RawValue still a good idea?
Patrick J Collins
You would want to use increment resetting the stop watch each time and have a stopwatch per thread (thread static would do)
ShuggyCoUk
A: 

As Kieran already pointed out, adding perfomance counters is not something you should do in runtime. The recommended manner is to create an executable that registers the performance counters, then later you can reference these custom counters from your application. Here is a sample of how such a executable could look:

using System;
using System.Diagnostics;

namespace RegisterPerformanceCounters
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("(R)egister counters?");
            Console.WriteLine("(D)elete counters?");
            Console.WriteLine("(C)ancel?");

            switch (Console.ReadLine().ToUpper())
            {
                case "R":
                    Console.WriteLine("Registering counters...");
                    CreateCounters();
                    break;
                case "D":
                    Console.WriteLine("Deleting counters...");
                    DeleteCounters();
                    break;
                case "C":
                    break;
                default:
                    Console.WriteLine("Invalid choise.");
                    break;
            }

            Console.WriteLine("Done.");
            Console.ReadLine();
        }

        static void CreateCounters()
        {
            CounterCreationDataCollection col = new CounterCreationDataCollection();

            // Create two custom counter objects.
            CounterCreationData counter1 = new CounterCreationData();
            counter1.CounterName = "Counter1";
            counter1.CounterHelp = "Custom counter 1";
            counter1.CounterType = PerformanceCounterType.CounterTimer;

            CounterCreationData counter2 = new CounterCreationData();

            // Set the properties of the 'CounterCreationData' object.
            counter2.CounterName = "Counter2";
            counter2.CounterHelp = "Custom counter 2";
            counter2.CounterType = PerformanceCounterType.NumberOfItemsHEX32;

            // Add custom counter objects to CounterCreationDataCollection.
            col.Add(counter1);
            col.Add(counter2);

            // Bind the counters to a PerformanceCounterCategory
            // Check if the category already exists or not.
            if (!PerformanceCounterCategory.Exists("Category Name"))
                PerformanceCounterCategory.Create(
                    "Category Name", 
                    "Category Description", 
                    col);
            else Console.WriteLine("Counters already exists");
        }

        static void DeleteCounters()
        {
            // Check if the category already exists or not.
            if (PerformanceCounterCategory.Exists("Category Name")) PerformanceCounterCategory.Delete("Category Name");
            else Console.WriteLine("Counters don't exist.");
        }

    }

, you only need to register the performance counters once. Later when you wan't to use the performance counters it may look something like this:

// Get an instance of our perf counter
PerformanceCounter counter = new PerformanceCounter();
counter.CategoryName = "Category Name";
counter.CounterName = "Counter1";
counter.ReadOnly = false;

// Increment and close the perf counter
counter.Increment();
counter.Close();
Response.Write("Counter is incremented");

See article in MSDN Library for more information.

Inge Henriksen
In the original sample code I do not add a counter at runtime.Also, Kieran pointed out that I should not attach to counters at runtime (a different operation).I'm not sure if counters should be attached in the Global.asax once at startup.And, my original question alas remains unanswered, I still can't figure out how AverageTimer32 works, any suggestions?
Patrick J Collins
A: 

AverageTimer32 increments by the time elapsed between two calls and the AverageBase increments by 1 for each operation taken.

here's a documentation of the PerformanceCounterType. http://msdn.microsoft.com/en-us/library/system.diagnostics.performancecountertype.aspx

and here's another resource: http://www.codeproject.com/KB/dotnet/perfcounter.aspx

Lawrence A. Contreras