Friday, December 16, 2005

How I find performance bottlenecks in my ASP.NET projects

Have you ever heard of a product called Ants Profiler by Red Gate?  An absolutely phenomenal product for profiling .NET code.  At the time of this writing, it runs at $295, and worth much more than every penny in its price.  I usually try to use free software as much as possible, or write my own.  Somehow writing a check for software hurts me.  But in this case, I recommend it to anyone.  And lest you feel like this is an ad, Red Gate is not paying me for this blog.  Any code profiler by definition does what Ants Profiler does.  I haven't found another product, however, that does it as intuitively as Ants Profiler does it, or throws in as much extra information as it does.

Very Common Scenario for me

I have several ASP.NET web projects that are based on NHibernate.  For the most part the web sites run decently fast.  Where there are problems, I find that enabling lazy loading seems to do the trick.  In one of my use cases, a user will want to download a dataset that has to be put together on-the-fly (or close to it) from several tables, including a table with > 42 million rows -- and all the information in that large table has to go into it.  When the operation took several hours, I blamed NHibernate, or the database server.  I added indexes to the database -- to no avail.  I stuck SQL Profiler on NHibernate to find out what time wasting activities it was doing -- but NHibernate was behaving perfectly. 

I turned on Performance Monitor and watched the CPU of my web server box and my db server box.  I was very surprised to find out that even as it pulled down the 42 million rows, the db server was just idling away while my web server was sweating bullets.  Clearly some code on my part had to be causing a bottleneck somehow.

Then I turned on Ants Profiler.  I ran a small version of the several hour operation.  This one took about 30 minutes.  Ants Profiler pointed to about 5 lines of code that were executing several million times, and taking up (between the several lines it pointed out) about 95% of the time spent in the operation.  These slow operations were things like case-insensitive string comparisons and O(n) array searches.  These may go fast on short strings and arrays, but when they execute several million times, they apparently turn into the bottleneck of otherwise well-tuned code.  Having been shown the problem code, I very easily changed the string comparisons and arrays to hash table lookups and sorted collections.  Having invested a few hours in total to this optimization and having changed only a couple dozen lines of code (at most!), my app is now about 10x faster at executing that operation.  Sweet.

Synopsis

I like coding using best practices and good class models with business objects.  I like my code to be readable.  If I'm coding and I think of two ways to code, one readable, the other optimized, I'll default to readable.  I'll optimize only when I find that that code is what slows my app down, like in the case above.  Using code profiling tools such as Ants Profiler, I can very quickly find that problem code and fix it when I'm about finished with my app. 

No comments:

Post a Comment