views:

285

answers:

6

I've been here for nearly a month and it seems that people have a tendency to be eager to use the "Premature Optimization is the root of all evil" argument as soon as someone mentions efficiency.

What is really a premature optimization? What is the difference between what is essentially writing a well designed system, or using certain methods and premature optimizations?

Certain aspects that I feel should be interesting topics within this question:

  • In what way does the optimization influence code complexity?
  • How does the optimization influence development time/cost?
  • Does the optimization emphasize further understanding of the platform(if applicable)?
  • Is the optimization abstractable?
  • How does the optimization influence design?
  • Are "general solutions" the better choice instead of specific solutions for a problem because the specific solution is an optimization?

EDIT / update: Found these two links that are very interesting regarding what a premature optimization really is:
http://smallwig.blogspot.com/2008/04/smallwig-theory-of-optimization.html
http://www.acm.org/ubiquity/views/v7i24_fallacy.html

+7  A: 

When you optimise something without profiling it. How can you be sure it will actually speed anything up? If anything it is likely to make your code harder to read and less maintainable without any real benefit. Modern compilers have a huge array of optimisations they can use, in many cases you may have no idea what effect (speedwise) your optimisation may have.

In short: Profile, Optimise, Profile, Optimise...

Callum Rogers
One would probably prefer speeding things up with optimization, not slowing them down ;-)
Péter Török
Oops :) Corrected.
Callum Rogers
You can design a system to be performant or not performant. By experience you can surely tell that doing something in a certain way leads to poor performance and avoid doing so (or suggesting to other people that they shouldn't do so). Is that a premature optimization since the actual code hasn't even been implemented yet? Compilers really can't optimize stupid solutions.
Simon
+19  A: 

Any optimisation that involves an associated pessimisation (i.e readability, maintainability) without a clear benefit, demonstrable via testing/profiling.

Visage
I like your answer but I think that it has simplified what I was really out to get. There are a lot of cases that you can't demonstrate by profiling or testing (such as not yet implemented software) and taking an optimized decision regarding design is definetly not always a bad thing, is it? I mean, one of the actual requirements of a system can be a certain run-time performance. If you didn't design the system with performance in mind, you'll most likely have to re-write all of it. This thought is specifically directed towards "How does the optimization influence development time/cost?".
Simon
@Simon - I think he may have prematurely optimised this answer...
Paddy
@Simon, the answer stands. If you're making the code less readable, or less maintainable, and you don't have profile results to prove that you need to, then the optimization is premature. You haven't proven that the code isn't good enough; you haven't proven that you need to hurt your productivity *today* by making the code worse in the face of a *possible* problem tomorrow. See YAGNI: http://en.wikipedia.org/wiki/YAGNI
Joe White
@Joe White: I think that smallwig's theory of optimization applies, and I think that his reasoning is way better than yours. Randall Hyde has written an even more extensive "Fallacy of Premature Optimizations". I suggest that you read them, they're way better than "YAGNI".
Simon
Wow, really succinct and to the heart of the matter, +1.
peterchen
I'm with Visage here. I've seen many instances of people making changes "because it'll be faster" which end up making no difference to performance and reducing maintainability. I've seen NO instances of someone "having to rewrite" because of performance. Virtually all speedups, in the rare cases where work has to be done, can be done by rewriting a tiny fraction of the code.
DJClayworth
The only exception is an obvious mistake, like writing a O(n^2) process where there is a good O(N) process that does the same.
DJClayworth
+3  A: 

Premature optimization is often meant to refer to something that makes the code less legible while at the same time attempting to increase performance. When asking what, exactly, it might look like I can best describe it as "you know it when you see it" which probably doesn't help.

Some offhand examples that I can think of:

  1. Using bit mangling to increment a counter in a for-loop.
  2. Creating one gigantic function instead of many smaller functions to avoid function call overhead.
  3. Setting everything public to avoid the overhead of mutators and accessors.
  4. Creating a list of commonly used strings within a data object so that they do not need to be constantly reallocated (string pooling anyone?)
  5. Writing your own collection which mirrors functionality within an existing collection to avoid the overhead of bounds checking.

I'm sure there's more I've seen but, you get the idea.

wheaties
These are simply examples of common low-level optimizations. I'm afraid that they don't really address any of the topics that I felt was interesting in the question.
Simon
Yes, they are common low level optimizations. More to the point, however, is that if they're done before you know you need them they make reading the code more difficult. They circumvent encapsulation, code reuse, and often times the principles of D-R-Y. Unless you need them, they're premature optimizations.
wheaties
@wheaties: I totally agree. Does this mean that high-level optimizations that are done on a design-level of a system is not premature optimizations? Or where is the line drawn? At the point where it makes code more difficult to read/debug/use?
Simon
Good question and I think the answer is "It Depends."
wheaties
@wheaties: On what? :) That's a bit what I was out to get when I wrote the question. How can you determine what is and what isn't a premature optimization?
Simon
As to readability, quite often yesterday's awkward optimisation is today's idiom. A coder in C or a C-based language that allows fall-through (i.e. not C#) should recognise Duff's Device well enough that it reads perfectly well. This doesn't mean it should be used (insert standard comment about compiler loop unwinding here), but that the readability factor has changed over time, not because the computers or the languages changed, but because the people did.
Jon Hanna
@wheaties: On a certain microcontroller and cross compiler I use a lot, "do {} while(--index);" loops produce faster and more compact code than other loop styles. Sometimes it matters; other times it doesn't. Generally, when coding for that micro I use the do{}while(--index);" loop as the 'standard idiom' for loops where I don't need an up-counting index. Is that "premature optimization" or simply a coding style? Incidentally, I use do-while rather than while unless there may be a need to skip the loop entirely.
supercat
You're dealing with a whole different regime than what I've mentioned up there in my post. In embedded systems, space/time/processing power are limited and the usual rules don't apply. On the other hand, if you're applying the same techniques to an enterprise application spanning multiple machines you're doing premature optimization. Let me clarify above.
wheaties
A: 

As an example, I have seen developers focus on specific code optimizations that inevitably make no difference. The focus should be to get the thing working and use profilers to tell you how much time is being spent. Using profilers is akin to going after the low hanging fruit.

The compiler itself will make many optimizations. I would think that if you keep your code complexity low, the compiler will have a better chance to get more optimizations.

Some optimizations are specific to the implementation and if the architecture/design is still influx, there can be a lot of wasted time and effort.

Edward Leno
I don't think "low hanging fruit" is the right analogy. That usually means "the least amount of effort". Through profiling you learn what are your bottlenecks and what will give the greatest return on investment.
Bryan Oakley
I meant the 'low hanging fruit' to be the glaring sections of the application that should be looked at first. Once all these obvious bottlenecks are addressed, more in-depth analysis may be needed to squeeze out more benefits.
Edward Leno
Profiling tells you where the tastiest fruit is. You then balance how tasty it is with how low-hanging it is, in deciding which to go after first.
Jon Hanna
@Bryan Oakley: If the machine spends 60% of its time executing one piece of code, and 1% of its time executing another, cutting the execution cost of the former by 1.5% will improve system performance as much as cutting the cost of the latter by 90%. Unless the former code is really efficient or the latter code is really inefficient, it's going to be easier to shave the cost of the former by 1.5% than cut the latter by 90%.
supercat
A: 

A typical premature optimization I encountered a lot of times is doing everything in the same function because of "function call overhead". The result is an insanely huge function which is difficult to maintain.

The fact is: you don't know where the code is slow until you actually run a profiler. I've seen a very interesting demonstration of this point when I've been asked to optimize some code, a long time ago. I already had a course in code optimization, so I ran gprof and checked. The result was that 70 % of the running time was spent zeroing arrays. When you face such situation, the speedup you can obtain is negligible, and every effort is just going to make things worse.

Another case is handmade loop unrolling. Most compilers support loop unrolling as a feature, but unrolling every loop you create just because you are told it's a possible speedup strategy is wrong. The point of optimization is not to write high-performance code. It is to write high-performance code where there's a need for it.

To answer your questions

In what way does the optimization influence code complexity?

optimized code most likely contains clever tricks, and clever tricks tend to be 1) non portable across platforms, 2) not easy to understand immediately. Optimization necessarily produces more difficult code.

