flow.
"Flow is a condition of deep, nearly meditative involvement." - Tom DeMarco

Unit Testing for Performance

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.

kick it on DotNetKicks.com

Feedback

# re: Unit Testing for Performance

I agree with you a high level but the problem with these test is that depend of time, so the problem is that running this code in one machino or another is different, and if you are ripping a DVD the results can get wrong too =(

There is a great framework:

http://www.codeproject.com/gen/design/nperf.asp

That is based on comparison.
For example to test that your custom collections run below 2x from the List implementation =)

Anyway, great article
Just my 2 cents

Marcos 4/3/2007 9:51 AM | Marcos

# re: Unit Testing for Performance

Thanks for the comment, Marcos.

You're completely correct...this is sort of the 'poor man's benchmarking too' :) This technique should not be used for any official benchmarking, only as a general guideline of how things are going.

Thanks for the link to NPerf. I wasn't familiar with this framework but it looks very cool. I notice it's from the creator of MbUnit as well which buys it some bonus points in my book. I'll definitely be playing with it over lunchtime today.

Thanks!
Jeremy 4/3/2007 10:57 AM | Jeremy

 re: Unit Testing for Performance

Instead of using "Assert.IsTrue(timer.ElapsedMilliseconds < 30);", you should use "Assert.Less(30, timer.ElapsedMilliseconds);". If the assertion fails, the first one will only tell you about the failure, but the second one will also tell you how long it actually took.

It is also possible, however, that you are aware of this and just got lazy for your examples. :-) 4/9/2007 4:52 PM | Anonymous

# re: Unit Testing for Performance

Oh yeah...that's it...I just got lazy :o)

Actually, that never occurred to me....that's a really slick way to do it. I like it a lot, actually. I'll definitely have to incorporate that the next time that I use this!

Thanks! 4/9/2007 10:16 PM | Jeremy

 re: Unit Testing for Performance

MbUnit has built-in attributes for performance testing. See:
http://www.mertner.com/confluence/display/MbUnit/DurationAttribute
http://www.mertner.com/confluence/display/MbUnit/PerfCounterAttribute
http://www.mertner.com/confluence/display/MbUnit/ThreadedRepeatAttribute
4/11/2007 10:59 AM | Ricardo Stuven

# re: Unit Testing for Performance

Wow, I didn't realize that. Great to know!

Thanks Ricardo! 4/11/2007 5:07 PM | Jeremy

Post a comment





 

Please add 7 and 4 and type the answer here: