(picture)

December 31, 2003

Optimization

I have very little to say about optimization. Some, but no more than necessary (hah).

"We've tested razors with any number of blades," [Occam] says, ignoring the five-bladed machete hanging overhead.
Paradoxically, working on optimisation is an incredibly slow, and lengthy, process. It really is endless: every time you're tempted to say it's done, there is much more to come. After several days' work and some good results, my code is still JavaScript not C++, and it's still very dumb about talking to the SQL database, and so on. The corollary of this is: don't be disheartened when performance gets worse. Sometimes a bright idea simply doesn't work out.

In no particular order, a few things I learned:

  • N-squared and worse high-order behaviours tend to get noticed. My app had one, and of course it was just a stupid bug; I noticed when taking the code from a development machine with twenty shared spaces, onto a lab machine with 500.
  • Measure, if you can. If you don't think you can measure, then find a way. It's too hard to guess which pieces of a system are making it slow: time your operations; count them.
  • Focus on places where you're running slow processes many times. Don't worry about slow operations which only happen once; and don't spend much effort on fast operations which happen many times. In my bot code, one of the biggest gains was where I used to read and parse an XML configuration file from disk every time it was needed; now it's only parsed once for each "work cycle".
  • Look at every place you do any network traffic (HTTP, SOAP, database work, etc), and ask why it's being done right there. Are you doing it more than once? Can it be done once, upfront, or deferred until later? Same for on-disk files and databases. In Groove, opening a telespace is much more expensive than opening a telespace descriptor; opening a record is much more expensive than finding a recordID.
  • Make your code lazy. Don't do unnecessary work. Especially, don't do dumb unnecessary work. If you know that there has been no change, or that a query will return nothing, or that an update will only replace identical data: just don't do it.
  • Cache. Instead of constructing an object (slowly) every time you need it, make it once, put it in a memory cache, and fetch from there when needed. Cacheing can be dangerous, though, unless you have a well-defined lifetime (in my bot, many things could live for a whole work cycle, at the end of which all the caches are flushed).

Finally, specialize. Where class methods have lots of code like if(this.m_Flags & FlagOne){...something...} else {...} and the flags are only set in your constructor, why not make some specialized classes to handle the different activities? Then the test only happens once. This sort of specialization actually saves so little processor time as to be completely useless, but it does make me feel good.