Mature Optimization: An Introduction
carlos.bueno.org
carlos.bueno.org
The modern era of low-latency interaction with high framerate scrolling & animations has completely changed the performance landscape. Performance is not something you can bolt on at the end anymore.
Also, judging from your jargon I'd say you work in games. Games are an entirely different dimension from the mainstream of programming.
Not if that inefficiency is enough to cause visible issues, which is not unusual.
> Also, judging from your jargon I'd say you work in games.
Nope, I work on mobile apps. Which are not entirely unlike games.
In my experience, one caveat to consider is that Knuth appears to be talking about talking about code optimisation, rather than architecture optimisation.
Let's consider a persistence layer. Let's say you make a decision to use an architecture that retrieves objects from a backing store one at a time, rather than also providing a bulk retrieval method. You'll likely hit a performance wall at some point. Unfortunately, when you make the decision to provide bulk CRUD APIs, you may have a lot of code that's not so easy to update. If you'd provided bulk CRUD early on, even if the implementation was simple loops, you could have avoided a serious headache for little cost.
I agree with the general sentiment of not optimising code too early. Blocks of code are easy to change later. Architecture can be very hard to change later - so it's perhaps worth a little more attention upfront.
To take your example, if you had made the decision from the start to abstract the nature of the retrieval method so that implementations like "bulk retrieval" and "one at a time" retrieval can be swapped for each other, you are free to follow Knuth's advice to not prematurely optimize.
It's obviously not possible to accurately predict where optimization will be needed in the future, but a good design means that the disruption from any optimization work will only extend so far.
Changing my API to add bulk retrieval is easy. Changing all (or a significant fraction of) the code that depends on that API to take advantage of the change is hard, particularly when you're working on an enormous product. Unfortunately this is an example of a situation where you often experience death by a thousand cuts, and IME the 97%/3% split doesn't hold up at all.
I think the other reason I think it's worth caring about this stuff up front is because it's not terrifyingly hard/time consuming, and applying simple considerations such that you don't screw yourself later doesn't have to impose an excessive tax upon your codebase quality. The same is often not true of pervasive code microoptimisation.
I suppose there's an extent to which such basic architectural decisions could be likened to simply 'doing the right thing' when you're coding - using sets instead of arrays for 'contains?' heavy code, and so on. I guess I've just seen several projects make obvious blunders when starting up that could have been averted by a pretty small amount of thinking ahead.
In some circumstances yes others no.
In my in house database, I asked a couple of years ago about the relationships between entities in the database. I got an answer, I designed (architected?) it from there. The database tracks samples for a DNA sequencing centre. Now a couple of years later, the technology has changed. And where it was a one to many relationship, its now a many-to-many. The tables affected are at the very heart of the application, so its going to be a pain to change. Is that bad architecture? The requirements genuinely did change, in a way that the non IT staff didn't anticipate.
On the other hand there are plenty of times when I can see a quick hack that my team leader suggests will lead to a tangled mess a couple of months down the line. Basically when the application layer is compensating for a crap database design, then its going to cause problems.
Sure, you could do a programmatic refactor that changes all the calls to request an array of one item, but that's pointless. Actually changing the code so that it truly batch-oriented is inevitably a substantial task.
Batch is hard of course. But it's conceivable to have a crappy implementation (just looping single statements) and then later refactor it to do it database side, or perhaps in parallel (looping single statements simultanously). All the while without modifying the dependent code.
No?
The thing is "refactoring architecture" often requires being able to abstract things in ways that popular programming languages make difficult, and which most programmers aren't familiar with dealing with and reasoning about. Its the same kind of problem that makes Haskell seem hostile to many programmers.
http://carlos.bueno.org/optimization/mature-optimization.pdf