Considering performance in the small is not “premature optimization”
joshbarczak.com
joshbarczak.com
It always seemed evident to me that Knuth's exhortation was about making sure that the thing you were optimized was in fact a major contributor of code time usage -- and that your change was an improvement.
Telling people to avoid, for example, bounds checking because it might turn out to be a cycle soak later sounds like a good way to make your software worse, not better, in the hopes that a few instructions saved will make the difference. I once worked on a code base once with three different half-right hand-hacked versions of date formatting code. I replaced them with strftime(). Certainly the call was slower, but I was provably better off optimizing the timer routine that ran 50 times a second than worrying about hand-formatting dates into strings.
The PDF he linked to for bounds-checking is a series of slides that notes that conventional approaches to bounds-checking incurs a 60% runtime performance hit, and that there is another approach that works as well but incurs only about a 23% runtime performance hit.
Aside: rapidly improving energy efficiency has probably been one of the greatest technological developments of the last 20 years; it would be amazing if software development practices could be similarly adapted. Like, is it possible to have a demoscene approach to software development while still keeping portability and maintainability? Imagine the transformation in software we could see in the next 10 years if someone figured out how to do that.
Turn airplane mode on, leave your screen off, and your phone will last a long time.
As an additional perspective, compare the solution of an engineer with 2 years of experience to that of an engineer with 5 years of experience. If the solutions are drastically different then interpretation of a rule such as "avoiding premature optimization" will be drastically different as well.
Like any overly simplified statement, it is actually highly subjective. The author of the article even calls out the specific context in which their interpretation of Donald Knuth's rules are being applied, "I’ve listed lots of relatively low-level things up there, but that’s just because it’s the level I work at." and as such their interpretation doesn't necessarily apply in other contexts.
Premature optimization is a problem if you're approaching it from a place of ignorance. If you're doing it mindfully based on experience and domain knowledge then it starts to make sense. But even under these conditions your best intentions can be wrong. I've been in plenty of situations where I thought I'd identified a code bottleneck only to have a far easier, cheaper, and better solution completely unrelated to code come to light.
The version I've always heard is:
Make it work, make it right, make it fast.
I stopped measuring at ~40,000 database round-trips.
"Engineering time costs more than CPU time" was the attitude, and for the original problem in its original specification, the solution was clearly OK.
But here we are, now needing to work out the original specifications, work out the current implementation (in case they differ anywhere), and work out whether it's worth re-writing it top-down or just fixing the worst of the loops.
And I'm not blaming whoever wrote this originally, it must have done its job to make it into the code base, but it really sucks to have to unpick it because an assumption of "database calls are free" is an assumption that unravels in a really messy way.
That always seems to be the problem: refactoring is only cheap if you can be sure that your test coverage catches it if you break something. Of course, the code bases that need heavy refactoring probably don't have it, because otherwise they would have been improved already...
That statement leads me to believe this was technical debt that went unpaid and not a failing of design. Additionally, if your company is in a successful enough position to spend the time required to pay down this technical debt then if I were in your position I would count myself as lucky.
"...it must have done its job to make it into the code base...", exactly right! Every time I hit one of these situations I view it as a way for me to, "Try and leave this code a little better than you found it." I'm all about making things easier for the next guy who comes along.
Cache: https://webcache.googleusercontent.com/search?q=cache:zjuUa-...
But it's impossible not to gloat at least a little bit about someone lecturing us about low level efficiencies from a site that can't manage to serve static blog content to a few hundred visitors a minute (or whatever it is that the top slot on HN drives these days).
If my blog is unreadable for five minutes a month, I am perfectly happy and I'm not going to change anything.
I wouldn't dream of keeping a blog that couldn't handle a HN traffic spike. That's the whole point of having a blog.
Failing at this is like launching your startup's MVP to an audience of the first fifty people who were able to load your product launch page before it crashed under the load. In other words, a giant, easily-avoidable waste of effort.
It seems possible that some people don't care that much about HN traffic spikes.
"The ratio of bytes loaded to load time should be very close to the I/O throughput of the machine."
How do you think the bytes served for a few hundred requests a minute compares to the theoretical I/O throughput of the relevant machines?
And because no one can see my tongue in my cheek across the internet, I'll be explicit that I'm sure he is a user of his publishing environment, not it's author, and so it's fair to view this as just another symptom of the problem he's discussing.
Thinking is hard, and takes time, and we want to get the feature out now, immediately, and worry about performance later. If at all.
And, sad to say (for an engineer), it's not clear that from a "business" perspective it's wrong. Hard to argue when accumulating features seems to matter more than crappy software. We have a lot more software these days, to run all of these bright new pieces of hardware, and perhaps because I'm an old-timer, the general quality seems to have degraded significantly. But the novelty of the stuff certainly has exploded, and I'm continually delighted by the twists and features that folks are coming up with, while being saddened by crashes, slowdowns, need for restarting, etc.
If CS majors spent a fraction of the time learning how to optimize the way EE/CEs do, we'd need a lot less magic from the latter.
CS programs vary, but basically all of them give you the tools needed to write efficient code. They generally don't teach you much about software "engineering", though, which remains a chaotic mess of a field.
So, a CPU?
One good point from the essay though is Knuth's example of 12% speed increase for low effort is definitely worth doing. I agree.
A better way of putting things is:
Considering low effort, small performance improvements that don't affect other factors such as code readability or system maintainability is not "premature optimization".
If you are considering performance "in the small" and it affects maintainability you are indeed prematurely optmizing.
It takes me literally no time whatsoever to know intuitively that, yes, this should be a struct rather than a class. But I've seen developers dismiss this as "premature optimization" rather than knowing how my software works.
Or, when I was at TripAdvisor, we (not me originally, though I inherited it) greatly improved typeahead performance by dropping the built-in collections classes (which used boxed primitives, because lolbrokengenerics) in favor of Trove (or Colt, I can't remember) and the reified primitive types. If I write Java today, I reach for Trove as a matter of course just because it means I never write List<Integer>.
This stuff matters. Getting in the right habits is not hard and will pay off in the ten percent of cases where it matters.
One man's saw sharpening [1] is another man's yak shaving [2].
Personally, I like my saws very sharp, thank you very much, but I think it's important to also have sympathy for people with different backgrounds and priorities.
[1] http://c2.com/cgi/wiki?SharpenTheSaw [2] http://programmers.stackexchange.com/a/34788
There's a lot of low hanging fruit, especially in languages like C and C++, where a slight code modification can make a difference in run time, with zero impact on code readability. Here's one example:
Using:
size_t len = strlen(myString);
for (int i=0; i<len; ++len) { ... }
Over: for (int i=0; i<strlen(myString); ++len) { ... }
The second for loop is potentially O(n^2) because strlen needs to scan the entire string every time through the loop, where the first for loop computes strlen once. It's possible the compiler can optimize so that only one strlen is used in the second case, but it's not guaranteed. Probably it doesn't matter, but it will start to slow down once this function is called with longer strings and/or inside tight loops.They're almost the same, so why ever use the slower one?
And C++ is even worse with destructors and copy and move constructors being called all over the place for people who aren't careful.
And I do think it's "zero impact on performance" in any real program; even if the compiler didn't optimize, I'll bet that particular loop wouldn't even show up in your profiler.
A second reason the first is better is that it clearly conveys to the next programmer that there is no intention for myString to change length during the running of the loop. If I came across the second example I would wonder if the length of myString was changing in the loop.
Compilers have a limit to how smart they can be, and that totally makes being smart about your stuff all the more important.
I work in Android, so optimizing I look at first is bitmap loading, backgrounding tasks, and sqlite queries. Usually, this is where 90% of the performance benefit comes from.
He is right about premature optimizations and also right about craftsmanship.
Many performance problems stem from bad overall design decisions, bad abstractions and bad data structures (summarized: craftsmanship). I always wonder, how fast today's processors have become and how slow they get with today's software.
One of my first computers had 64k of memory and a processor that was so incredible slow compared to today's processors, that it is unbelievable. And still, they made so many good software with it. In today's programs the resources of this computer would not suffice for the idle loop.
Also the first Unix computer that I worked on, had only 8MB of memory and had X11, LaTeX and many other stuff!
It was a long way down the road of abstractions, that today's computer could hardly live with 1GB of memory and 1GHz processor (2 cores, please, please!) for basic usage.
The real art in computer science is, to know where to optimize -- to use your time most effectively.
His whole argument boils down to: > Considering performance in the small is not “premature optimization”, it is simply good engineering, and good craftsmanship
But that's the same reason German industry tends to compete so badly right now. They do a lot of hand trimmed and finished components that really could have been better designed for automation and require less custom craftsmanship. His argument seems to boil down to aesthetics.
Many people don't even know the context or the original quote. And many times I've seen discussions about improving a piece of code shot down by a single incantation of this Knuth. It's sad.
Be smart when developing, and know where the easy optimizations and common pitfalls are in your language and toolset. Optimize as you can without sacrificing maintainability.
Don't forget web browsers. Web browsers are horrible.
And no, incorporating speed in the specification doesn't make any difference. Suppose the spec says, "page must load in 200ms." Fine, if you don't care what correct page loading means, you're perfectly served with a blank one.
What's at the root of such intellectual capitulation? Complacency? Absence of skills? "Correctness is hard, let's just randomly perturb settings instead while fiddling with a stopwatch. Correctness is hard, let's just conflate motion with progress."
Whence the shabby treatment of correctness like porn: I'll know it when I see it.
I don't understand what's new.
And stop quoting Knuth to justify it.