How does the optimization influence development time/cost?

you pay a development price to perform optimization, both for the actual brainwork to do, for the code deployment and testing, and for the less maintainable code you obtain. For this reason, optimization is better done on code that you are not going to touch in the future (e.g. base libraries or frameworks). Pristine new, or research code are better left unoptimized for obvious reasons.

Clearly, performance can be a product, so the important point is to strike the balance between development investment and customer satisfaction.

Does the optimization emphasize further understanding of the platform(if applicable)?

Absolutely. Pipelining requires a specific arrangement of instructions in order not to reset the pipeline. Highly parallel machines require rearrangement and communication that sometimes depend on the architecture or the networking of the cluster.

Is the optimization abstractable?

there are patterns for optimization, so in this sense yes, it can be abstracted. In terms of code, I doubt it, unless you delegate some optimizations to the compiler through auto-detection or pragmas.

How does the optimization influence design?

potentially, a lot, but if you have a good design, optimization tends to be hidden (simple example, replacing a Linked List with a Hash, or performing a do-loop in fortran column-wise instead of row-wise. In some cases however, a better design can have better performance (such as the case of the flyweight pattern).

Are "general solutions" the better choice instead of specific solutions for a problem because the specific solution is an optimization?

Not really. With general solution I assume you talk about general approaches such as a well-known design pattern. If you find out that you need a change in the pattern to satisfy your needs, that's not an optimization. It's a design change aimed at your specific problem. Optimization is aimed at improving performance. In some cases, you change the design to accommodate performance, so in this case the design change is also an optimization for performance, which can either reduce or increase maintainability, depending if the new design is more complex or not. An overdesigned system can also have performance problems.

That said, optimization is a case-by-case thing. You know the art, and you apply it to your specific problem. I'm not a great code-optimizer, but I am a good refactorer and the step is rather short.

Stefano Borini
Sun's JIT compiler for example likes small methods. Refactoring to more and smaller methods (that are called often) can speed up things when on a JVM.
eljenso
Mostly I see the insanely huge subroutine due to simple incompetence. If someone does it *on purpose*, they really need to be flogged with the cluestick.
T.E.D.
I think that it's interesting that you took my topics and addressed them as specific to any general optimization. I was thinking of them more like points to think about and not questions. I really like on of the things that you point out regarding "How does the optimization influence design?" - "potentially, a lot, but if you have a good design, optimization tends to be hidden". Does this include that proposing a "better" design to a system (which leads to better performance) is not a premature optimization? Or is it?
Simon
@T.E.D. : I should do flogging at least three major research institutes then.
Stefano Borini
@Simon : that is debatable, and a matter of point of view. You can do a completely stupid design which leads to slow performance. In some sense, it would be comparable to build a shopping mall with the parking lot on the opposite side of the entrance. Doing it the right way is kind of a premature optimization, but it's an optimization of the design, not of the implementation. The "premature optimization" problem is mostly addressed to implementation optimization, although you should also not prematurely optimize a design unless you already know it's going to be slow.
Stefano Borini
@Simon : in other words, the premature optimization evilness resides in focusing on planning and implementing optimization since the beginning when you _don't_ _know_ _anything_ about the performance beforehand, and even if you know, you really have to be in the exact same situation. Pure code optimization (implementation) must always comes last because in general is a very fine, low-level process, compared to design which is a high level one. Premature design optimization can result in analysis paralysis, which is a baaad situation.
Stefano Borini
@Stefano Borini - Three? Well...you'd best get started then. :-)
T.E.D.
@Stefano Borini: Any optimization, be it premature or not, can fail and lead to worse performance. I agree that pure code optimization (implementation) must always come last. I must say that you surely can know something about performance beforehand. A pretty good example is designing a system to place data in a none-cache friendly way while the main idea of the system is to iterate over a set of data and perform low-level processing on it. I've seen several "premature optimization!" screams about suggesting improvement regarding that kind of systems, and I think it is not.
Simon
+3  A: 

A premature optimisation is one that was done too soon to guarantee an optimisation.

This is different to a bad optimisation:

  1. Maybe that something really does speed up an important piece of code isn't worth some down-side, even if it was applied after profiling and user-experience demonstrated its value - in which case it is bad, but not premature.

  2. On the other hand, maybe something done prematurely actually turns out to be just what was needed - in which case it was good, even though it was premature. Generally people who are guilty of premature optimisation will do this a lot, it's just that they'll balance it by doing stuff that makes things worse even more frequently.

Premature also doesn't mean early, just too early.

If you're designing an application that involves use of communication between two agents, and there is a way to either build a caching mechanism into the communication, or to build on one in an existing communication system (e.g. if you use HTTP), then this caching is not premature, unless the responses won't actually be cacheable (in which case it's premature as it's pointless). Such a fundamental design decision based on analysis of the problem (but not profiling of actual code - the code hasn't been written) made for purposes of efficiency can be the difference between feasibilty and infeasibilty.

Premature doesn't mean going for the option that seems most likely to be efficient given seemingly equivalent choices. You have to go for one of the options after all, so the likely efficiency is one of the factors you should weigh up. This is a case where intuition does have some value: (A) you don't yet have enough data to make an informed decision purely on the facts, (B) some of the other factors you are weighing up are intuitive also (those that affect how readable the code is; measuring it beyond just seeing how readable it is to you is only useful in a few cases, and again you can't measure the readability of something that hasn't been written, and finally it can only be measured statistically so if you're the main dev your opinion matters more than a statistically valid sample of devs). Move too far away from an option just because you fear being accused of premature optimisation, and you're just prematurely optimising for a different feature.

Nor does premature mean a small optimisation. If you can make a small improvement with no downside, go for it. Indeed, when Knuth said "premature optimisation is the root of all evil" he actually brings it up while defending taking the effort of working to gain small efficiency gains:

The improvement in speed from Example 2 to Example 2a is only about 12%, and many people would pronounce that insignificant. The conventional wisdom shared by many of today's software engineers calls for ignoring efficiency in the small; but I believe this is simply an overreaction to the abuses they see being practiced by pennywise-and-pound-foolish programmers, who can't debug or maintain their "optimized" programs. In established engineering disciplines a 12 % improvement, easily obtained, is never considered marginal; and I believe the same viewpoint should prevail in software engineering~ Of course I wouldn't bother making such optimizations on a oneshot job, but when it's a question of preparing quality programs, I don't want to restrict myself to tools that deny me such efficiencies. There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. 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. It is often a mistake to make a priori judgments about what parts of a program are really critical, since the universal experience of programmers who have been using measurement tools has been that their intuitive guesses fail. —Knuth, Donald. "Structured Programming with go to Statements", Stanford, 1974

Premature is when it is simply too early to know whether something you are doing, with efficiency (whether in speed, memory, resource use, or a combination of these) in mind will actually produce the benefits intended and not also bring about other lacks and indeed that they won't actually harm the efficiency in the area they are seeking to be efficient in (a classic in the .NET world is people, often acting out of an emotional desire for control, seeing the GC as their enemy and working to "improve the efficiency" of garbage collection, and making it less efficient perhaps 95% or more of the time).

That's premature because it is, well, premature - too early.

Jon Hanna
"Premature is when it is simply too early to know wether something you are doing, with efficiency in mind, will actually produce the benefits intended and not also bring about other lacks..". I think that sentence was a really good definition of premature optimizations. It suits into the fact that when designing a system, it is ok to have optimization in mind in order to "foresee" probable performance-costs. As long as it doesn't make the system more complex/hard to work with in the future, it's even good if it's a small optimization... right?
Simon
And also, sometimes you **can** foresee definite performance costs. You should have as much of an idea of the conditions affecting your application as you can before you write the first line of code. Sometimes optimising on the basis of these is about probabilities, but sometimes you can tell with 100% certainty that a certain approach will be more performant. As said above, sometimes such optimisations are the difference between a feasible solution and one that's impossible with available hardware.
Jon Hanna
On the flip side, sometimes a reasonable decision you hoped will make things more efficient, and which doesn't hurt in any other regard, will have no effect or a negative one on efficiency. Such an optimisation wasn't premature, but it was incorrect. When you **do** come to profile, you may well end up replacing it with what you at first thought would be less efficient. For that matter what is efficient today may be inefficient tomorrow and vice versa (changes in runtime are often talked about, but changes in the underlying data is a more common reason for good code becoming bad).
Jon Hanna
@Jon Hanna: That's a very good point. I think that people tend to ignore the way data is represented, and how that changes, way too often.
Simon
@Jon Hanna: One problem is that programmers are empirically bad at finding hot spots, which is why some of us keep insisting on profiling first. This obviously doesn't apply to the overall algorithm, or to things that might help but won't hurt (like using pre-increment operators when possible in C++).
David Thornley
@Simon. It's certainly something I've come up against, where I've changed collection-type used and gained an improvement and then months later changed it back and made an improvement again, because what (and particularly, how much) is going into that container has changed.
Jon Hanna
@David. LOL, it's still second nature to me to use ++x over x++ in cases where they are equivalent, though in the C# it's *extremely* unlikely that one ever beats the other. No harm done, though, and since so many are used to it, it becomes idiomatic which aids readability.Perhaps we could call reasonable *a priori* decisions "aiming at the optimial", to differentiate from optimising (which *a priori* is a verb that doesn't yet have an object). Good aiming at the optimal is different to good optimising, and the later need profiling or at least some sort of measurement.
Jon Hanna
@Jon Hanna, @David: I think you guys are pointing out something very important here. "priori" decisions are different from person to person though and how abstract can you make such a decision? Post/pre increment is a small thing, but could using container X instead of container Y be such a thing? Using stack allocated data vs heap? Etc.
Simon