Don't Sweat the Small Stuff

In the Iron Man article I said one of the keys to good software development is thinking small, as exemplified by the archetypal engineer Tony Stark. What I neglected to mention is that thinking small also means not thinking microscopically... All things in moderation and too much of a good thing, right?

So what exactly is thinking too small? Thinking too small is when you're spending lots of hours of development time working on ways to improve the efficiency of your code that depend heavily on the current behavior of the language or operating system: things you can't control.

There have been lots of examples of this throughout the history of software. One example I remember described by some of the engineers I believe at Sun Microsystems involved the optimization of DLLs. It used to be common for people who wrote DLLs to optimize them by moving the most commonly used functions to the front of the file, because given the operating system's existing architecture, calls to those functions would be faster. Later changes to the operating system changed that, making calls to any function in the library roughly equally efficient -- unless you had previously optimized the DLL. What happened was that software using the optimized DLLs actually performed worse on the new operating system than they would have on the old operating system without optimization.

For a while in the ColdFusion community it was at least semi-popular to use cfscript for performance reasons. Apparently in the early days (pre CF5 I think), the parser for cfscript was more efficient than the tag parser. Then new versions were released and at some point I remember seeing a test case that showed that while cfscript wasn't generally faster, for some reason performing loops with cfscript instead of cfloop was significantly faster. Then another version was released and now as far as I know, there's no difference. Which just goes to show that choosing to use cfscript because it was more efficient was probably a bad idea -- because things change.

I also remember reading at least one article that mentioned similar issues with garbage collection in Java. Not that I've ever been fully immersed in the Java world, but as I understand it, the conventional wisdom with regard to garbage collection has vacillated back and forth between doing a lot of your own garbage collection and then in later versions doing your own garbage collection became a liability.

And then there are databases. It's certainly possible to tell SQL Server to perform a query "with nolock" or "with rowlock" and in theory it should help, but in practice SQL Server largely ignores those hint statements so most of the time it's just time wasted.

Lots of people in the ColdFusion community in particular will tell you never to use evaluate() EVER because it doesn't perform well... same thing with IIF() and for some folks even isDefined()... the problem with these is that this advice depends on something you have no control over -- the internal workings of the language. And in the case of IIF() I'm told that on recent versions of ColdFusion, IIF() and cfif tags produce the same Java bytecode underneath and so both are equally efficient.

And in some cases an evaluate() or isdefined() can be more efficient than the alternative. Consider for example isDefined("Framework.Settings.aSetting"). When you reference any variable in ColdFusion, the server cycles through a list of scopes to find it (form, url, variables and a couple others I think). A lot of folks use this information as an argument against ever using isDefined() however, when using the isDefined function the server will cycle through it's list of scopes just once, looking for the variable named "framework". If you were to instead use StructKeyExists(), which is more precise, you would write something like this: structKeyExists(framework,"settings") and structKeyExists(framework.settings,"aSetting"). If framework is undefined, the condition will short-circuit at the "and" and return false. Otherwise you've just doubled the number of times the server will cycle through scopes looking for the "framework" variable. And in this case you can see pretty literally in the code where you've doubled the server's workload because you've asked it to perform the structKeyExists() evaluation twice. You've given it 2 instructions where you could have given it just one. Although ultimately what I'm getting at here is you shouldn't care on that level -- it's a very minute difference and also prone to changing in later versions of the server, so you're wasting time with any amount of fussing over which is better.

For my money, I think I'd rather focus on things that are more tangible. I do occasionally performance tune -- the SQL abstraction tools got an overhaul a while back for that reason, and various parts of the XHTML library have as well. Recently I even updated the Members onTap plugin to performance tune its RegionManager.cfc by moving away from caching structures to instead using query of query. But when I do that performance tuning I try to focus on things that are really within my control. Instead of choosing between using a cfif tag or using an iif(), I focus on ways that I can reduce the number of instructions the machine will need to process at my level - the number of instructions I've declared.

The first iteration of the framework's XHTML library for example created a separate html node for each option in a query-driven select statement, which meant a half-dozen instructions to create each and a half-dozen instructions to display each. In practice it just wasn't efficient enough to use. Later versions store the query in the select element and loop over it at display time, which reduces the number of instructions from about a dozen per option to about three per option, bringing it roughly in-line with the performance of a flat cfloop.

Related Blog Entries

Comments
BlogCFC was created by Raymond Camden. This blog is running version 5.5.006. | Protected by Akismet | Blog with WordPress