A colleague of mine today committed a class called ThreadLocalFormat, which basically moved instances of Java Format classes into a thread local, since they are not thread safe and "relatively expensive" to create. I wrote a quick test and calculated that I could create 200,000 instances a second, asked him was he creating that many, to which he answered "nowhere near that many". He's a great programmer and everyone on the team is highly skilled so we have no problem understanding the resulting code, but it was clearly a case of optimizing where there is no real need. He backed the code out at my request. What do you think? Is this a case of "premature optimization" and how bad is it really?
|
migrated from stackoverflow.com May 29 '11 at 8:37
|
It's important to keep in mind the full quote:
What this means is that, in the absence of measured performance issues you shouldn't optimize becuase you think you will get a performance gain. There are obvious optimizations (like not doing string concatenation inside a tight loop) but anything that isn't a trivially clear optimization should be avoided until it can be measured. The biggest problems with "premature optimization" are that it can introduce unexpected bugs and can be a huge time waster. |
|||||||||||||||||||
|
|
Id's say premature micro optimizations are the root of all evil, because micro optimizations out context. almost never behave the way they are expected. What are some good early optimizations in the order of importance:
Some mid development cycle optimizations:
Some end development cycle optimizations
So to answer your question :-) : Not all early optimizations are evil, micro optimizations are evil if done at the wrong time in the development life cycle, as they can negatively affect architecture, can negatively affect initial productivity, can be irrelevant performance wise or even have a detrimental effect at the end of development due to different environment conditions. If performance is of concern (and always should be) always think big. Performance is a bigger picture and not about things like: should I use int or long?. Go for Top Down when working with performance instead of Bottom Up. |
|||||
|
|
optimization without first measuring is almost always premature. I believe that's true in this case, and true in the general case as well. |
|||
|
|
Optimization is "evil" if it causes:
In your case, it seems like a little programmer time was already spent, the code was not too complex (a guess from your comment that everyone on the team would be able to understand), and the code is a bit more future proof (being thread safe now, if I understood your description). Sounds like only a little evil. :) |
|||||||||
|
|
Personally, as covered in a previous thread, I don't believe early optimization is bad in situations where you know you will hit performance issues. For example, I write surface modelling and analysis software, where I regularly deal with tens of millions of entities. Planning for optimal performance at design stage is far superior than late optimization of a weak design. Another thing to consider is how your application will scale in the future. If you consider that your code will have a long life, optimizing performance at design stage is also a good idea. In my experience, late optimization provides meagre rewards at a high price. Optimizing at design stage, through algorithm selection and tweaking, is way better. Depending on a profiler to understand how your code works is not a great way of getting high performance code, you should know this beforehand. |
|||||||||||||
|
|
There are two problems with PO: firstly, the development time being used for non-essential work, which could be used writing more features or fixing more bugs, and secondly, the false sense of security that the code is running efficiently. PO often involves optimising code that isn't going to be the bottle-neck, while not noticing the code that will. The "premature" bit means that the optimisation is done before a problem is identified using proper measurements. So basically, yes, this sounds like premature optimisation, but I wouldn't necessarily back it out unless it introduces bugs - after all, it's been optimised now(!) |
|||||||||||
|
|
I believe it's what Mike Cohn calls 'gold-plating' the code - i.e. spending time on things which could be nice but are not necessary. He advised against it. P.S. 'Gold-plating' could be bells-and-whistles kind of functionality spec-wise. When you look at the code it takes form of unnecessary optimisation, 'future-proofed' classes etc. |
|||||
|
|
Since there is no problem understanding the code, then this case could be considered as an exception. But in general optimization leads to less readable and less understandable code and should be applied only when necessary. A simple example - if you know that you have to sort only a couple of elements - then use BubbleSort. But if you suspect that the elements could increase and you don't know how much, then optimizing with QuickSort (for example) is not evil, but a must. And this should be considered during the design of the program. |
|||||||||||||
|
|
I've often seen this quote used to justify obviously bad code or code that, while its performance has not been measured, could probably be made faster quite easily, without increasing code size or compromising its readability. In general, I do think early micro-optimizations may be a bad idea. However, macro-optimizations (things like choosing an O(log N) algorithm instead of O(N^2)) are often worthwhile and should be done early, since it may be wasteful to write a O(N^2) algorithm and then throw it away completely in favor of a O(log N) approach. Note the words may be: if the O(N^2) algorithm is simple and easy to write, you can throw it away later without much guilt if it turns out to be too slow. But if both algorithms are similarly complex, or if the expected workload is so large that you already know you'll need the faster one, then optimizing early is a sound engineering decision that will reduce your total workload in the long run. Thus, in general, I think the right approach is to find out what your options are before you start writing code, and consciously choose the best algorithm for your situation. Most importantly, the phrase "premature optimization is the root of all evil" is no excuse for ignorance. Career developers should have a general idea of how much common operations cost; they should know, for example,
And developers should be familiar with a toolbox of data structures and algorithms so that they can easily use the right tools for the job. Having plenty of knowledge and a personal toolbox enables you to optimize almost effortlessly. Putting a lot of effort into an optimization that might be unnecessary is evil (and I admit to falling into that trap more than once). But when optimization is as easy as picking a set/hashtable instead of an array, or storing a list of numbers in double[] instead of string[], then why not? I might be disagreeing with Knuth here, I'm not sure, but I think he was talking about low-level optimization whereas I am talking about high-level optimization. Remember, that quote is originally from 1974. In 1974 computers were slow and computing power was expensive, which gave some developers a tendency to overoptimize, line-by-line. I think that's what Knuth was pushing against. He wasn't saying "don't worry about performance at all", because in 1974 that would just be crazy talk. Knuth was explaining how to optimize; in short, one should focus only on the bottlenecks, and before you do that you must perform measurements to find the bottlenecks. Note that you can't find the bottlenecks until you have written a program to measure, which means that some performance decisions must be made before anything exists to measure. Sometimes these decisions are difficult to change if you get them wrong. For this reason, it's good to have a general idea of what things cost so you can make reasonable decisions when no hard data is available. How early to optimize, and how much to worry about performance depend on the job. When writing scripts that you'll only run a few times, worrying about performance at all is usually a complete waste of time. But if you work for Microsoft or Oracle and you're working on a library that thousands of other developers are going to use in thousands of different ways, it may pay to optimize the hell out of it, so that you can cover all the diverse use cases efficiently. Even so, the need for performance must always be balanced against the need for readability, maintainability, elegance, extensibility, and so on. |
||||
|
|
|
Premature optimization is not the root of ALL evil, that's for sure. There are however drawbacks to it:
Instead of premature optimization, one could do early visibility tests, to see if there's an actual need for better optimization. |
|||
|
|
|
I've found that the problem with premature optimization mostly happens when re-writing existing code to be faster. I can see how it could be a problem to write some convoluted optimization in the first place, but mostly I see premature optimization rearing its ugly head in fixing what ain't (known to be) broke. And the worst example of this is whenever I see someone re-implementing features from a standard library. That is a major red flag. Like, I once saw someone implement custom routines for string manipulation because he was concerned that the built-in commands were too slow. This results in code that is harder to understand (bad) and burning a lot of time on work that probably isn't useful (bad). |
|||
|
|
|
The answer is: it depends. I'll argue that efficiency is a big deal for certain types of work, such as complex database queries. In many other cases the computer is spending most of its time waiting for user input so optimising most code is at best a waste of effort and at worst counterproductive. In some cases you can design for efficiency or performance (perceived or real) - selecting an appropriate algorithm or designing a user interface so certain expensive operations happen in the background for example. In many cases, profiling or other operations to determine hotspots will get you a 10/90 benefit. One example of this I can describe is the data model I once did for a court case management system which had about 560 tables in it. It started out normalised ('beautifully normalised' as the consultant from a certain big-5 firm put it) and we only had to put four items of denormalised data in it:
This was (at the time) the largest J2EE project in Australasia - well over 100 years of developer time - and it had 4 denormalised items in the database schema, one of which didn't really belong there at all. |
||||
|
|
|
I suppose it depends on how you define "premature". Making low-level functionality quick when you're writing is not inherently evil. I think that's a misunderstanding of the quote. Sometimes I think that quote could do with some more qualification. I'd echo m_pGladiator's comments about readability though. |
|||
|
|