Friday, April 24, 2009

The Root Of All Evil

In my most recent round of interviews, I chatted with the founder of U1 Technologies. He and I had a lot in common philosophically and, with a slight shift in timing, I might very well have been working for him instead of Maxis.

One of the things that caught my attention was his phone screen questions. I've been on both ends of these things, and they usually consist of simple questions that ensure that the candidate isn't obviously inflating his or her resume. Things like: "Explain how an Iterator works." or "When would you use a List versus a Map?" (Yes, I've actually been asked these.)

Nathaniel's first question reflected his company's decision to only hire senior engineers: "When should you optimize your code?" Fifteen years of software engineering provided a quick answer: "When you know there's a performance problem with it."

"Premature optimization is the root of all evil," is a quote that's been attributed to both Donald Knuth and C.A.R. Hoare. (Most people cite Knuth, who cites Hoare, who denies saying it, according to Wikipedia.)

This may sound at odds with one of my other favorite programming axioms: "The earlier you find the bug, the cheaper it is to fix." But optimizing code usually goes hand in hand with making it less maintainable and reusable. Specializing algorithms, caching values, adding different control structures, and more all add complexity. If you don't have performance numbers to back up that work, you're adding the complexity and (probably) not getting any benefit from it.

You may have inefficient code that rarely gets called. You may think something's inefficient when in fact the compiler or optimizer or processor does just fine with it. Or your code could actually be inefficient and it might just not matter enough to affect the user.

I've seen this in practice for years. And yet the temptation to optimize is always there, coaxing even the best programmers into a fen of tangles and mazes and confusion.

While I was working on the "real time time update" feature of my reusable date picker pop-up, I set it up so that the delegate would be called for every UIControlEventValueChanged message from the UIDatePicker.

I envisioned the date and time changing as the wheels in the date picker spun to their new locations, so I decided to be smart about calling the delegate method.

I could have written this in my event-handling method:

if ([delegate respondsToSelector:@selector(dateChangedWithoutSaving:)]) {
[delegate dateChangedWithoutSaving: date];

But I assumed that respondsToSelector would be a relatively expensive call. I also assumed that the runtime binding to dateChangedWithoutSaving: method would be costly. And I didn't want that cost slowing down the user's interaction with the date picker wheels. Instead, I made a boolean in the controller that tracked whether or not the delegate actually supported the method and set it at the same time I stored the reference to the delegate:

- (void) setDelegate: (id) delegate {

delegateRespondsToContinuousDateChanged = [delegate respondsToSelector:@selector(dateChangedWithoutSaving:)];

I changed my event-handling logic accordingly:

if (delegateRespondsToContinuousDateChanged) {
[delegate dateChangedWithoutSaving:[myDatePicker date]];

Then I fired up my app.

And discovered something I didn't know. That UIControlEventValueChanged message only gets sent once the date picker wheels have finished spinning. Rather than sending a message for every number that cruises by, it only sends, essentially, one message per gesture. My optimization didn't buy me much, since the code can't possibly interfere with the spinning wheel animation.

So what did my "clever" optimization get me?

  • My own implementation of setDelegate (rather than the one created by Objective C's synthesize keyword)

  • An extra layer of logic in my event-handling code.

I've added more complexity (the extra layer of logic) and more lines of code (the setDelegate method) with the concurrent possibility of bugs.* Performance gain: virtually none.

Premature optimization is the root of all evil.

* Yes, of course, this isn't much extra complexity or code. But add up enough items like this, and you get a significant amount of technical debt

1 comment:

  1. This comment has been removed by a blog administrator.