How to Write Fast Code
asserttrue.blogspot.com
asserttrue.blogspot.com
1. Do we have to optimize this? Time is never free, and opportunity cost in terms of engineering effort is usually very significant.
2. Can we do less work? (this article)
3. What's the bottleneck? CPU, FP, memory bandwidth, lock contention?
4. How do we squeeze out better performance? Assembly, loop unrolling, etc.
I usually cringe when I hear about people talking about #4, as very few of them have asked #1, #2, or #3 yet.
And usually, you just hear people talking about doing what the compiler's already doing (writing it in assembly? /The compiler already does that/), usually doing a pretty reasonable job at high optimization levels. They just exchange maintainability for the warm fuzzy feeling that they've been as macho as needed.
Use the 80-20 rule. 80% of your code's time is spent in 20% of the code.
Optimizing without good profiling is like painting a portrait blindfolded.
Pre-optimizing by coding your system to be "fast" before you get to profile it is like starting to paint a portrait before your model is even sitting.
When Kent Beck came in to optimize the Chrysler C3 payroll system, he discovered that one of the biggest time-sinks in the system was string concatenation!
It's also useless to optimize before you have a system that runs correctly. Kent also asked how he could validate the C3 system so he could optimize it. He was told it wasn't even producing correct results yet. He replied: "In that case I can make it run really fast!"
What are the facts? Again and again and again---what are the facts? Shun wishful thinking, ignore divine revelation, forget what "the stars fortell," avoid opinion, care not what the neighbors think, never mind the unguessable "verdict of history"---what are the facts, and to how many decimal places? You pilot always into an unknown future; facts are your single clue. Get the facts!
- lazarus longPreferably, define targets in real (really real) world terms. This is difficult, but "a user should reasonably be able to complete this macro-process in 5 minutes" is the kind of target.
I've seen so many specs that demand "millisecond response time" for the application. Or something ludicrous. (Requirements for business continuity and downtime/etc are usually similarly crazy/unqualified).
When one consultancy I know got a similar requirement forced down their throat contractually -- they simply changed the application so that a process that was a few pages, was now dozens. Much shorter delays! The fact is that the user wouldn't even blink at a several-second delay when submitting (it was a big operation, so you'd expect a delay). The whole process was longer and more cumbersome - they'd optimised the wrong thing entirely.
It's an extreme example I know -- But the upshot is that the target was wrong -- It's not just the people writing the specs. In a lot of places, it's still rare for developers to sit down with actual users. This always seemed crazy to me (half the time it was because companies are worried that developers will go "hey yeah sure, I can implement that in an hour!" -- which does admittedly sometimes happen).
So you're absolutely right! :) Profile, Profile, Profile -- but don't forget to profile your users (imho do this first).
1. is the human variant. 3. finding which thing to "do less" of (benchmarking/profiling seem to be the "do less" ways of doing this task). 4 from the low level CPU perspective.
As I see it, all "four stages of optimization" are the application of the same rule just from different perspectives.
Modern CPUs do not "execute a fixed number of instructions per second"; even leaving aside the fact that certain instructions are faster than others, factors such as the order in which you access memory locations and the predictability of branches can easily result in a tenfold difference in performance.
In many cases, code can be made faster by executing more instructions; I recently dealt with some code where changing a (theoretically) O(N) algorithm to an O(N log N) algorithm and increasing the number of operations by a factor of 10 nevertheless resulted in the code running twice as fast. Now, this was an extreme case -- the code went from accessing completely random memory locations to accessing completely sequential memory locations -- but it demonstrates the importance of memory access patterns.
Very true for x86, but AFAIK things like ARM based chips are quite a bit closer to 'fixed number of instructions per second'. Still penalties for badly thought out memory access etc though.
If you're using a higher-level language like Python or Ruby, worrying about things like locality should be pretty low on your performance checklist.
If you're using Python or Ruby for CPU-intensive applications, you probably don't have a performance checklist.
And Python and Ruby both have excellent C bindings, you can rewrite CPU-intensive portions of your application pretty much anytime you need to.
Of course. But at that point you're writing your CPU-intensive code in C, not in Python or Ruby. Saying "Python and Ruby are fast because they can call C code" makes no more sense than saying "Humans are really good at arithmetic because they can use calculators".
By using Python I can write the application quicker, test for correctness, and optimize the most important pieces without wasting time writing the entire thing in C.
IIRC, the blocksorting algorithm requires more than 1MB; the figure 12n for the suffix sort sticks in my head, but I don't recall the specifics.
But hey, you can merge-sort a million records in only 20 sequential passes! Not sure how to apply that to BWT though.
IO is. And, if you're not IO bound, you're probably memory bound.
Yes, caches help a lot, but a memory cache miss is 1-400 cycles and IO is far more expensive. In other words, cosine is faster than a cache miss.
Not really: if you're improving the program's memory access behavior, the machine really is "doing more": by removing a memory stall, you're replacing a situation in which the processor was idling with a situation in which the processor is doing useful work. Prefetching and speculative execution are other examples of the "do more work, spend less time idling" theme.
For projects that take advantage of this, see: http://spiral.net http://www.fftw.org/
Your N is 10^10? That's a big problem.
For example, right now I'm detailing some design principles for a project I'm working on.
I realise there is a limited amount of time to be able to complete the project effectively and to make it look decent, so in the process I've come to make some hard choices.
I've found the trying to be creative with less is working out well so far and in the end it should pay off.