Wednesday, November 28, 2012

When Premature Optimization Isn't

Earlier this month, I decided I wanted to write a post on not all optimization being premature optimization after hearing more than one developer use this mantra as an excuse for not making a better decision in the same week. Bozhidar Bozhanov beat me to it with his post Not All Optimization Is Premature, which makes some excellent but different points than I had planned to make in postulating that there is nothing wrong in early optimizing "if neither readability nor maintainability are damaged and the time taken is negligible."

Bozhanov's post reminded me that I wanted to write this post. I use this post to provide additional examples and support to backup my claims that too many in our community have allowed "avoid premature optimization" to become a "bumper sticker practice." In my opinion, some developers have taken the appropriate advice to "avoid premature optimization" out of context or do not want to spend the time and effort to really think about the reasons behind this statement. It may seem easier to blindly apply it to all situations, but that is just as dangerous as prematurely optimizing.

Good Design is Not Premature Optimization

I like Rod Johnson's differentiation between "design" and "code optimization" in his book Expert One-on-One J2EE Design and Development (2002). Perhaps the most common situations in which I have seen developers make bad decisions under the pretense of "avoiding premature optimization" is making bad architecture or design choices. The incident earlier this month that prompted me to want to write this post was a developer's assertion that we should not design our services to be coarser grained than he wanted because that was premature optimization and his idea of making numerous successive calls on the same service was "easiest" to implement. In my mind, this developer was mistaking the valid warning about not prematurely optimizing code as an excuse to not consider an appropriate design that might require a barely noticeable amount of extra effort.

In his differentiation between design and code optimization, Johnson highlighted, "Minimize the need for optimization by heading off problems with good design." In that same section he warns against "code optimization" for four main reasons: "optimization is hard" ("few things in programming are harder than optimizing existing code"), "most optimization is pointless," "optimization causes many bugs," and "inappropriate optimization may reduce maintainability forever." Johnson states (and I agree), "There is no conflict between designing for performance and maintainability, but optimization may be more problematic."

Applying Appropriate Language Practices is Not Premature Optimization

Most programming languages I'm familiar with often offer multiple ways to accomplish the same thing. In many cases, one of the alternatives has well-known advantages. In such cases, is it really premature optimization to use the better performing alternative? For example, if I'm writing Java code to append characters onto a String within a loop, why would I ever do this with a Java String instead of StringBuilder? Use of StringBuilder is not much different in terms of maintainability or readability for even a relatively new Java developer and there is a known performance benefit that requires no profiling to recognize. It seems silly to write it with String in the name of "avoiding premature optimization" and only change it to StringBuilder when the profiler shows it's a performance issue. That being stated, it would be just as silly to use a StringBuilder for simple concatenations outside of a loop "just in case" that code was ever placed within a loop.

Similarly, it's not "premature optimization" to write a conditional such that the most likely condition is encountered first as long as doing so does not make the code confusing. Another example is the common use of short circuit evaluation in conditionals that can be effective without being premature optimization. Finally, there are cases where certain data structures or collections are more likely to be appropriate than others for a given operation or set of expected operations.

Writing Cleaner Code is Not Premature Optimization

Some developers might confuse more "efficient" (cleaner) source code with premature optimization. Optimizing source code for readability and maintainability (such as in refactoring or carefully crafting original code) has nothing to do with Knuth's original quote. Writing cleaner code often leads to better performance, but this does not mean writing cleaner code is a form of premature optimization.

Others' Thoughts on Misunderstanding of Premature Optimization

Besides the Not All Optimization Is Premature post, other posts on the misapplication of the "avoid premature optimization" mantra include Joe Duffy's The 'premature optimization is evil' myth and 'Avoid Premature Optimization' Does Not Mean 'Write Dumb Code'.

Duffy puts it so well that I'll quote him directly here:

I have heard the "premature optimization is the root of all evil" statement used by programmers of varying experience at every stage of the software lifecycle, to defend all sorts of choices, ranging from poor architectures, to gratuitous memory allocations, to inappropriate choices of data structures and algorithms, to complete disregard for variable latency in latency-sensitive situations, among others. Mostly this quip is used defend sloppy decision-making, or to justify the indefinite deferral of decision-making. In other words, laziness.

The James Hague post points out one of the signs of potentially having crossed into premature optimization: "The warning sign is when you start sacrificing clarity and reliability while chasing some vague notion of performance." Hague also writes, "What's often missed in these discussions is that the advice to 'avoid premature optimization' is not the same thing as 'write dumb code.'" I like this last sentence because I believe that just as some developers have adulterated the agile concept to mean (to them) "no documentation," some developers have adulterated the sound advice to "avoid premature optimization" to mean (to them) "blindly write code."

Premature Optimization is a Real Problem

Premature optimization is a problem we developers must guard against. As Johnson states in the previously cited book, "Few things in programming are harder than optimizing existing code. Unfortunately, this is why optimization is uniquely satisfying to any programmer's ego. The problem is that the resources devoted to such optimization may well be wasted." There makings examples of where premature optimization wastes significant resources and in some cases even makes things perform worse. There is indeed a reason that the well-regarded Knuth wrote that "premature optimization is the root of all evil." I'm not saying that premature optimization doesn't exist or that it's not harmful. I'm just saying that avoiding this admitted dysfunctional behavior is often used an an excuse to avoid thinking or to avoid implementing sound decisions.

Conclusion

Like pithy bumper stickers on cars that naively boil down complex issues to a few clever and catchy words, use of "avoid premature optimization" is often used much more broadly than it was intended. Even the best of recommended practices can cause more harm than benefit when applied improperly and the misuse of "avoid premature optimization" is one of the best examples of this. I have seen the high cost paid in maintainability, readability, and even in performance when supposed "optimization" was implemented too early and at the expense of readability and maintainability for no measurable benefit. However, just as high of a cost can be incurred by blindly using "avoid premature optimization" as an excuse to avoid designing and writing better performing software. "Avoiding premature optimization" is not an excuse to stop thinking.

1 comment:

@DustinMarx said...

In Java Performance: The Definitive Guide, Scott Oaks articulates well a point I was trying to make in this post: "[avoid] constructs that are known to be bad for performance ... it isn't the type of premature optimization that should be avoided; it's the kind of choice that good coders learn to make. Don't let out-of-context dogma from pioneering heroes prevent you from thinking about the code you write."