While getting rid of extraneous junk in an application package is easy using Trimmit, the only way to prevent "code bloat" (and accompanying excess RAM and CPU usage) is through good programming practices. Where most developers fall short is in poor optimization of borrowed code. Let’s take CTGradient as an example as it’s well known and used (or more accurately, abused) in dozens of applications.
CTGradient contains an incredible diversity of built-in gradients, gradient styles, and methods for creating fancy rainbows, radial gradients, linear gradients, aqua gradients, and a number of other interesting class and instance methods. It allows you to dynamically alter gradients by adding or removing colors, changing the level of transparency, filling NSRects or NSBezierPaths, rotating the gradient, etc. For demonstration purposes, all these features are excellent. For production, this is a nightmare.
CTGradient.m weighs in at over 1300 lines of code.
Ignoring Mr. Weider’s unique style of formatting, take a look through the code. If you’re putting this class in an application, you can immediately remove over a thousand lines. Just like that. But if you take some time to understand what’s going on, you can optimize this thing till it runs like a Ferrari.
You’ll notice Trimmit uses a gradient background for it’s window. Let’s cut down CTGradient until it matches the level of optimization of Trimmit’s gradient code.
Clear the junk
Firstly, let’s remove the methods we know for sure we won’t need. Remove the following methods completely from both the interface and the implementation (scroll down for more):
// We don't need the preset gradients
+ (id)dividerGradient;
+ (id)statusBarGradient;
+ (id)aquaSelectedGradient;
+ (id)aquaNormalGradient;
+ (id)aquaPressedGradient;
+ (id)unifiedSelectedGradient;
+ (id)unifiedNormalGradient;
+ (id)unifiedPressedGradient;
+ (id)unifiedDarkGradient;
+ (id)sourceListSelectedGradient;
+ (id)sourceListUnselectedGradient;
+ (id)rainbowGradient;
+ (id)hydrogenSpectrumGradient;
// We won't make any drastic modifications to the gradient once created
- (CTGradient *)gradientWithAlphaComponent:(float)alpha;
- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position;
- (CTGradient *)removeColorStopAtIndex:(unsigned)index;
- (CTGradient *)removeColorStopAtPosition:(float)position;
- (CTGradientBlendingMode)blendingMode;
- (NSColor *)colorStopAtIndex:(unsigned)index;
- (NSColor *)colorAtPosition:(float)position;
// Now we don't need to conform to the NSCopying and NSCoding protocols
- (id)copyWithZone:(NSZone *)zone;
- (void)encodeWithCoder:(NSCoder *)coder;
- (id)initWithCoder:(NSCoder *)coder;
// We only need to fill a simple NSRect
- (void)drawSwatchInRect:(NSRect)rect;
- (void)radialFillRect:(NSRect)rect;
- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle;
- (void)radialFillBezierPath:(NSBezierPath *)path;
// Remove the entire (Private) category;
// but leave -addElement:
- (void)_commonInit;
// move setBlendingMode code into init
- (void)setBlendingMode:(CTGradientBlendingMode)mode;
- (CTGradientElement *)elementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtPosition:(float)position;
The following C functions are now unused:
static void chromaticEvaluation(void *info, const float *in, float *out);
static void inverseChromaticEvaluation(void *info, const float *in, float *out);
static void transformRGB_HSV(float *components);
static void transformHSV_RGB(float *components);
static void resolveHSV(float *color1, float *color2);
And also remove the following from the header:
typedef enum _CTBlendingMode
{
CTLinearBlendingMode,
CTChromaticBlendingMode,
CTInverseChromaticBlendingMode
} CTGradientBlendingMode;
Remove the <protocols> and unnecessary instance variables so the interface looks like this:
@interface CTGradient : NSObject {
CTGradientElement* elementList;
CGFunctionRef gradientFunction;
}
+ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end;
- (void)fillRect:(NSRect)rect angle:(float)angle;
- (void)addElement:(CTGradientElement *)newElement;
@end
Before we go further, we’ll need to take a detour and fix the remaining code so that it compiles. That’s easy enough - just remove any references to code we’ve removed.
If you’ve been following along, your CTGradient should now look like this (also made it readable!).
In a few short minutes, we’re down from more than 1300 to a little over 200 lines.
It gets better.
Optimize it
First stop, fillRect:angle:. Since the gradient for Trimmit’s window only runs vertically (angle 90), we can straight away take out the angle argument, and also cut the entire if / else structure with the angle down to the two lines that are under the if(angle == 90). Ah, much better.
Now things start getting a teeny bit more complex - and fun.
The CTGradient code is written so that one can have many different color stops. The CTGradientElement struct has a nextElement which points to the next CTGradientElement and the position of the current element is stored under the float position. However, we need just two - the starting and ending shades.
Let’s take a look at cutting the multiple elements down to just two.
The addElement: method has a lot of looping to insert the element at the correct place. However, since we know that we only ever need two, we can cut it down to this:
- (void)addElement:(CTGradientElement *)newElement {
if(elementList) {
// elementList exists, add second element
elementList->nextElement = malloc(sizeof(CTGradientElement));
*(elementList->nextElement) = *newElement;
elementList->nextElement->nextElement = 0;
} else {
// no elements - add first element
elementList = malloc(sizeof(CTGradientElement));
*elementList = *newElement;
elementList->nextElement = 0;
}
}
Similarly with dealloc,
- (void)dealloc {
CGFunctionRelease(gradientFunction);
free(elementList->nextElement);
free(elementList);
[super dealloc];
}
While we’re at it, let’s clean up init as well:
- (id)init {
if(self = [super init]) {
CGFunctionCallbacks evaluationCallbackInfo = {0 , &linearEvaluation, 0};
static const float input_value_range[2] = { 0, 1 };
static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
gradientFunction = CGFunctionCreate(&elementList, 1, input_value_range, 4, output_value_ranges, &evaluationCallbackInfo);
}
return self;
}
Now we head to the linearEvaluation function. info passed in refers to elementList (see the CGFunctionCreate call). Again, since we know that we’ll only ever have two elements we can reduce it to:
void linearEvaluation (void *info, const float *in, float *out) {
float position = *in;
CTGradientElement *color1 = *(CTGradientElement **)info;
CTGradientElement *color2 = color1->nextElement;
out[0] = (color2->red - color1->red)*position + color1->red;
out[1] = (color2->green - color1->green)*position + color1->green;
out[2] = (color2->blue - color1->blue)*position + color1->blue;
out[3] = (color2->alpha - color1->alpha)*position + color1->alpha;
}
And now we can finally remove the lines from +gradientWithBeginningColor:endingColor: where the positions are set, and also remove the position float from the CTGradientElement struct.
We’re now down to a little over 100 lines. Your CTGradientElement.m should now be looking something like this. Going well, but let’s take things up a notch.
For Trimmit’s background, we’re not interested in the red, green, blue and alpha components - we just need a grayscale shading. This is the most fun part.
Instead of float red, green, blue, alpha; in the CTGradientElement struct, we can have just float shade;.
Now, during init we have:
CGFunctionCallbacks evaluationCallbackInfo = {0 , &linearEvaluation, 0};
static const float input_value_range[2] = { 0, 1 };
static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
gradientFunction = CGFunctionCreate(&elementList, 1, input_value_range, 4, output_value_ranges, &evaluationCallbackInfo);
The third argument, input_value_range is the domain. This gets passed into linearEvalution through *in.This is the independent variable. When we’re drawing the gradient, linearEvaluation is called with *in (the domain) starting at the first value of input_value_range and ending up at the second. So, something like 0.000, 0.001, 0.002 … 0.998, 0.999, 1.000. The function’s job is to set the the color components for each given value of the domain.
The first optimization we can make here is to return only one channel instead of four. This changes it to:
- (id)init {
if (self = [super init]) {
CGFunctionCallbacks evaluationCallbackInfo = {0, &linearEvaluation, 0};
static const float range[2] = {0, 1};
static const float domain[2] = {0, 1};
gradientFunction = CGFunctionCreate(&elementList, 1, domain, 1, range, &evaluationCallbackInfo);
}
return self;
}
This change impacts back on the linearEvaluation function - now it only needs to return one channel:
void linearEvaluation (void *info, const float *in, float *out) {
CTGradientElement *color1 = *(CTGradientElement **)info;
out[0] = (color1->nextElement->shade - color1->shade)*(*in) + color1->shade;
}
And don’t forget +gradientWithBeginningColor:endingColor:. We improve performance here, as we only need the shade - not a color. We can rename it to something appropriate.
+ (id)gradientWithBeginningShade:(float)begin endingShade:(float)end {
id newInstance = [[[self class] alloc] init];
CTGradientElement color1, color2;
color1.shade = begin; color2.shade = end;
[newInstance addElement:&color1];
[newInstance addElement:&color2];
return [newInstance autorelease];
}
Now we’re only returning one grayscale channel. But hold on, we’re still in the RGB colorspace! That’s easily fixed. In fillRect:, replace:
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
#else
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
#endif
with:
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
CGColorSpaceCreateDeviceGray is device-dependent on Mac OS X 10.3 and below, but starting from Tiger, it’s now device-independent which is good in terms of appearance.
While we’re in fillRect:, we can also make a few more improvements:
- (void)fillRect:(NSRect)rect {
CGContextRef currentContext = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(currentContext);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
CGContextClipToRect(currentContext, *(CGRect *)&rect);
CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, CGPointMake(0, 0), CGPointMake(0, NSMaxY(rect)), gradientFunction, 0, 0);
CGContextDrawShading(currentContext, myCGShading);
CGShadingRelease(myCGShading);
CGColorSpaceRelease(colorspace);
CGContextRestoreGState(currentContext);
}
I moved the startPoint and endPoint CGPoints straight into the CGShadingCreateAxial call. Also, we only ever fill the whole view with the gradient, so just put in 0 instead of using NSMinX and NSMinY. We could replace the CGPointMake(0, 0) with (CGPoint){0,0}, but I’m not sure if that’d be noticeably faster.
Our code is at a lean 80 lines. Now for the biggest change yet.
Heading back to dear old linearEvaluation. Look at this line:
out[0] = (color1->nextElement->shade - color1->shade)*(*in) + color1->shade;
What’s happening here? Think about it like this:
Essentially, we’re just "graphing" a straight line:
y = mx + c
where x is the position, *in, and y is the shade we return (out[0]), and c is the initial shade.
Since our x value only goes from 0 to 1 (see domain in CGFunctionCreate), at x = 0, we will have y = c. At x = 1, we’ll have y = m + c.

