Monday, April 02, 2007 10:15 PM
In the vein of recent posts regarding unit testing, here's another quick idea. Many functional requirements detail specific performance requirements which must be adhered to. For example, you may have a search function which, according to business requirements, must return within 30 milliseconds. Or, you may have a sorting algorithm, which regardless of the size of the data, must be able to balance your entire data tree in under 100 milliseconds.
These metrics are easy enough to gather occasionally, but can be difficult to monitor on a regular basis. What if you could include these performance metrics as a normal part of your functional unit tests? Well, there's a quick and easy to do just that.
Have you heard of the
Stopwatch class? The Stopwatch class is a class which was added in .NET 2.0 to facilitate performance profiling of your code. Before the Stopwatch came along we simply relied on
DateTime and
TimeSpan objects sprinkled throughout the code or many of us simply rolled our own stopwatch from these components. But the Stopwatch class really cleaned things up. Take a look at the following snippet, I'm using a Stopwatch to figure out exactly how long a particular search routine took and then outputting that information to the user...
public void SeachPerformanceTest()
{
Stopwatch timer = new Stopwatch();
timer.Start();
EntityList entityList = new EntityList();
Entity fluffy = entityList.Search("Fluffy");
timer.Stop();
Console.WriteLine("Search() took " + timer.ElapsedMilliseconds + " milliseconds.");
}
Pretty neat, huh? I know what you're thinking...that's great when you take the time to put that code in there but who wants to leave that in production code. It's unnecessary overhead and clutters up your routines. Well, I agree. In fact, I think we should move it out to unit tests! You know what one of the great things about unit tests is? We can use an assertion to pretty much validate any condition. Whether one value is the same as the other value...whether one value is different from another value...even whether one value is greater than another value. That's right...we can assert whether one value is greater than another value. Do you see where I'm going yet? Take a look at the following code...
[Test]
public void SeachPerformanceTest()
{
Stopwatch timer = new Stopwatch();
timer.Start();
EntityList entityList = new EntityList();
Entity fluffy = entityList.Search("Fluffy");
timer.Stop();
Assert.IsTrue(timer.ElapsedMilliseconds < 30);
}
This is an automated test that completely verifies whether or not our search routine executed in the time allowed. Using tests like these, we can keep an eye on performance creeps along with our normal unit tests and identify problems as soon as they start to get out of hand.
The possibilities don't stop there, either. Check out this code...
[Test]
public void MemoryFootprintTest()
{
long bytesAllocatedInitially = GC.GetTotalMemory(true);
EntityList entityList = new EntityList();
IList<Entity> allEntities = entityList.GetEntities();
long bytesAllocatedNow = GC.GetTotalMemory(true);
Assert.IsTrue((bytesAllocatedNow - bytesAllocatedInitially) <= 50000);
}
This test lets us keep an eye on our memory footprint, and lets us know when it starts to get a little chubby. Imagine that you have a technical requirement that your data layer must be able to retrieve references to every entity in the database without consuming more than 5K of additional memory (also imagine your technical requirements specify that your thick client must run on a 1981 Coleco). This way you can track that memory consumption and be alerted as soon as your code starts to get greedy with those precious bits.
Getting any ideas yet? I hope so. For extra credit think about this. .NET has the concept of performance counters. These are pre-built objects already setting on your target machine which are setup to measure just about every aspect of your machine imaginable. Everything from
garbage collection and
JIT performance, to
networking and security performance. And if by some off chance what you want doesn't already exist...then you can just
make your own. Just imagine what sort of metrics and benchmarks you could squeeze from your code on a regular basis with just a few performance counters, some clever assertions, and a little imagination.