Optimization for Dummies
Posted on November 23, 2007 by Brian Webster
Filed Under Cocoa, Programming, Development |
A somewhat inconspicuous looking article on optimizing third party code has stirred up quite a conversation/flame war among many in the Mac developer community. The comment thread on the article is already quite long, and it seems to me that the people with opposing viewpoints are just talking right past each other at this point, so I thought I’d add in my own perspective here.
The jist of the article is that the author, Ankur, needed to draw some gradients in one of his applications, downloaded the source code for CTGradient, an open source library that provides a class to draw various types of gradients, and decided that it had way more functionality than he needed in his own program. So, he went through the code and basically removed everything he didn’t need for this single application. That’s all fine and good, and the article is actually an interesting look at performing various refactorings and dead code removal.
The problem arises in that he implies that if a developer decides to use some open source code and doesn’t go through and strip out every ounce of functionality that they’re not immediately using, that means that they’re encouraging “code bloat” and that their code is not “optimized”. Several commenters asked what kind of performance/memory gain he actually saw, and the only numbers he provided were from the “Real Memory” column in Activity Monitor, which is a pretty crude measure. The conversation went downhill from there.
I think one main point of miscommunication here is over the terminology the original author uses for some of the things he’s talking about. When you talk about “optimizing” code, I, and most other developers I know of, think of making the code run faster. The author reinforces this by stating “…you can optimize this thing till it runs like a Ferrari”. Certainly sounds like he’s talking about making the code faster, but the vast majority of what he’s doing is simply stripping out code that he’s not going to be using. This has pretty little effect in and of itself - it saves a few KB in disk space, and if the code truly is unused, it probably won’t even get paged into memory in the first place. There are a few places where the code probably runs faster, but the gains look pretty minimal in the big picture. However, arguing the nitty gritty details about his particular optimizations is missing the bigger point…
Engineering is all about tradeoffs, and in computer software, this typically means choosing between things such as memory usage, disk usage, CPU usage, and so on. Every one of these, however, inevitably comes up against the restraint of development time. You can spend days, weeks, or months optimizing your code in various ways, but it’s all for naught if you don’t eventually ship your application. This means that you can’t do everything, and have to pick and choose what areas of your code to work on, whether to add new features or shore up existing ones, and how much time to spend optimizing performance and memory usage.
What is conspicuously missing from the article is any sort of evidence that CTGradient was actually causing any sort of performance or memory problem in his application in the first place. Now, may more have gone on that he didn’t include in his write-up, but it sounds like he simply looked at the code, decided that it was obviously too big and bloated, and set to work spending quite a bit of time hacking it down to the bare minimum he needed.
Finding and fixing performance and memory problems in real applications, however, is rarely so simple. It’s rare that you can glance at a piece of code and immediately deduce that it’s going to be problematic for your application, causing slowdowns or whatnot. Most such bottlenecks are discovered as a result of rigorous testing, using tools provided by Apple such as Sampler, Shark, and Instruments (among others) to dig into the details of what your app is actually doing and where it’s spending its time. Upon discovering such a problem, you can then go in and spend your precious time fixing what most needs to be fixed. It’s not that the modifications he makes don’t actually reduce code size and memory footprint (they do) or increase performance (still not really sure about this without empirical data), but with testing first to find what needs fixing, the time spent doing all this could very well have been better spent fixing something that actually needs fixing.
I actually use CTGradient in a couple of my projects, and I use the code basically untouched. This is not because I’m “lazy” and would rather count my customers’ money while cackling evilly than spend the time to strip out everything I don’t use, but rather because I have plenty of other things to spend time on, optimizing my app in ways that make a difference, and adding features that people want and need. None of my tests of my drawing code have ever shown any performance problems arising from using CTGradient as-is, so my motto is, if it ain’t broke, don’t fix it. The critical flaw in Ankur’s argument in his post is that he never showed any evidence that anything was broken in the first place.
Comments
2 Responses to “Optimization for Dummies”
Leave a Reply
If you have a support issue that requires a response, please e-mail support@fatcatsoftware.com
Brian, the dead code stripping was only to start with. If you look at the final result, it’s much faster than the original. Instead of the struct with r, g, b, a floats and a pointer, you have only one single float. The entire looping and malloc and elementList is gone. Instead of all the logic and calculations in the C function you’re down to a single addition operation. The problem is, people don’t look at the whole article and make accusations claiming I was somehow dissing CTGradient when this is far from the case.
The only reason I only counted the memory savings was because a) people were begging for it and b) I thought it’d be obvious to anyone who looked at the code how much faster it would run. I didn’t count on people misinterpreting and attacking the article.
I think Colin summed it up pretty nicely:
Oh, I don’t doubt that the other optimizations did make the code run faster, although I’d love to see a test that draws a gradient like 10,000 times using each set of code to see exactly how much the difference is (being able to say for certain that my new code is 24% faster is pretty satisfying :-). I think the disagreement lay in the bigger context, over prioritizing what to work on. I think that’s part of the reason the discussion got kind of derailed, that people were arguing totally different points and ended up just talking right past each other. Oy.
I agree that there certainly are different levels of trust when it comes to using code you haven’t written yourself, with Apple’s code having generally a high level of trust (although I’ve certainly been bitten a time or two!), and others coming down at other various levels.
Generally, the more a class/framework is used by others, the more eyes are looking at it, and the higher the level of trust is. Things like CTGradient, Sparkle, Growl, etc. are fairly widely used, so most bugs and such will have been taken care of, and there’s also a community there to offer help when a problem does come up. I’ve also certainly encountered pieces of code that were pretty sketchy, and while the concept was good, I decided to just write my own version instead.