Profiling for performance
In the world of high-traffic applications, execution time, memory usage and database query are all extremely important terms used to define the efficiency and performance of an application. But although they’re important, all too often they’re an afterthought in the software coding process.
Here’s what usually happens.
An architect and developer sit down together, bang out the “ultimate” design on a whiteboard, sketch out a class diagram and presto! They have all the ingredients to start coding an amazing, new, ground-breaking feature. But once they complete it and test it, that’s when the red flags start going up: the feature doesn’t feel fast enough or perhaps the database server queries are spiking.
Time to crack out the good old code-profiling software. Ideally, it should have been unleashed long before this point, but as many have done and will continue to do, profiling the shiny new code is only considered after problems arise. It’s the classic example of “putting the cart before the horse” that software developers have come to know all too well.
Being such a developer, here’s an example I can share with you from experience.
Not long ago at Lightspeed, we designed a feature to allow our customers to import items into their Retail account. Sounds pretty straightforward, right? And at first, it was, at least for simple item fields like description or price.
But then, something happened.
As we started to make more fields importable, we noticed that our performance dropped by 30%! We were using more memory and querying the database far more than before. Suffice it to say, this was not ideal.
How did we fix this?
We deployed our favourite profiling tool and dug a little deeper. We wrote a unit test specifically for measuring import performance. The code profiler provided us with statistics such as overall execution time, memory usage, cpu usage and database queries (remember those?). It also provided us with a nice visual code execution path complete with classes, method names and a percentage of time spent in these methods. We were able to track the code path from start to finish. It was really cool and I wish you all could have been there to see it for yourselves.
After maybe 5 minutes of examining the results, we identified one of our performance-sapping culprits! It was a certain data column that, when imported, used some existing code to fulfill its requirement and caused everything to slow down.
But that operation merely returned a True or False value, so why was it so slow? Back to the profiling tool!
Leveraging the profiler, we saw that for each call to this method it was generating four queries to the database. Therefore, for 1000 items there were 4000 queries, all just to return the same True or False value. Obviously this had to change, so we decided to cache the result. This change produced a 27% reduction in execution time, 31% decrease in memory usage and 16% reduction in database queries — all with about five lines of code.
That is the cautionary tale I wanted to share with you today, because without using a code-profiling tool, it would have been very difficult to track that down.
If you are not code-profiling, I urge you to start. And start early on in your projects; it will help identify potential performance issues while they can still be nipped in the bud. And it can also help identify and eliminate any design flaws you may have, allowing your amazing, new, ground-breaking feature to realize its full potential.