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 commentPages: « 1 2 3 [4] Show All
Doesn’t matter. You are a guest. Just because their house has a huge kitchen doesn’t mean you can pig out.
mentioned by J. on November 23, 2007 9:25 am | Permalink
Wow. This thread is turning into high comedy
professed 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?
written 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.
recorded 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.
stated 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.
posted 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.
stated 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.
composed 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?
posted 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.
mentioned 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.
voiced 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.
disclosed 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?
stated 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.
written 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.
disclosed 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.
voiced 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.
announced 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.
reported 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.
declared 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.
recorded 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.
mentioned 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!
mentioned 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
reasonded 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.”
disclosed 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.
stated 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?
determined 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.
recorded 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?
divulged 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.
recorded 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?
professed 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.
determined by Daniel on January 19, 2009 7:24 am | Permalink
Pages: « 1 2 3 [4] Show All
Leave a comment