So we only need the start shade as our *info value, since we can have m as the difference between the final and initial shades. Let’s effect this in our code.
We can get rid of the CTGradientElement struct. Remove from the header:
typedef struct _CTGradientElement {
float shade;
struct _CTGradientElement *nextElement;
} CTGradientElement;
Instead of the struct, we’ll use a single shade instance variable. We don’t need the +gradientWithBeginningShade:endingShade: anymore, nor do we need -addElement:. So our interface now looks like this:
@interface CTGradient : NSObject {
float shade;
CGFunctionRef gradientFunction;
}
- (void)fillRect:(NSRect)rect;
@end
From the implementation, we can remove the free calls in dealloc as well as the whole of +gradientWithBeginningShade:endingShade: and addElement:. We can set the shade in the init method (and replace &elementList with &shade), and change the linearEvaluation function to:
void linearEvaluation (void *info, const float *in, float *out) {
out[0] = *(float*)info + (*in)*0.1;
}
The 0.1 is what determines how much brighter the top of the gradient is (it’s the m value of our straight line) compared to the bottom.
But since the (*in)*0.1 will go from 0 to 0.1 (as *in goes from 0 to 1), we can actually change the domain we declare for CGFunctionCreate to {0, 0.1} and have our function as:
void linearEvaluation (void *info, const float *in, float *out) {
out[0] = *(float*)info + *in;
}
You can now add accessors for shade if you need to change the shade of the gradient or you could even have an initWithShade: method. In fact, here’s an Xcode project with the gradient.
We now have our gradient code down from 1300 lines to just over 30 lines.
We’ve almost reached a similar level of optimization to Trimmit’s gradient. There will be further optimizations you can make, but it depends on what you’re using the gradient for. For example, if the gradient you’re drawing is always the same, you can optimize further by removing the shade instance variable and all references to it, and hardcode the value into the linearEvaluation function.
Getting to the end of this article, you’re probably thinking that it would be a bit of a waste to use CTGradient if by the time you optimize it for your application, it doesn’t resemble the original at all. And you’re quite right. The documentation already shows you how to draw gradients, yet the number of applications using CTGradient - the whole 1300 lines of it - is astonishing.
Please: When you use other people’s code, don’t put it in without a thought. Go through it, understand it, and optimize it for your specific need. For the better performance and reduced RAM usage, computers will thank you.
124 Comments so far
Leave a commentI’d be surprised if it actually ran noticeably faster, but it certainly improves readability.
recorded by Taybin on November 21, 2007 11:04 am | Permalink
Always skeptics. We went from 1300 lines of code, to under 30. There will definitely be huge gains in performance. We’re no longer passing a struct around with four floats and a pointer, no more create / destroy every time, significantly less computation in the function, etc., etc. (I speak from experience. The memory usage for Trimmit went waaay down after some smart optimizations.)
I think the way CTGradient is formatted, any change would improve readability.
stated by Ankur on November 21, 2007 11:20 am | Permalink
“I think the way CTGradient is formatted, any change would improve readability.”
Yeah, that’s like the worst of all worlds formatting.
revealed by Taybin on November 21, 2007 11:46 am | Permalink
While I agree that this change is more readable, show me a benchmark, a profile, some numbers that there are these “huge gains” in performance that you speak of.
Give us the numbers! The skeptics will always be here without hard evidence. We’re doing computing here, not tweaking of cocktail recipes. Back up what you say with data, and the skeptics will all go away.
Because after all,
“Premature optimization is the root of all evil” — CarHoare
http://c2.com/cgi/wiki?PrematureOptimization
voiced by Joe Goh on November 21, 2007 11:59 am | Permalink
Agree that you need to post real benchmarks. This was a great exercise in optimization (well, a great exercise in spatial optimization at least - a poor exercise in performance optimization because you didn’t measure!). I’m glad you posted it, but the fact that you’re letting the benefits float in theoretical land makes the exercise a lot less admirable.
If you can do real performance testing that shows a significant benefit, you’ll not only win more respect from readers, but also convince some people to switch to your code.
The reason the original code is “Just Fine” to most of us? We’re more than happy to give up the memory usage for 1300 lines of code in order to have a simple, drop-in solution for gradient rendering. I like your refined and minimalist expression of the code, and will probably switch to it if there are demonstrable benefits. Without demonstrable benefits? The risk of switching code bases isn’t worth it.
professed by Daniel Jalkut on November 21, 2007 12:14 pm | Permalink
Following up … in general I would say it’s better to use code without reviewing it or obsessing over the finer details. After all, we take the pros and cons of framework code from Apple in the MEGABYTES. Unless the code you’re importing can be fixed to the measurable benefit of your application, then it’s not worth reviewing it as long as it satisfies the criteria you imported it for.
written by Daniel Jalkut on November 21, 2007 12:18 pm | Permalink
Be aware that massively editing someone else’s code means that you’ll have to do it all over again when you update to a newer version of it.
posted by Peter Hosey on November 21, 2007 12:38 pm | Permalink
I’m not saying that the CTGradient rewrite here is incorrect or buggy, but rewriting correct code that is already known to be widely used means that we’re risking introducing bugs into our products for no known benefit, excepting readability for us.
A relevant excerpt for this discussion from this great article from Joel On Software (which I highly recommend everyone to read): http://www.joelonsoftware.com/articles/fog0000000069.html
“There’s a subtle reason that programmers always want to throw away the code and start over. The reason is that they think the old code is a mess. And here is the interesting observation: they are probably wrong. The reason that they think the old code is a mess is because of a cardinal, fundamental law of programming:
It’s harder to read code than to write it.”
“The idea that new code is better than old is patently absurd. Old code has been used. It has been tested. Lots of bugs have been found, and they’ve been fixed. There’s nothing wrong with it. It doesn’t acquire bugs just by sitting around on your hard drive. Au contraire, baby! Is software supposed to be like an old Dodge Dart, that rusts just sitting in the garage? Is software like a teddy bear that’s kind of gross if it’s not made out of all new material?
Back to that two page function. Yes, I know, it’s just a simple function to display a window, but it has grown little hairs and stuff on it and nobody knows why. Well, I’ll tell you why: those are bug fixes. One of them fixes that bug that Nancy had when she tried to install the thing on a computer that didn’t have Internet Explorer. Another one fixes that bug that occurs in low memory conditions. Another one fixes that bug that occurred when the file is on a floppy disk and the user yanks out the disk in the middle. That LoadLibrary call is ugly but it makes the code work on old versions of Windows 95.
Each of these bugs took weeks of real-world usage before they were found. The programmer might have spent a couple of days reproducing the bug in the lab and fixing it. If it’s like a lot of bugs, the fix might be one line of code, or it might even be a couple of characters, but a lot of work and time went into those two characters.
When you throw away code and start from scratch, you are throwing away all that knowledge. All those collected bug fixes. Years of programming work.”
stated by Joe Goh on November 21, 2007 12:44 pm | Permalink
Ok, I wrote an app with the full CTGradient, and another with just the gradient.
And making the window fullscreen:
The full CTGradient is expensive. And the “optGrad” app could be optimized even further.
That’s not the point - “my code” is hardly 30 lines. You could write the same thing just by looking at the docs. I’m not trying to put down CTGradient and get people to use my code. I’m trying to tell people that such wastage in a shipping app is not on.
You’re right and I’ve had issues along these lines before with PHP, but it doesn’t apply to this case - CTGradient is 1300 lines of examples and demos. The gradient code taken almost straight from the documentation and optimized is tiny. It’s Apple’s code. You won’t have to “do it all again” when there’s a new version of CTGradient.
I’m talking about 1300 lines of EXAMPLES and DEMONSTRATIONS shipping with dozens - probably more - applications. The gradient code is tiny. It works because it’s from the docs. Have you read the article?
expressed by Ankur on November 21, 2007 1:03 pm | Permalink
Awh, not a fan of Whitesmiths?
http://en.wikipedia.org/wiki/Indent_style#Whitesmiths_style
posted by Chad Weider on November 21, 2007 1:11 pm | Permalink
Sends shivers down my spine
The BSD KNF style seems the most readable to me.
posted by Ankur on November 21, 2007 1:17 pm | Permalink
That’s what that is? I remember seeing it in Icon source code and I wondered where the hell they had gotten that bizarre formatting.
professed by Taybin on November 21, 2007 2:14 pm | Permalink
If you use the subversion vendor branch pattern, this isn’t a problem. It’s the only sane way to use 3rd party libraries.
professed by Taybin on November 21, 2007 2:15 pm | Permalink
Would you care to share how you got those memory usage numbers for optGrad and CTGradient? I’m running both projects and they are always within about a 0.5 mb of each other. Plus, it’s not really a fair estimate since the apps do slightly different things (CTGradient.app has controls and other code).
revealed by Matt on November 21, 2007 3:24 pm | Permalink
It’s a different optGrad.
Here.
Especially when you resize the windows, CTGradient goes ballistic. At least on my computer.
Wha…?
divulged by Ankur on November 21, 2007 4:03 pm | Permalink
If CTGradient really causes an app to use an additional 10MB of memory … yeah … that’s VERY interesting. That’s your punch line. Not the 30 lines of code.
But let’s see some more analysis on that front. 10 MB? What the hell is it doing?
published by Daniel Jalkut on November 21, 2007 4:08 pm | Permalink
Assuming you do have the resources to go over, understand, and change code other people wrote, could you also give a concise summary of what should be done licensing-wise? i.e “Giving back to the community” - do I need to post those 30 lines somewhere? As a separate file? As a method in the original file? Don’t I need to document the limitations I’ve decided upon somewhere?
stated by uv on November 21, 2007 4:08 pm | Permalink
Daniel, it’s not always that dramatic:
Depends what you do with it, and what the user does with the window.
I suppose that depends on the license the code was released under. In this case our 30 lines can be accomplished straight from the docs, so no issues. But “Giving back to the community”, as you say, is always good.
disclosed by Ankur on November 21, 2007 4:16 pm | Permalink
I’d rather spend a day on refining my UI than spending the day optimizing a drawing routine that gets called once in a blue. I prefer apps by developers who’d do the same.
As computers get faster, our libraries get higher abstractions. This is not a bad thing.
divulged by Joachim Bengtsson on November 21, 2007 5:00 pm | Permalink
Whoa! Once in a blue? This thing usually gets called at every single ‘display’.
You could go so far as to call it a good thing, but it doesn’t justify waste. CTGradient is a demo of the gradient drawing. The whole thing shouldn’t be mindlessly plonked into any and every app. It’s not as if it’s hard to write your own gradient code. Just copy and paste from the docs!
One principle of Wil Shipley’s that I do try to follow - tiny code.
expressed by Ankur on November 21, 2007 5:09 pm | Permalink
I’ve downloaded and ran the optGrad project you’ve linked above, and i’m not seeing results anywhere near what you’ve been describing.
http://myskitch.com/funkeemonk/activity_monitor-20071121-150847/
And here’s the memory usage at full-screen on my machine: http://myskitch.com/funkeemonk/activity_monitor-20071121-152425/
Only about 0.1MB difference between the two.
published by Joe Goh on November 21, 2007 5:12 pm | Permalink
“at every single ‘display’”: True, but that is probably not very often, except when resizing windows. And if it is, then yes, optimizing it like this is justified. But not until it actually becomes a problem.
“But it doesn’t justify waste”: A thousand lines of demo code in a library routine can’t be more than a kilobyte added to the final binary. I’d hardly call that ‘waste’.
“tiny code”: In your app, absolutely. In a library written by someone else? As long as it works and does a good job, I don’t really see a good reason to cut into it.
announced by Joachim Bengtsson on November 21, 2007 5:15 pm | Permalink
It’s not a ‘library’. That’s the point. The whole thing is a demo.
stated by Ankur on November 21, 2007 5:22 pm | Permalink
“Probably” is not very certain. An NSLog where you draw the gradient will found out pretty quickly.
I think the best thing people can do here is to actually read the CTGradient.m code before making comments.
I’m not going to argue whether this five minute is optimization “worth it” or not. I know it is. If you read the code you’ll also know it is. (Or better yet, if you read the documentation, you’ll realize that you don’t even need the class to draw simple gradients.)
I really didn’t expect so much discussion over something so small. An application is a guest on the user’s system. It should behave as such.
proclaimed by Ankur on November 21, 2007 5:33 pm | Permalink
Ankur, you seem to be repeatedly confusing peoples’ sentiment that your optimizations are not worth the time, with a sentiment nobody is expressing: that they’re not valuable. Mixed up in all of this is a defensive tone among respondents, probably inspired by the righteousness and dismissiveness with which you express your argument.
You keep reflecting back to and obsessing on “if you read the docs,” etc. Well, you’ve got the luxury of time and you chose to focus on gradients. You know a lot about them, and you’ve got some mathematical cleverness that allows you to sit down and, as you say it, spend “5 minutes” optimizing this situation.
Most of us would not have had this cranked out in 5 minutes. Obviously it was a lot easier for us to just link in the trusted class and have it do its thing. This is how most of us get work done. We’re not in the business of fine-tooth-combing through every single action our application makes. If we were, we’d be too busy optimizing our own array and dictionary collection classes to ship an app.
You’ve obviously got real talent but in the realm of optimization you’re expressing more ignorance than brilliance, as you repeatedly argue for measurement methodologies that make no real impact on the end user experience.
published by Daniel Jalkut on November 21, 2007 11:16 pm | Permalink
‘We’re not in the business of fine-tooth-combing’
Yes and your reputation in that regard precedes you.
voiced by Rick on November 21, 2007 11:35 pm | Permalink
‘You’ve obviously got real talent but in the realm of optimization you’re expressing more ignorance than brilliance, as you repeatedly argue for measurement methodologies that make no real impact on the end user experience.’
Wins today’s award for the most pompous pile of bile quoted on the World Wide Web. As anyone can see the ‘righteousness and dismissiveness’* of which you so high handedly speak is your own and no one else’s.
*According to dictionary.com ‘dismissiveness’ is not a word.
composed by Rick on November 21, 2007 11:41 pm | Permalink
‘Premature optimization is the root of all evil.’ - TONY Hoare
Yes Tony Hoare is well known. But it’s not exactly premature anymore when the bloody application is finished, is it? Some people really need to get a clue, read the quotes from the giants whose shoulders they’ll never be permitted to stand on, and try to understand what those giants mean and do not mean. Who knows? Fantastic things might result. Such as halfway decent code for the first time ever.
announced by Rick on November 22, 2007 12:02 am | Permalink
‘You’ve obviously got real talent but in the realm of optimization you’re expressing more ignorance than brilliance, as you repeatedly argue for measurement methodologies that make no real impact on the end user experience.’
Hilarious! Go on, tell me ‘memory is cheap’ I dare you ;o) As I see it, as the old saying goes, “look after the pennies and the pounds will look after themselves”. All optimisation is worth it, period. Whether it saves memory or space on disk or merely sharpens one’s mind it is worth it. For sure it takes time but there’s a very instructive inverse proportion there - the less one does it the longer it takes to do, whilst if one makes it part of one’s daily approach to coding the methods become second nature.
stated by diem on November 22, 2007 12:48 am | Permalink
@ Red Sweater
“Well, you’ve got the luxury of time and you chose to focus on gradients”
Ok? So you don’t like doing your homework?
determined by Sean on November 22, 2007 1:51 am | Permalink
Sean - I don’t have time to do “my homework” on everything. Do you?
Who’s going to vet and optimize darwin for us? I’m sure we could save a few bytes!
stated by Daniel Jalkut on November 22, 2007 1:53 am | Permalink
Actually, its “the love of premature optimization” that is the root of all evil…except for all that other evil whose root is the love of money.
It should be noted that all of the “bloat” you removed for CTGradient did strip out functionality. The price of using code that does more than you need is…extra code. You can’t really call that “bloat”.
If all you want to do is draw a gradient with one color and two endpoints, you don’t need to use CTGradient at all, as you have pointed out. One can copy and paste code from Apple docs.
Have you really optimized anything here? You’ve stripped down CTGradient until it effectively implements a simple gradient. We’re not even talking about CTGradient any more.
For your next blog posting, how about hacking the bloat out of OmniNetworking so that it only offers UDP connections? That would be fab.
disclosed by Captain Obvious on November 22, 2007 2:14 am | Permalink
Whoa. I think the entire article and many of the commenters missed the point of CTGradient. It’s most likely all things to all people who simply need a gradient. I’ve written gradient routines and optimized them. It wasn’t fun. But every time I needed a different gradient, I had to rewrite the routine and re-optimize. Suddenly, I have dozens of gradient drawing routines, each slightly different. Not cool.
My solutions was to move everything into a custom class and abstract everything away. My optimizations lost to convenience. I regained some sanity and I’d do it again. Then I found CTGradient and started using it. Instead of having to make changes to my own gradient class, CTGradient just seemed to do what I wanted when I wanted something different. I like that and consider it a worthwhile trade off.
If you only have one type of gradient or need speed, then you should obviously roll your own. The docs give you all the code you need for a simple gradient. However, if you need something more complex and don’t want to take the time to roll your own, CTGradient is great.
Also, until I see a reproducible table of results for an example application, I have to call “premature optimization.”
disclosed by Grayson on November 22, 2007 2:31 am | Permalink
Joe Goh - I do not believe the Joel on Software quote applies in this situation. I read that same article some time ago. Joel was talking about throwing code away without taking the time to understand it, and then rewriting it from scratch. Taking the time to understand the code and then iteratively refactoring it and/or throwing away the parts that are not needed is not “throwing away all that knowledge”.
Daniel - I would not argue the merits of this based on performance, though there may be a performance-based argument. I believe that reducing 1300 lines of code to 30 is a savings in itself, especially if you ever have to modify or debug your reused code. The benefit to the end user in this case is reduced shipping time for that update with the new feature or bug fix.
In my opinion, less code is always better than more code, but I may be influenced by my job in safety-critical software, where each untested statement is viewed as a potential bug, and we are required to write tests that achieve 100% MC/DC coverage of the code.
declared by Tim on November 22, 2007 3:27 am | Permalink
@ Daniel
“I don’t have time to do “my homework” on everything. Do you?”
If I decide to stick my neck out and make snide comments, I’d make an attempt to make sure I know what I’m talking about. But then again, you’re just a blogger who suffers from unwarranted self-importance, so I wouldn’t expect anything more out of you.
“Who’s going to vet and optimize darwin for us? I’m sure we could save a few bytes!”
Oh, so open source projects like Linux were created by just one guy plugging away at a keyboard for years and years, with no improvements (however small they may be) done by anyone else. Uh huh.
Do us a favor, think of this as being a thanksgiving dinner. The adults are busy discussing things at the dinner table. Take your slice of turkey, and go back to the childr- Oops, I mean, “indy” developer table.
Anyway, I’m still stunned as to how someone would be COMPLAINING about having code being reduced to about 15% of its’ original line count.
All your original comments go back to you just being TOO LAZY to read ANY SORT OF DOCUMENTATION and then somehow think that THIS IS THE WAY YOU’RE SUPPOSED TO DO THINGS!
revealed by Sean on November 22, 2007 4:42 am | Permalink
Well at least something good came out of this utterly idiotic comment thread. I learnt my preferred C indent style has a name. Whitesmiths FTW.
spoken by Jonathan Wight on November 22, 2007 5:05 am | Permalink
I should clarify my above statement:
I’m stunned that people are complaining about having code being reduced to about 10% of what it originally was, before AK even started to do any real heavy lifting.
declared by Sean on November 22, 2007 5:08 am | Permalink
@Sean: When you’re a one man shop developing, testing, shipping, marketing and supporting several applications then optimising some 3rd party code you are using to do gradients, that you decided to use for the sole reason that you can just drop it in and use it just isn’t worth the time. Now that someone else who had the time has done it then people might start using the new code. Unfortunately time is limited and so you have to prioritise if you want to get anything done.
determined by Martin Pilkington on November 22, 2007 5:16 am | Permalink
Rick -
C.A.R. Hoare is correct as well. Same dude.
announced by Ian Baird on November 22, 2007 6:04 am | Permalink
Observation and accuracy may be considered basic skills required of a programmer. The following simple test may help highlight issues relating to file quality where small and simple is regarded best. There is no time limit to the test that follows where the task is to read and manually count the occurrence of the character f within the following sentence…
‘Finished files are the formal result of years of scientific formulation of research.’
Take a few minutes rest and then repeat the exercise. Compare the recount with the previous result. Ask a colleague to perform the same. The worth of the exercise may well rely on repeating the exercise a number of times whilst being observant, analytical and recognising potential sources of error.
Those not capable of arriving at a consistently satisfactory stable count may of course drop this text into an appropriate file editor and perform a search but then the real purpose will be lost. The results of the test may just provide an insight into why file bloat is related to quality, stability and security.
What did you notice as the exercise was repeated?
Where are sources of error on such a trivial task?
Are there any lessons to be learned from the observations?
If there are issues encountered in a small task such as this what are the implications for anything more complex?
uttered by Tony on November 22, 2007 6:06 am | Permalink
Bravo! Like a beautiful piece of music Ankur. Well thought out, written and expressed. I’m a manufacturing engineer and can appreciate a finely crafted process. Clean engineering is a thing to behold and speaks volumes for the intellect that creates it.
And yes, it does have a positive effect on the world it operates in. It’s something wonderful.
SLmanDR
published by Steve on November 22, 2007 6:19 am | Permalink
@sean
no one is complaining about the reduction in line count. people just want to see some numbers about any improvements in speed, binary size, and memory usage. it’s well known that programmers aren’t good at picking where to optimize without the use of a profiler.
written by Taybin on November 22, 2007 6:25 am | Permalink
Looks like a follow up from Rick:
http://www.rixstep.com/2/1/20071121,00.shtml
I don’t think I’d be understating it to say, this guy is a 911 conspiracy theory-level fucking nutter.
posted by Jonathan Wight on November 22, 2007 6:29 am | Permalink
Is it so difficult to get Ankur’s point?
Does everybody agree that lean, bloatfree code is
-easier to read and to understand
–therefore easier to maintain
–therefore easier to keep free of hidden bugs
-wasting less disk space
-performing better
?
OK.
The big question is: why did Ankur have to waste his time optimizing CTGradient? Why didn’t the original author do in the first place what Ankur had to now? That code didn’t just come into existence on its own, it was created by somebody, and this person should have felt responsible for producing quality code.
Take Ankur’s Trimmit tool. You can de-bloat all sorts of applications in a breeze. Still, why should you have to waste valuable time doing that, when you already paid the software author for this job? Because this should be an important part of the software authors job! Hallo!?
Imagine you check into a hotel and in the room they give you, you find out that YOU have to clean the on-suite bathroom and change the last person’s dirty bedcloth? I bet you would be off to the manager in no time.
Bloated code is no different than a dirty toilet in a hotel room.
announced by g.org on November 22, 2007 6:46 am | Permalink
“Bloated code is no different than a dirty toilet in a hotel room.”
posted by Jonathan Wight on November 22, 2007 7:05 am | Permalink
“The big question is: why did Ankur have to waste his time optimizing CTGradient? Why didn’t the original author do in the first place what Ankur had to now?”
It seems to be that a lot of Ankur’s “optimisations” are really specialisations. The code suddenly isn’t particularly useful to everyone any more. It isn’t really fair (and is in fact rather idiotic) to compare the two codebases any more.
Accusing anyone of using Chad’s original code of not caring about code bloat is frankly a little presumptuous if not downright rude. The line count (which may or may not equal bloat) has gone down, but so has the general usefulness of the code.
announced by Jonathan Wight on November 22, 2007 7:37 am | Permalink
Whitesmith! Coming from Pascal, my C style was kind of formless until I learned the value of consistency from McConnell’s Code Complete, from which I took the Whitesmith brace indentation style.
McConnell didn’t so much recommend it as stress its advantages, and I agreed. My immediate superior and I disagree on it quite often, but that’s the purpose of code formatters. Use ’em, heretics.
voiced by Allan on November 22, 2007 7:45 am | Permalink
As Ankur said, there’s nothing wrong with Chad’s original code for demonstration purposes. Frankly, if someone puts the whole thing in an app instead of the one from the docs which is way smaller, then there’s every reason to accuse them of bloat. Intentional or not is another matter.
(Correct me if I’m wrong, but) I think that’s the point. Code should be specialized to fit the implementation, unless it’s a shared framework or library.
stated by Harley on November 22, 2007 7:46 am | Permalink
Precisely. If I have a gradient in Trimmit, it should be for Trimmit. Code that’s useful for everyone is fine for everyone. For a single app, it should be for that single app only.
spoken by Ankur on November 22, 2007 7:51 am | Permalink
You know, I just realized I’ve been using SQLite all this time but I probably only use half the features. Time to start choppin’ code! And I bet there’s loads of system frameworks I don’t need..
Seriously, though, couldn’t you have just said, “I found out that CTGradient does a lot of stuff I don’t need, so I copied out the 30 lines of code I was using from it.” And if you still need to pretend you’re educating the masses: “If you’re using CTGradient, you should consider whether you’d be better off using the CGShading functions instead.”
Now THERE’S a useful reduction in bloat.
stated by Dave on November 22, 2007 8:11 am | Permalink
Read the article next time?
recorded by Harley on November 22, 2007 8:14 am | Permalink
“Precisely. If I have a gradient in Trimmit, it should be for Trimmit. Code that’s useful for everyone is fine for everyone. For a single app, it should be for that single app only.”
Oh Ankur!
You don’t really understand the term “code re-use” at all. Chad’s code is useful for everyone - as is. If developers consistently re-wrote 3rd party code there would be very few apps actually shipping.
Instead of telling people to re-write their 3rd party code you should be telling them to profile their code, identify any bottlenecks and start optimising there.
reported by Jonathan Wight on November 22, 2007 8:24 am | Permalink
This discussion is getting off the topic. It appears people are not reading the article before expressing their opinions, hence the high level of lame and irrelevant comments.
stated by Ankur on November 22, 2007 8:30 am | Permalink
“Read the article next time?”
Did. It wasn’t very good.
recorded by Dave on November 22, 2007 8:37 am | Permalink
Oh Jonathan!
You don’t really understand the term “programming” at all.
The reusable code is in CGShading.
Is it just me or are all the flamers constantly changing their arguments? First it’s “show me the numbers!”, then “I can’t be bothered optimizing!” then “It’s a library!”.
It’s a fantastic article, Ankur. (Unlike other, I actually read it)
uttered by Harley on November 22, 2007 8:39 am | Permalink
What is this “programming” of which you speak?
determined by Jonathan Wight on November 22, 2007 8:40 am | Permalink
I suppose it should be mentioned that you can trim the code down to 0 lines if you choose to use NSGradient in 10.5
declared by Chad Weider on November 22, 2007 8:56 am | Permalink
Eh - semi-sarcastic comment about Weider’s 0-line solution retracted. Thanks for the “edit” feature, Ankur.
stated by Daniel Jalkut on November 22, 2007 10:00 am | Permalink
“Take Ankur’s Trimmit tool. You can de-bloat all sorts of applications in a breeze. Still, why should you have to waste valuable time doing that, when you already paid the software author for this job? “
Interesting example.
Given the source code, I’ll bet I can find some cases which don’t apply to how I would use the application, or the applications on which I would use it. If I strip that code out, can I write a blog about how I’ve optimized Ankur’s shoddy work and reduced his massive code bloat?
determined by Wade on November 22, 2007 4:14 pm | Permalink
‘Is it just me or are all the flamers constantly changing their arguments? First it’s “show me the numbers!”, then “I can’t be bothered optimizing!” then “It’s a library!”’
Oh, then eventually they just accuse you of never releasing a mac application. Sorry to spoil it.
composed by Sean on November 22, 2007 4:33 pm | Permalink
To point out the absurdity: Why aren’t people so hyped up over how NSGradient tries to provide both linear and radial gradients? How dare they?! The nerve to introduce such wasteful code bloat! We should narrow it down to the simplest possible thing that solves only one problem. And then disparage anyone for noting that it tries to solve multiple problems in a convenient interface.
CTGradient is a drop-in class. Sure, stuff can be stripped away to provide the bare essentials. I have an NSData category that provides base64 encoding and decoding. What if I only need one for a particular application? Am I somehow beholden by some imaginary (seriously, prove it) problem to strip away one when it isn’t currently used in an application? If you said “yes”, you can volunteer to separate my classes out into dozens of files and coordinate what I need and when.
This isn’t to say that the article is necessarily wrong. I agree that programmers should use the simplest possible thing that will work. However, there are tradeoffs to be made. When you accept libraries, drop-in convenience classes, and, hell, any code that you didn’t write, you have to accept that the code probably wasn’t written for your specific need alone. Even if you write reusable classes, they sometimes need to be tweaked to work in multiple locations. Welcome to abstraction. It makes code reusable.
revealed by Grayson on November 22, 2007 5:06 pm | Permalink
OMG Wade.
Did you not read? I’ll break it down for you:
And keep in mind this is an example. If they’re so careless about this - and defending their position vigorously - then I shudder to think of how the rest of their applications are coded.
posted by Harley on November 22, 2007 5:53 pm | Permalink
“Oh, then eventually they just accuse you of never releasing a mac application. Sorry to spoil it.”
Chip say hello to shoulder.
reasonded by Jonathan Wight on November 22, 2007 5:56 pm | Permalink
Grayson, if this had been a framework or a shared library, I’d have agreed with you 100%. But what I see here is that instead of copying and pasting tiny code, developers are copying and pasting huge code, and don’t care about the performance implications. It wouldn’t take more time to get the CGShading in. I’ve done it before. Ankur’s just done it with his app. The only explanation I can think of - and it’s pretty scary - is that the offending developers care more about their own convenience, popularity and image than their users.
uttered by Harley on November 22, 2007 6:00 pm | Permalink
“developers are copying and pasting huge code, and don’t care about the performance implications”
Of which there may or may not be any.
You are getting your knickers in a twist over nothing. Competent developers are more than capable of profiling their apps, and analyse the performance implications themselves.
uttered by Jonathan Wight on November 22, 2007 6:08 pm | Permalink
Of course there will be. Have you looked at the code?
And besides, even if there aren’t, the fact that they don’t care speaks volumes.
Competent developers would not raise such a raucous debate about it. And if they care more about their own convenience than their users’, their level of competency should be in question.
published by Harley on November 22, 2007 6:15 pm | Permalink
@harley,
Yes I’ve looked at the code. Most of Ankur’s optimisations were specialisations for his needs. Not all of those epecialisations will be relevent or needed for other apps. Some apps might need to render parts of a view for example, so the code that Ankur removes will actually be needed.
There seem to be two camps: A) people stating that ankur’s code is the best thing since sliced bread and that 30 lines is obviously superior to 1300. B) people stating that they need more information (e.g. profiles) and aren’t willing to assume that the optimsations are necessary
I’m in the B camp if you couldn’t tell.
reasonded by Jonathan Wight on November 22, 2007 6:26 pm | Permalink
You’re right of course. But I would say that last bit - that “you should’ve written your own you lazy dolt” - was more relevant. It’s like if I wrote a wrapper to NSWindow that flips it, rotates it, zooms it, etc. taking 2 mb and you use it in your apps because you need the
[window titlebarHeight]method. Can you say waste?Ya.
Ok.
I think camp B would do well to read the comment by diem.
But I still don’t understand the reason for this fuss.
disclosed by Harley on November 22, 2007 6:34 pm | Permalink
The “All optimisation is worth it, period. ” comment?
Optimisation comes with a cost: developer time.
Like everything else it has to be weighed.
And the reason it caused such a fuss… Who knows? The internet is full of strangeness.
posted by Jonathan Wight on November 22, 2007 6:41 pm | Permalink
Sean, as in Sean Collins?
http://www.seosoft.info/seolog/2007/11/22/an-introduction-to-sean-collins/
proclaimed by Kenneth on November 22, 2007 7:24 pm | Permalink
Oh noes!! But wait! CGShading has axial and radial too!
Indeed. There’s really no reason to get so uptight about it. As I said before, fanboy and co. were initially demanding “numbers”, then saying this is a shared library (yeah, right), then the (only valid) argument about taking time. But if that’s the case, why the flamin’ flaming? Not to mention the nonsense on Mr. Tweetie.
Ankur: You should think about installing lemur CATTA. Keep out the idiots.
reported by Harley on November 22, 2007 7:26 pm | Permalink
Actually the main person getting uptight over this is Rick. Notice the stream of postings to his site? Seems to be indulging in some serious mud slinging and name calling.
Screw loose perhaps?
recorded by Jonathan Wight on November 22, 2007 7:48 pm | Permalink
I’m sad to say I don’t disagree with him.
If his client list (and software) is anything to go by, he seems entitled to it. He may have just gained another customer (me).
published by Anonymous Coward on November 22, 2007 7:54 pm | Permalink
@anonymous: All Rick ever seems to do is complain about how stupid users are because they put up with developers not being as “perfect” as he likes to think he is. He fails to realise that 99% of users don’t care about most of the stuff he complains about. And to be completely honest I wouldn’t trust someone who steals icons from other 3rd party developers.
professed by Martin Pilkington on November 22, 2007 9:55 pm | Permalink
Are people being obtuse? (deliberately or otherwise)
I have little programming experience beyond a HP47C calculator, but programming isn’t the point here.
It’s about waste; it’s about care and pride in one’s work; it’s about providing customers with the best that the vendor can provide. These ideas or ideals are part and parcel of all social intercourse.
“Couldn’t be bothered” lazy dickheads abound in every field of human activity. “Not worth the effort”? To whom?
Customers’ needs come first.
Not your disk space/memory being wasted? Then who cares, hey? Fuck ‘em!
recorded by pol on November 22, 2007 10:45 pm | Permalink
I appreciate the detail the article goes into on how to optimize code for a specific need.
I also appreciate “code aesthetic”, and even “.app aesthetic” to an extent, but I definitely rate the “real programmers ship” mantra over aesthetics.
I do not appreciate name calling and don’t care about name dropping. Real programmers ship, this is a meritocracy.
MarsEdit’s .app contains extraneous files? File a bug, he’ll fix it. You aren’t insinuating that it’s intentional, I’m sure.
What this article fails to point out is that programmer efficiency is more important than disk space or global optimization (and incidentally the binary output of 1300 of lines of code is guaranteed to be statistical noise).
“Time to market” is always a concern, you want working code as soon as you feel a concept works, whether you’re coding for yourself or for your boss.
Since programmer efficiency isn’t mentioned, there’s clearly no way measuring before acting would be mentioned, or in other words profiling before optimizing. This is dangerous and misleading, and should be rectified.
My gut feeling is that most of the time the gradient code wouldn’t have a significant impact on application performance, I might be wrong but there are no numbers that confirm or deny it.
Without the numbers backing the case, this article is just an academic exercise, with an unjustified negative connotation on the number of lines of code.
uttered by Duncan Wilcox on November 22, 2007 11:51 pm | Permalink
@Diem: “All optimisation is worth it, period.”
I’m sure you could write far more “optimized” code in x86/PPC assembly language than Objective-C. I would not consider that “worth it.”
stated by Brian Christensen on November 23, 2007 5:05 am | Permalink
The linearEvaluation function proposed by the author appears to be at least four or five times faster when considering computation. However, it is likely to perform even better than this due to the decreased footprint, lack of temporary variables, and the monochromatic output.
stated by J. on November 23, 2007 7:40 am | Permalink
J: That might be so. However, is the slower performance making window resizing annoying, or even noticeably degraded, even on an old machine? If in your app this is true, then yes, optimization might be a good idea. If not, maybe my customers might prefer if I spent that time replying to a dozen support mails.
divulged by Joachim Bengtsson on November 23, 2007 8:21 am | Permalink
Joachim,
Forget optimization. If you used the CG functions instead of using the CTGradient class, you would have the better performance and the time to answer your support emails.
Those who are viciously attacking this article are perhaps spending too much time in OO-land and not noticing the C.
spoken by J. on November 23, 2007 8:30 am | Permalink
@POL
You’re lack of experience of production programming is evident in your attitude. I am not being condescending. In fact, that is the very same attitude that many early developers approached programming with. At some point however, one has to consider the trade off. For example, are we positive that Daniel Jalkut is only drawing monochromatic linear gradients (in a single direction, no less) with his code? Having a single class that generalizes behavior certainly adds memory and computation time if you analyze a single use. However, consider the situation where I have to draw 10 different gradients. Now I have 10 instances of very specialized code. And I have to keep them separate, have a different API for them, etc. Then in my other project where I also use that code, I have 10 different instances of the code. I have to maintain all of these different instances of what amounts to very similar, but specialized code. That time isn’t free. For an indie developer who is doing development, testing, support, and sales, that time isn’t just free, it’s lost revenue. On the flip side of that, what is the gain for the customer? The customer gets an extra couple kilobytes of memory, and maybe instead of taking 100 ms to draw the gradient, it takes 20-25ms. Is that relevant to the user? Even if you do it a billion times, will the user notice? Is it stopping the user from completing their task? Now, take that time that the customer has gained, by the optimization, and compare that to the time the customer would lose because the developer spent an extra 6 months optimizing (for a relatively small project) and specializing the code. Is it worth it for the developer to sacrifice the money and income to do that? Because he does it, does he get to charge more for the product. Do the users see any real value in that optimization, and will they pay more in order to support the indie developer for their time?
It’s a trade off. Specialized code is reduces bloat for a single use scenario, but ends up adding bloat when you use the code in multiple places.
@J.
But one might argue that by using CT, instead of CG, Joachim didn’t have to write any of the underlying gradient drawing code, thus saving much more time.
stated by Vinay Venkatesh on November 23, 2007 8:32 am | Permalink
@pol: If there is a huge difference in memory usage then by all means optimise your code. But you have to think about what is worthwhile. 10MB off 40MB of memory usage is a huge gain, but 1MB isn’t quite as big a gain, especially if you have to do a lot to get that gain. It’s all about weighing up whether the effort expended is worth the performance gain.
And editing 3rd party code isn’t a bad thing, if you know there is something seriously wrong with it that is causing problems in your application. If Ankur profiled the app and found CTGradient to be the problem then by all means optimise it, if the effort expended is worth the gain. I’ve been optimising some rather poor code I wrote a while ago to do syntax highlighting, but found an issue in AGRegex which I was using to parse the code. Changing 1 line managed to help me taken nearly a second of some large test cases I had.
Of course, this is all about processing time and memory usage. You also mentioned disk space. Now developers are also huge application users and often use many applications (so it is our disk space being “wasted”). Many of the disk space optimisations suggested saved 1-2MB per application (obviously a bit more for localised applications). For me that would be around 100-200MB saved in my apps folder. The thing is, 100-200MB is very little by todays standards. That is 0.12% of my HD space (I have a 160GB HD). Cleaning out your downloads folder can often save more space. Arguments like this would have been valid maybe 5, definitely 10 years ago, but the fact is that disk space is the one resource on a computer that can be seen as a commodity.
1 user in a 1000 may actually care about an application taking up an extra MB of space and even less will actually bother to complain about it. Why spend time sort out something for the minority when you can be adding new features, shipping applications, supporting other users, promoting your application or making your application faster in ways that actually affect your users?
voiced by Martin Pilkington on November 23, 2007 8:39 am | Permalink
One might refute that statement with a URL to the appropriate page in the documentation.
mentioned by J. on November 23, 2007 8:42 am | Permalink
happy thanksgiving everyone.
revealed by Taybin on November 23, 2007 8:44 am | Permalink
Martin: I just had a look at the VT page for this “Trimmit” app. You should read the comments.
uttered by J. on November 23, 2007 8:46 am | Permalink
@J: Yes, 1GB of Adobe design premium, which is several apps, with lots of legacy. And 50MB from Safari by stripping away a binary and removing the many localisations. I pointed out that localisations will increase the size of the application and are the main cause for an increase in size. As for the Adobe applications I couldn’t say without looking at what was removed as to what is causing the increased file size.
stated by Martin Pilkington on November 23, 2007 9:19 am | Permalink
Doesn’t matter. You are a guest. Just because their house has a huge kitchen doesn’t mean you can pig out.
written by J. on November 23, 2007 9:25 am | Permalink
Wow. This thread is turning into high comedy
disclosed by Jonathan Wight on November 23, 2007 9:39 am | Permalink
@J: Pigging out implies taking a lot of space. This is more akin to eating an apple in a supermarket. If you care so much about that bit of space then use applications to get rid of the excess. We’ll ship our applications as universal binaries with localisations so that a user can download one application and know it will work. The alternative is to offer slimmed down versions for PPC, PPC 64, x86, x86-64 in every language. Now which sounds more Mac like?
recorded by Martin Pilkington on November 23, 2007 9:43 am | Permalink
Indeed. Spare a though for the unfortunate author.
You’re sounding pretty Mac-like: arguing, but don’t quite “no” what about.
(Sorry, had to make a joke to keep JW happy).
You think the only way to slim applications is by removing the architectures and localizations? Go check out Rick’s site. The reviews. Mac apps are in general poorly packaged. .DS_Store’s, pbdevelopment.plist, classes.nib, info.nib, data.dependency, uncompressed tiffs, InfoPlist.strings, and debug symbols galore. And the unfortunate thing is, the developers don’t care / don’t know. I’m presuming you fall in the “don’t know” category.
disclosed by J. on November 23, 2007 9:50 am | Permalink
“You’re sounding pretty Mac-like: arguing, but don’t quite “no” what about.”
Thanks for pointing that out, corrected.
“You think the only way to slim applications is by removing the architectures and localizations? Go check out Rick’s site. The reviews. Mac apps are in general poorly packaged. .DS_Store’s, pbdevelopment.plist, classes.nib, info.nib, data.dependency, uncompressed tiffs, InfoPlist.strings, and debug symbols galore. And the unfortunate thing is, the developers don’t care / don’t know. I’m presuming you fall in the “don’t know” category.”
You presume wrong, I’m in the don’t care group. The only way to significantly slim applications is by removing localisations, given that these make up the bulk of an app bundle. Rick is one of the few developers who I know of who actually care about this and is about the only person I know who considers disk space precious. If we were talking 100s of MB in each application then sure, but it raises my eyebrows when I see Rick calling people out for “not giving a shit” about their customers over 752KB of disk space. The year is 2007 not 1987.
determined by Martin Pilkington on November 23, 2007 10:05 am | Permalink
I would actually advise the opposite.
Code from Apple is quite different from other 3rd party code. There’s a different trust level there. Advising folks to drop in random classes you can find off the ‘net, written by Joe Blow you’ve never heard of, seems like a colossally bad idea to me.
It’s all about trust though. Maybe I’m just a more risk-adverse person. Perhaps I’ve got a mild case of NIH syndrome. Or maybe I’m overreacting because CGShading is so dead simple to use, and I’ve really never have seen the need for CTGradient in the first place. Grains of salt, take it with them, etc.
announced by Colin Barrett on November 23, 2007 10:08 am | Permalink
The only way to significantly slim is by removing localizations? Who told you that? If the localizations take up the most space, your packaging is probably wrong anyway. Rule no 1: Keep as much in your resources folder as possible.
You don’t care about shipping junk, you don’t care about your users. How do we know you don’t take that attitude with your code?
Why don’t you explain to your users with dial-up? Or those using G3 iBooks? Even 100kb is a lot of dial-up, and some apps are reduced to less than half their size after removing the cruft alone - not counting the localizations and architectures.
But I’m not going to argue now. If you don’t care, that’s up to you.
announced by Coward on November 23, 2007 10:22 am | Permalink
Well said, Colin! CGShading FTW!
Ok, back to my cave again…
expressed by Harley on November 23, 2007 10:24 am | Permalink
“The only way to significantly slim is by removing localizations? Who told you that? If the localizations take up the most space, your packaging is probably wrong anyway. Rule no 1: Keep as much in your resources folder as possible.”
By significantly I mean by more than just a MB or two. Generally large applications are that way due to lots of localised resources and duplicated NIBs.
“You don’t care about shipping junk, you don’t care about your users. How do we know you don’t take that attitude with your code?”
I do care about shipping junk. It’s just 99.9% of users would class shipping junk as being something that looks bad, doesn’t work, is slow etc. That is because most users spend time using the applications rather than looking at the file size.
“Why don’t you explain to your users with dial-up? Or those using G3 iBooks? Even 100kb is a lot of dial-up, and some apps are reduced to less than half their size after removing the cruft alone - not counting the localizations and architectures.”
OK then…
Hello my dial-up users. The reason I don’t use an application like Trimmit is simple. My untouched version of my more musical application is 4.1MB, which compresses to on the disk image to 1.8MB for you to download. After testing Trimmit I managed to get the application down to 2.8MB, which is an improvement. But I doubt any of you, who are most likely dealing with large audio files, are going to begrudge me 1.3MB of your disk space, which is plentiful. When compressing the disk image containing the 2.8MB “trimmed” version it compressed to 2.2MB, so while I may be giving you a little bit less disk space I am reducing your download size by 400KB, and being dial up users, 100s of KBs are now significant.
reported by Martin Pilkington on November 23, 2007 10:38 am | Permalink
That deserves to be framed.
1.3 MB less, but compressed to 400k more?
professed by J. on November 23, 2007 10:47 am | Permalink
“That deserves to be framed.
1.3 MB less, but compressed to 400k more?”
Indeed, I thought it was weird and actually had to redo the test to make sure it was right. I then tried MarsEdit and got it down from 7.7MB to 6.6MB but on a fresh disk image it went from 3.9MB compressed to 4.2MB compressed (which is quite impressive when you consider the regular MarsEdit 2.0.4 disk image is 4.1MB).
Only reason I can think of is that Disk Utility is better at compressing the uncompressed image files than the ones that Trimmit compresses.
determined by Martin Pilkington on November 23, 2007 10:57 am | Permalink
I doubt it. Last I checked, Trimmit mostly removes files and only tiffs are compressed. Most likely you’re doing something weird.
divulged by Harley on November 23, 2007 11:05 am | Permalink
You’re being presumptuous. Not every app is a 100 KB freeware wonder with just a simple set of menus and a few strings. Text for asian and other multibyte localizations can be huge. Even for non-Asian languages, graphics are often localized, as are online manuals and/or quick start guides, which are often bundled.
spoken by Zhak on November 23, 2007 12:57 pm | Permalink
You know, Rix’s style of defaulting to bitter ad hominem is not going to win Rix or anyone else any friends. It reminds me of the tactics used by the violent followers of the GOP cult in the USA, except that sometimes Rix is technically correct. Can we stick to facts please?
written by Zhak on November 23, 2007 1:01 pm | Permalink
Coward wrote: “If the localizations take up the most space, your packaging is probably wrong anyway. Rule no 1: Keep as much in your resources folder as possible.”
Localizations often include localized help files, which can be significant in size if there are lots of images. And contra your point, they do go in the Resources folder.
mentioned by Jon Hendry on November 23, 2007 6:02 pm | Permalink
So how much of the ‘bloat’ in CTGradient is removed if you use dead code stripping?
IMHO, removing ‘bloat’ by removing (or commenting out, or defining out) whole methods you don’t use hardly merits comment and can barely be described as an optimization. Any fool can comment out methods until the code no longer compiles in order to find the minimal subset of a class.
Finding and removing excess code in the code path you need, and replacing inefficient algorithms with efficient ones? Yeah, that’s optimization worth the name. Getting rid of convenience methods that you don’t need? Not so much.
mentioned by Jon Hendry on November 23, 2007 6:53 pm | Permalink
Jon, you know there are two headings. “Clear the junk” to start with but there’s “Optimize it” as well.
published by Harley on November 23, 2007 7:01 pm | Permalink
“I doubt it. Last I checked, Trimmit mostly removes files and only tiffs are compressed. Most likely you’re doing something weird.”
Or maybe the fact that the files Trimmit is removing aren’t very big in relation to the image files and so those files being removed doesn’t affect the compression as much. Bare in mind I wasn’t removing anything that would be stupid to remove from a shipping application like other architectures and localisations.
posted by Martin Pilkington on November 23, 2007 11:44 pm | Permalink
To Martin Pilkington
Hi, I’m a mac user living in the third world and using an old B&W G3 with dial up, and because of your attitude I’ll never use any of your applications, I don’t have resources to waste.
One thing I love about open source software is that they’re always really small downloads, like 200Kb for a program to do CD and DVD burning in Ubuntu.
composed by Third World User on November 24, 2007 12:28 am | Permalink
Don’t worry, after using a Mac for ten minutes, my mind gets very bare.
professed by Disgruntled on November 24, 2007 9:38 am | Permalink
“because of your attitude I’ll never use any of your applications”
Just when I thought this blog post couldn’t stoop any further.
uttered by Jonathan Wight on November 24, 2007 11:27 am | Permalink
I believe more bytes & developer hours have been consumed by the comments thread for this post than by the author’s or Daniel’s use of CTGradient. Lose - lose.
proclaimed by Ian Baird on November 28, 2007 1:42 am | Permalink
What’s interesting is that when Wil Shipley says the same things, no one argues. It’s even on the topic of gradients! But when this guy says it, uproar!
posted by J. on November 28, 2007 1:34 pm | Permalink
@J: He doesn’t say the same things. Have you actually read that post? He increases the number of lines of code and isn’t taking code that creates a gradient and improving it. He’s taking code that uses an image and swapping it for code that uses a gradient, completely different to what happened here
determined by Martin Pilkington on November 28, 2007 8:55 pm | Permalink
That’s what he’s doing. Read what he says while doing it. Identical.
That’s why you’re still a padawan.
reasonded by J. on November 28, 2007 9:22 pm | Permalink
@J: Nobody every made the argument against optimising code. The argument was against developers being bad if they don’t remove all the code they don’t need from a 3rd party class. And yes, what he says and what he says while doing it are identical. They are not however identical to this post. Does this post take someone’s specific code that uses images and replace it with general code that doesn’t use images but does it all in code? No.
And note the difference in optimisation philosophies behind the two posts: “And having fewer files in your project is EVEN BETTER than having fewer lines of code in a single file.”
stated by Martin Pilkington on November 28, 2007 9:49 pm | Permalink
Oh, relly? I thought it was about developers “not having time” to optimize their code sufficiently.
How’s that a difference in philosophy? Anyway, CGShading directly gets it in one file rather than two for CTGradient. And Wil’s saying that having fewer lines of code is a good thing too.
Do you still not get it? Read it once more. Read what he says.
uttered by J. on November 28, 2007 10:09 pm | Permalink
I think we can boil it down to two basic facts:
So the logical choice would be to use CGShading, no?
professed by J. on November 28, 2007 10:16 pm | Permalink
“Oh, relly? I thought it was about developers “not having time” to optimize their code sufficiently.”
No, it’s about developers not having time to reduce the line count of 3rd party code that they use for the sole reason that they don’t want to have to deal with writing said code themselves.
“How’s that a difference in philosophy? Anyway, CGShading directly gets it in one file rather than two for CTGradient. And Wil’s saying that having fewer lines of code is a good thing too.”
Nobody is arguing that fewer lines of code is a bad thing, but only when doing the same thing. I could reduce OS X to a few 100 lines of code but it wouldn’t do anything. What has been done on CTGradient isn’t simple optimisation and reduction of code, but removal of functionality to make it more specialised to a single application.
“Do you still not get it? Read it once more. Read what he says.”
Yes, I have read it and understood it and as someone who writes, supports, markets and tests their own applications I understand the context under which Wil says you should optimise. For example, I recently went an optimised my syntax highlighting class in one of my applications. I managed to significantly reduce the number of lines of code, increase readability and speed it up. Did I do this because it took my fancy? No, I did it because it was seriously slowing down my application. Therefore it is was a priority. I suggest you go away and release some software and come back when you have the experience to back up your argument.
declared by Martin Pilkington on November 28, 2007 10:43 pm | Permalink
Any more of that and my head will explode. I don’t know what you’re saying about line count. Sure he does that. But look at the reduction in computation, the reduced looping, reduced memory usage.
???
I’m sorry. I didn’t realize we were dealing with a genius here.
NO. Read the code. If you understand C, you can plainly see that the code will run faster and use less memory. The only logical explanatations for your lack of understanding are a) you didn’t read the code or b) you don’t know C.
Ha! How old are you, boy?
voiced by J. on November 29, 2007 7:30 am | Permalink
I think the main complaint here is that you took a bunch of code and made it smaller mostly by making it do less. Of course you succeeded at making it smaller, but it lost functionality. You may not need that functionality now, but if you need it later, now you’re gonna have to go hammer on this class again.
Second, how much disk space does 1,200 lines of code make to the binary anyway? As a consumer with a 100+ GB hard drive and cable internet connection, I would prefer you spent the time developing features and trimming actual heap space used by your app. I couldn’t care less about the static binary size. Hopefully you’ll just use an order file, which is a lot simpler, and keeps most of this code from ever being paged in. If your not already familiar with them see ‘man ld’ and read the -order_file section for information about order files.
All in all, it’s an optimization for the customizer, and de-optimization for the developer. You’re time is really important to us customers, we want you to spend it on great features. I bet there was a better way to spend that time that would have yielded even better results.
mentioned by Jon Hess on January 3, 2008 8:22 pm | Permalink
Jon Hess, you’re only looking at the reduction in binary size but you neglect to look at the speed and memory improvements which are phenomenal.
reasonded by Harley on January 4, 2008 10:39 am | Permalink
No doubt, as a non-developer end user, this is probably gross naivety on my part but the take home message of the original article that I am getting is not that you should spend hours optimising third party code just for the sake of it, but rather it is if you actually knew what you were doing in the first place you would have realised that you shouldn’t even be using “CTGradient” for this particular job. The fact that the theoretical “you” is using it in this instance just demonstrates that they don’t know how to develop programmes properly. The real code optimisation here is to use the correct (and simplest) tool for the job and not to use the wrong one just because it is the easy thing to do.
That this has apparently passed so many people posting here by is quite breathtaking.
Or perhaps it is just me?
determined by Jonathan on January 10, 2008 6:22 am | Permalink
The author of this site is simply warning against using third-party code unilaterally without considering its fitness and optimization for one’s particular needs. It is a good reminder for all us to keep sharp and our implementations crisp.
Sure it can be over done. Two golden rules are: 1) don’t optimize and 2) don’t optimize too soon. Nonetheless…
My training as an engineer included instilling in me a respect for elegant simple design as a goal for many beneficial reasons. Those arguing about memory, disk, can CPU resources growing so quickly to obviate such concerns are forgetting that we now target iPhones and maybe iPhone Nanos soon enough. Price point drivers will always require the optimization of resources: bandwidth, memory, disk, CPU, GPU, pixel breadth and depth, whatever.
I don’t think the author was trying to be autocratic: he was just reminding us that blindly using another’s code without understanding it might cost everyone. Cost you, cost your users. And maybe that cost is sometimes acceptable and other times it’s not.
The take-away is this: just beware of the compromises and their impact–make an enlightened decision for your problem space and design case. And realize your evaluation might change over time as well.
stated by Daniel on January 19, 2009 7:24 am | Permalink
Leave a comment