CTGradient code bloat


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.

Graph of Gradient Function

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.

Lean Gradient Xcode Project

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.


Back to Top ↑

124 Comments so far

Leave a comment
  1. 1

    I’d be surprised if it actually ran noticeably faster, but it certainly improves readability.

  2. 2

    I’d be surprised if it actually ran noticeably faster

    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.)

    but it certainly improves readability.

    I think the way CTGradient is formatted, any change would improve readability. :P

  3. 3

    “I think the way CTGradient is formatted, any change would improve readability.”

    Yeah, that’s like the worst of all worlds formatting.

  4. 4

    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

  5. 5

    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.

  6. 6

    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.

    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.

  7. 7

    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.

  8. 8

    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.”

  9. 9

    Give us the numbers!
    Agree that you need to post real benchmarks.

    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.

    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.

    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.

    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.

    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.

    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

    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?

  10. 10

    I think the way CTGradient is formatted, any change would improve readability.

    Awh, not a fan of Whitesmiths?

    http://en.wikipedia.org/wiki/Indent_style#Whitesmiths_style

  11. 11

    Awh, not a fan of Whitesmiths?

    Sends shivers down my spine :D

    The BSD KNF style seems the most readable to me.

  12. 12

    Awh, not a fan of Whitesmiths?

    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.

  13. 13

    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.

    If you use the subversion vendor branch pattern, this isn’t a problem. It’s the only sane way to use 3rd party libraries.

  14. 14

    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).

  15. 15

    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.

    It’s a different optGrad.

    Here.

    Especially when you resize the windows, CTGradient goes ballistic. At least on my computer.

    CTGradient.app has controls and other code

    Wha…?

  16. 16

    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?

  17. 17

    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?

  18. 18

    Daniel, it’s not always that dramatic:

    Depends what you do with it, and what the user does with the window.

    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 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.

  19. 19

    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.

  20. 20

    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.

    Whoa! Once in a blue? This thing usually gets called at every single ‘display’.

    As computers get faster, our libraries get higher abstractions. This is not a bad thing.

    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.

  21. 21

    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.

  22. 22

    “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.

  23. 23

    A thousand lines of demo code in a library routine… I’d hardly call that ‘waste’.

    It’s not a ‘library’. That’s the point. The whole thing is a demo.

  24. 24

    “at every single ‘display’”: True, but that is probably not very often

    “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.

  25. 25

    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.

  26. 26

    ‘We’re not in the business of fine-tooth-combing’

    Yes and your reputation in that regard precedes you.

  27. 27

    ‘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.

  28. 28

    ‘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.

  29. 29

    ‘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.

  30. 30

    @ 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?

  31. 31

    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!

  32. 32

    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.

  33. 33

    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.”

  34. 34

    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.

  35. 35

    @ 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!

  36. 36

    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.

  37. 37

    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.

  38. 38

    @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.

  39. 39

    Rick -

    C.A.R. Hoare is correct as well. Same dude.

  40. 40

    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?

  41. 41

    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

  42. 42

    @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.

  43. 43

    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.

  44. 44

    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.

  45. 45

    “Bloated code is no different than a dirty toilet in a hotel room.”

  46. 46

    “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.

  47. 47

    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.

  48. 48

    Accusing anyone of using Chad’s original code of not caring about code bloat is frankly a little presumptuous if not downright rude.

    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.

    It seems to be that a lot of Ankur’s “optimisations” are really specialisations.

    (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.

  49. 49

    The code suddenly isn’t particularly useful to everyone any more.

    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.

  50. 50

    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.

  51. 51

    Seriously, though, couldn’t you have just said, “If you’re using CTGradient, you should consider whether you’d be better off using the CGShading functions instead.”

    Read the article next time?

    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.

  52. 52

    “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.

  53. 53

    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.

  54. 54

    “Read the article next time?”

    Did. It wasn’t very good.

  55. 55

    “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.

    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)

  56. 56

    What is this “programming” of which you speak?

  57. 57

    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

  58. 58

    Eh - semi-sarcastic comment about Weider’s 0-line solution retracted. Thanks for the “edit” feature, Ankur.

  59. 59

    “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?

  60. 60

    ‘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.

  61. 61

    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.

  62. 62

    OMG Wade.

    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?

    Did you not read? I’ll break it down for you:

    • CTGradient is cool for testing gradients.
    • The actual gradient code for a single type of gradient is tiny. Simple.
    • The documentation provides the tiny code you need to draw gradients.
    • Developers choose to use the whole of CTGradient instead of using the tiny gradient code

    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.

  63. 63

    “Oh, then eventually they just accuse you of never releasing a mac application. Sorry to spoil it.”

    Chip say hello to shoulder.

  64. 64

    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.

  65. 65

    “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.

  66. 66

    Of which there may or may not be any.

    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.

    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.

    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.

  67. 67

    @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.

  68. 68

    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.

    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?

    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.

    Ya.

    B) people stating that they need more information (e.g. profiles) and aren’t willing to assume that the optimsations are necessary

    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.

  69. 69

    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.

  70. 70

    Sean, as in Sean Collins?

    http://www.seosoft.info/seolog/2007/11/22/an-introduction-to-sean-collins/

  71. 71

    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?!

    Oh noes!! But wait! CGShading has axial and radial too!

    And the reason it caused such a fuss… Who knows? The internet is full of strangeness.

    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.

  72. 72

    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?

  73. 73

    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).

  74. 74

    @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.

  75. 75

    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!

  76. 76

    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.

  77. 77

    @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.”

  78. 78

    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.

    1. The linearEvaluation function is called 4097 times each time the gradient is displayed.
    2. Resizing a window by 10 mm, the gradient is displayed 50 times.
    3. Resizing a window by 10 mm, linearEvaluation is called 204850 times.

    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.

  79. 79

    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.

  80. 80

    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.

  81. 81

    @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.

  82. 82

    @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?

  83. 83

    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.

    One might refute that statement with a URL to the appropriate page in the documentation.

  84. 84

    happy thanksgiving everyone. ;)

  85. 85

    Martin: I just had a look at the VT page for this “Trimmit” app. You should read the comments.

  86. 86

    @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.

  87. 87

    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.

    Doesn’t matter. You are a guest. Just because their house has a huge kitchen doesn’t mean you can pig out.

  88. 88

    Wow. This thread is turning into high comedy

  89. 89

    @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?

  90. 90

    Wow. This thread is turning into high comedy

    Indeed. Spare a though for the unfortunate author.

    We’ll ship our applications as universal binaries with localisations so that a user can download one application and no 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?

    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.

  91. 91

    “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.

  92. 92

    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.

    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.

  93. 93

    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.

    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.

    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.

    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.

  94. 94

    Well said, Colin! CGShading FTW!

    Ok, back to my cave again…

  95. 95

    “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.

  96. 96

    That deserves to be framed.

    1.3 MB less, but compressed to 400k more?

  97. 97

    “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.

  98. 98

    Only reason I can think of is that Disk Utility is better at compressing the uncompressed image files than the ones that Trimmit compresses.

    I doubt it. Last I checked, Trimmit mostly removes files and only tiffs are compressed. Most likely you’re doing something weird.

  99. 99

    If the localizations take up the most space, your packaging is probably wrong anyway.

    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.

  100. 100

    I doubt it. Last I checked, Trimmit mostly removes files and only tiffs are compressed. Most likely you’re doing something weird.

    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?

  101. 101

    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.

  102. 102

    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.

  103. 103

    Jon, you know there are two headings. “Clear the junk” to start with but there’s “Optimize it” as well.

  104. 104

    “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.

  105. 105

    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.

  106. 106

    Bare in mind I wasn’t removing anything that would be stupid to remove from a shipping application

    Don’t worry, after using a Mac for ten minutes, my mind gets very bare.

  107. 107

    “because of your attitude I’ll never use any of your applications”

    Just when I thought this blog post couldn’t stoop any further.

  108. 108

    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.

  109. 109

    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!

  110. 110

    @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

  111. 111

    That’s what he’s doing. Read what he says while doing it. Identical.

    That’s why you’re still a padawan.

  112. 112

    @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.”

  113. 113

    The argument was against developers being bad if they don’t remove all the code they don’t need from a 3rd party class.

    Oh, relly? I thought it was about developers “not having time” to optimize their code sufficiently.

    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.”

    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.

    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.

    Do you still not get it? Read it once more. Read what he says.

  114. 114

    I think we can boil it down to two basic facts:

    • CTGradient and CGShading both take a similar amount of time and “effort” to implement.
    • Using CGShading directly has great performance benefits over CTGradient.

    So the logical choice would be to use CGShading, no?

  115. 115

    “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.

  116. 116

    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.

    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.

    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

    I’m sorry. I didn’t realize we were dealing with a genius here.

    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.

    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.

    I suggest you go away and release some software and come back when you have the experience to back up your argument.

    Ha! How old are you, boy?

  117. 117

    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.

  118. 118

    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.

  119. 119

    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?

  120. 120

    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.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Comments may be edited for formatting.