Sunday, November 28, 2010

Refactoring and performance

"The interesting thing about performance is that if you analyze most programs, you find that they waste most of their time in a small fraction of the code. If you optimize all the code equally, you end up with 90% of the optimizations wasted, because you are optimizing code that isn't run much. The time spent making the program fast, the time lost because of lack of clarity is all wasted time ... [When] the code is clearer, you have a better understanding of your options and what kind of tuning will work" (Refactoring, p.70)

I think Martin Fowler has a very good point here, which I have known for a while, but couldn't quite articulate as well. Writing performance-optimized code comes at a cost, that cost is typically clarity, maintenance and ultimately time. Another quote comes to mind:

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified” - Donald Knuth

Very often the most unexpected things may end up being performance bottlenecks. Someone may spend days, weeks, or even months, trying to squeeze out every inch of performance out of a piece of code, and in the end it may not matter at all, as you discover it's something else (the part of code that doesn't even look like it needs optimizing) that creates 90% of the performance loss.

What's worse, the prematurely optimized code can end up being re-written altogether, because the user requirements have changed, and then the hours spent painstakingly squeezing out miliseconds of CPU time are wasted 100%!

5 comments:

  1. 1ms optimization may get you from 30 to 60fps. do not forget about that :)

    ReplyDelete
  2. Yes, but my point about 100% wasted time still stands :)

    ReplyDelete
  3. Not that milliseconds are not important ...

    ReplyDelete
  4. "the prematurely optimized code can end up being re-written altogether"

    But this may happen for all code ... so the hours you have spent refactoring for clarity and easy of maintenance may also be wasted. This is really an 'argument' against doing anything, ever, because it might be redone later.

    Premature anything is bad by definition, but unless there's some operational criteria attached, by which we can figure out whether it's premature before the fact, the sentiment is inherently meaningless. It's like saying "Too much ice cream is bad for you", it's true by definition, but useless without quantification. Anyway, what Knuth is really opposing is not optimization as such, but two things:
    1) Prioritizing performance over correctness. First make the program correct, then make it fast.
    2) Optimizing without data. Find out where the problem is, and optimize that. Don't guess. And don't neglect to optimize once you have the data.

    "Very often the most unexpected things may end up being performance bottlenecks"

    True, but we have a solution for this, and the solution isn't writing clearer code, so that the programmer can introspect the code and easily find the bottlenecks, the solution is profiling. No optimization should be undertaken without profiling, because programmers are much worse than programs at discovering bottlenecks.

    So there :-)

    be well
    -h-

    ReplyDelete
  5. Obviously I'm making an oversimplified point here. In order not to raise objections, this post would need to be more thorough, perhaps have some examples, and cover a lot of ground. But then ... the world around us would change, and I would have to rewrite the post to adjust to the new realities. That would be 100% wasted time! :)

    In all seriousness I think if I were to make a more precise point here, it would be that I rarely START with optmizations in mind. Premature optimization would be to write code that would probably be performance optimal up front is often a bad idea. In my next post, I will have an example of something that is clearly not optimal and yet I choose leave it as is until the performance is proven to be an issue.

    As for writing clearer code, while it's not going to directly address the performance issue, it might help us figure out what to DO about it.
    But I think we both agree with Knuth in the end that profiling is the way to go, and correctness trumps performance.

    ReplyDelete