Saturday, June 27, 2015

Balancing Performance with Code Readability

Image result for balance

This is a topic much discussed in the industry and the position I take is a common one: Code first for readability and support, refactor for performance as necessary. There are notable exceptions to the wisdom of taking a readability-first approach. In general you will know if your code is one of these exceptions. It could be graphics-intensive operations, operations widely used and distributed in libraries, and similar situations where you know critical paths through the code a priori. For the 90% case, where squeaking out every nanosecond of performance is not critical I like to use the following steps:
  1. Write code to be as readable and supportable as possible
  2. Validate that the performance is acceptable
  3. If performance is not acceptable, profile to find the hot spots
  4. Identify techniques for improving performance of these hot spots
  5. Using the Strategy Pattern
    1. Extract each module to  be refactored into a separate component and introduce a simple factory to serve up the existing implementation
    2. Introduce a higher performing implementation and update the factory to use this new strategy
    3. Add comments as necessary to the new strategy implementation
The factory gives us much more flexibility. It allows us to restore the original strategy if we later find that we really don't gain enough overall performance to warrant the new version. It also allows us to experiment with different strategies for improving the performance before deciding on one.

I am of the school of thought that a lot of comments in code is a design smell that the code wasn't as well thought out and decomposed as it should be. I think that most code should be written to be self-documenting with appropriately named methods with small scope. There are certain exceptions to this. Writing for performance is a common way of adding unintentional complexity. Often this added complexity is unavoidable. We should add sufficient comments to explain the code and the reasoning behind the extra complexity so that someone further down the road does not reverse our decisions (perhaps for readability) without understanding the implications of that reversal.

Next week I will push into a use-case where I opted for readability over performance in conflict with published performance recommendations from the platform vendor.

1 comment:

  1. I made the mistake of failing to run profiling on an open source piece of software of mine, and prematurely optimized certain portions. Amusingly, when I finally profiled, the big, huge, bottleneck was in calls to the random number generator. Turned out, there was a good way to minimize such calls in many instances, cutting the calls to the RNG by 1/3 in many cases. This change added complexity, but nothing like the complexity I had added myself by prematurely optimizing.

    ReplyDelete