James Stanley

How (and why) to make your software faster

Fri 15 April 2016

Have you ever been bothered by how slowly your webapp loads, but never profiled it? Much like test-driven development and A/B tests, performance profiling almost always throws up surprises and big wins, and yet most people never bother to do it. If you have anything that runs too slowly, you should profile it today, you will find improvements to make.

What Profiler Should I Use?

For profiling C, valgrind's callgrind is excellent, and you can examine the output in KCacheGrind. For Perl, NYTProf is excellent, and you can examine the output in a web browser, using nytprofhtml. In the worst case, in any framework, you can insert logging statements before and after each stage of processing, and find your bottlenecks that way.

A large portion of my day job is speeding up software in various ways: make user interfaces load faster, make backend processing happen sooner, make backend processing handle more data in parallel. It is almost always possible to make software faster, and I've noticed some recurring themes. Even in cases where it is not economical to improve the software itself, you can still make it faster by spending more on hardware. In this age of DigitalOcean droplets and EC2 instances, this is easier than ever.

The 3 Most Important Things to Know to Make your Software Faster

1.) Profile it. Just having the profiling data gets you 80% of the way there. Get the profiling data, and spend a few minutes looking at it and deciding whether it makes sense, and which parts seem slower than you expected.

2.) Abstraction layers hide inefficiencies. Layers of abstraction are absolutely necessary for the development of complex software systems. But often, the abstractions implicitly perform unnecessary work, just because it is sometimes needed. When you spot bottlenecks in the profiling output, decide whether they absolutely have to exist. It is not unusual for the slow work to be necessary only in special cases. This is an easy win: run less code.

3.) Database queries. When the software was first written, there wasn't much data, and all was well. Over the years, the database has grown substantially. Now your profiling shows you that a simple query takes too long. See if you are missing important indexes. If the query is being repeated, see if you can cache the output, or grab the output for all data items at once instead of repeating the query.

The Business Case

A poorly-performing user interface doesn't just make a task take longer, it discourages you from doing any task that has that interface as part of the workflow, and provides an opportunity to get distracted. If it's an internal application, it discourages you from doing valuable work. If it's your product, it discourages your customers from using it. Making the interface faster doesn't just make the task quicker, it enables whole new classes of tasks that make heavier use of the interface.

A poorly-performing backend process limits your throughput, and thus is a barrier that needs to be knocked down before you can exceed a certain scale.

Case Studies

A process at my day job handles millions of URLs per day. As part of handling each URL, it checked whether the URL was present in the database, and then logged something if it was. This was implemented so that we could get interesting stats on how many of the URLs were already present at report time. It turned out that it was spending 30% of its time doing these database queries, there was still no automated process to generate the stats, and humans had long since stopped checking. Deleting that 1 line of code knocked 30% off the execution time of every URL.

An admin overview page was spending 2.5ms per item loading extended data. But the page was now an overview of 8000 items, which means it was spending 20 seconds loading this data. This severely discouraged people from using the interface, and some work went undone. And it turned out, since the overview page was just an overview, the extended data wasn't even being shown! The abstraction layer loaded the data implicitly, because it didn't know it wasn't going to be shown. Pass in a flag to make it stop loading extended data on the overview page: the page loads faster, people use it more, and more work gets done.

The Hard Sell

Since I stopped doing my day job full-time, I am able to take on more projects. Here's my pitch:

If I can't make your worst-performing page load 50% faster, you don't have to pay the bill.

Get in touch if you think you might want to hire me: james@incoherency.co.uk. Also get in touch if you want to discuss something even if you will never hire me. I want to hear from you!

If you like my blog, please consider subscribing to the RSS feed or the mailing list:
or follow me on Twitter.

James Stanley - james@incoherency.co.uk | jesblogfnk2boep4.onion | /ipns/jes.xxx/ | [rss]