23 August 2007

.NET Performance Counter Problems

I've recently been doing some work with performance counters in .NET and discovered the support for them in the framework is a bit nasty.

One example is that the only way to add counters is through the PerformanceCounterCategory.Create method so you can't append a new counter to an existing category. You have to copy all the counters from the old category, add you new counter, delete the old category and then recreate the category with the new list of counters.

Another example is that in order to use any of the fraction based counters like averages you have to add two separate counters, one for the numerator and one for the denominator.

All very long winded anyway so I decided that I'd wrap up the creation of the counters and categories along with a few other bits in a helper class. That was the plan but it fell at the first hurdle - trying to create categories.

The symptom

This, seemingly innocuous bit of code...

if(!PerformanceCounterCategory.Exists(categoryName))
{
 PerformanceCounterCategory.Create(categoryName, categoryName, new CounterCreationDataCollection() );
}

if(!PerformanceCounterCategory.CounterExists(counterName, categoryName))
{
 //add a counter
}

...was yielding the following error on line 6:

System.InvalidOperationException: Category does not exist.
   at System.Diagnostics.PerformanceCounterLib.CounterExists(String machine, Str
ing category, String counter)
   at System.Diagnostics.PerformanceCounterCategory.CounterExists(String counter
Name, String categoryName, String machineName)
   at System.Diagnostics.PerformanceCounterCategory.CounterExists(String counter
Name, String categoryName)

i.e. three lines after the statement creating a category it says the category doesn't exist. Odd, I thought, so I knocked together a quick Snippet Compiler script:

string categoryName = "foo";
string counterName = "bar";
  
if(PerformanceCounterCategory.Exists(categoryName))
 PerformanceCounterCategory.Delete(categoryName);
  
WL("Category " + categoryName + " exists? " + PerformanceCounterCategory.Exists(categoryName));
  
PerformanceCounterCategory.Create(categoryName, categoryName, new CounterCreationDataCollection() );
  
WL("Category " + categoryName + " exists? " + PerformanceCounterCategory.Exists(categoryName));
      
try
{
 WL("Counter foobar exists? " + PerformanceCounterCategory.CounterExists(counterName, categoryName));
}
catch(Exception ex)
{
 WL(ex.ToString());
}
  
RL();

Further oddness arose from this because the call to PerformanceCounterCategory.Exists after the Create actually gives the correct response, it is only the CounterExists method that fails. I immediately suspected some sort of caching issue.

The cause

After having a poke around with Lutz Roeder's .NET Reflector I discovered that all the Performance Counter classes make use of an internal class called PerformanceCounterLib which has a few hashtables it uses to cache information about the categories and their counters.

The hashtable concerned here was the CategoryTable which, on adding a category for he first time, does not update properly. When the CounterExists call runs this line:

if(this.CategoryTable.ContainsKey(category))

it fails throwing the error however the Exists call has this line:

return (PerformanceCounterLib.IsCustomCategory(machineName, categoryName) 
   || PerformanceCounterLib.CategoryExists(machineName, categoryName));

the right part of this calls the same CategoryTable.ContainsKey however the IsCustomCategory on the left accesses the registry directly and returns the correct answer - true.

Why the CounterExists doesn't call this method itself and opts instead to provide its own implementation i've no idea.

The solution

Luckily the PerformanceCounter.CloseSharedResources() method flushes all these internal caches so the calls all return the same answer. Adding a call to this straight after my PerformanceCounterCategory.Create solved the problem.

19 August 2007

Archiving your CD collection

This post could probably be considered to be the second part of my very first post on this blog entitled Freeing your digital media in which I talked about the DLNA standards and how they allowed you to store all your media on a NAS device and stream it around your house over your network.

I'd been considering this more seriously and realised that a large proportion of my MP3 collection were low bitrate poor quality rips which just sound awful played though a half decent hi-fi.

Storage space is only getting cheaper and today you can pick up a 500GB external hard disk, enough for 700 uncompressed albums, for less than £80; the Western Digital My Book series proving to be a popular choice. There really is no reason why, with this much space, we should be sticking to lossy compression formats for our music archiving. So, with that in mind, what are the options?

The main contenders

WAV
Microsoft's uncompressed audio format
AIFF
Apple's uncompressed audio format
WMA Lossless
Microsoft's lossless audio compression format
Apple Lossless
Apple's lossless audio compression format
FLAC
Open source lossless audio compression format - now part of the Xiph.org Foundation, originally developed by Josh Coalson
Monkey's Audio
A lossless audio compression format developed by Matt Ashland
Shorten
An old open source lossless audio compression format developed by Tony Robinson at SoftSound

Hydrogen Audio have prepared a good comparison table of lossless formats. Uncompressed 44.1kHz 16bit audio uses up about 10MB of space per minute. The lossless compression formats listed above all reduce file sizes to about 6MB per minute. Not bad but compared to the 1 - 2MB per minute we're used to with lossy formats this still seems like a lot. My justification for this space usage is that I want the convenience of being able to carry 700 albums around with me and I've paid for the CDs so i might as well be listening to them in full CD quality.

My criteria for a format were; open source, good compression and good hardware and software support. This already removes all the Microsoft and Apple options and Monkey's Audio. Shorten is old and has poor support so the natural choice is FLAC which, being part of the Xiph.org Foundation, has a large developer base behind it and support in all the major software and hardware music players albeit via new firmware in some cases. Not to mention that it's now packaged with arguably the best CD ripper, Exact Audio Copy.

A quick download, set up and a few rips later and I'm rather astounded by the results. To my ears the FLAC files actually sound better than playing the original CD through the same system, delivering a far more punchy and detailed sound. I have to admit I wasn't expecting this and I'm assuming it's down to my CD-ROM reading the audio data poorly when playing the CD directly.

Links