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.
123 Comments so far
Leave a commentPages: « 1 2 [3] 4 » Show All
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.
determined by Ankur on November 22, 2007 8:30 am | Permalink
“Read the article next time?”
Did. It wasn’t very good.
disclosed by Dave on November 22, 2007 8:37 am | Permalink
Oh Jonathan!
You don’t really understand the term “programming” at all.
The reusable code is in CGShading.
Is it just me or are all the flamers constantly changing their arguments? First it’s “show me the numbers!”, then “I can’t be bothered optimizing!” then “It’s a library!”.
It’s a fantastic article, Ankur. (Unlike other, I actually read it)
announced by Harley on November 22, 2007 8:39 am | Permalink
What is this “programming” of which you speak?
reasonded by Jonathan Wight on November 22, 2007 8:40 am | Permalink
I suppose it should be mentioned that you can trim the code down to 0 lines if you choose to use NSGradient in 10.5
proclaimed by Chad Weider on November 22, 2007 8:56 am | Permalink
Eh - semi-sarcastic comment about Weider’s 0-line solution retracted. Thanks for the “edit” feature, Ankur.
written by Daniel Jalkut on November 22, 2007 10:00 am | Permalink
“Take Ankur’s Trimmit tool. You can de-bloat all sorts of applications in a breeze. Still, why should you have to waste valuable time doing that, when you already paid the software author for this job? “
Interesting example.
Given the source code, I’ll bet I can find some cases which don’t apply to how I would use the application, or the applications on which I would use it. If I strip that code out, can I write a blog about how I’ve optimized Ankur’s shoddy work and reduced his massive code bloat?
reported by Wade on November 22, 2007 4:14 pm | Permalink
‘Is it just me or are all the flamers constantly changing their arguments? First it’s “show me the numbers!”, then “I can’t be bothered optimizing!” then “It’s a library!”’
Oh, then eventually they just accuse you of never releasing a mac application. Sorry to spoil it.
voiced by Sean on November 22, 2007 4:33 pm | Permalink
To point out the absurdity: Why aren’t people so hyped up over how NSGradient tries to provide both linear and radial gradients? How dare they?! The nerve to introduce such wasteful code bloat! We should narrow it down to the simplest possible thing that solves only one problem. And then disparage anyone for noting that it tries to solve multiple problems in a convenient interface.
CTGradient is a drop-in class. Sure, stuff can be stripped away to provide the bare essentials. I have an NSData category that provides base64 encoding and decoding. What if I only need one for a particular application? Am I somehow beholden by some imaginary (seriously, prove it) problem to strip away one when it isn’t currently used in an application? If you said “yes”, you can volunteer to separate my classes out into dozens of files and coordinate what I need and when.
This isn’t to say that the article is necessarily wrong. I agree that programmers should use the simplest possible thing that will work. However, there are tradeoffs to be made. When you accept libraries, drop-in convenience classes, and, hell, any code that you didn’t write, you have to accept that the code probably wasn’t written for your specific need alone. Even if you write reusable classes, they sometimes need to be tweaked to work in multiple locations. Welcome to abstraction. It makes code reusable.
disclosed by Grayson on November 22, 2007 5:06 pm | Permalink
OMG Wade.
Did you not read? I’ll break it down for you:
And keep in mind this is an example. If they’re so careless about this - and defending their position vigorously - then I shudder to think of how the rest of their applications are coded.
announced by Harley on November 22, 2007 5:53 pm | Permalink
“Oh, then eventually they just accuse you of never releasing a mac application. Sorry to spoil it.”
Chip say hello to shoulder.
determined by Jonathan Wight on November 22, 2007 5:56 pm | Permalink
Grayson, if this had been a framework or a shared library, I’d have agreed with you 100%. But what I see here is that instead of copying and pasting tiny code, developers are copying and pasting huge code, and don’t care about the performance implications. It wouldn’t take more time to get the CGShading in. I’ve done it before. Ankur’s just done it with his app. The only explanation I can think of - and it’s pretty scary - is that the offending developers care more about their own convenience, popularity and image than their users.
reported by Harley on November 22, 2007 6:00 pm | Permalink
“developers are copying and pasting huge code, and don’t care about the performance implications”
Of which there may or may not be any.
You are getting your knickers in a twist over nothing. Competent developers are more than capable of profiling their apps, and analyse the performance implications themselves.
revealed by Jonathan Wight on November 22, 2007 6:08 pm | Permalink
Of course there will be. Have you looked at the code?
And besides, even if there aren’t, the fact that they don’t care speaks volumes.
Competent developers would not raise such a raucous debate about it. And if they care more about their own convenience than their users’, their level of competency should be in question.
declared by Harley on November 22, 2007 6:15 pm | Permalink
@harley,
Yes I’ve looked at the code. Most of Ankur’s optimisations were specialisations for his needs. Not all of those epecialisations will be relevent or needed for other apps. Some apps might need to render parts of a view for example, so the code that Ankur removes will actually be needed.
There seem to be two camps: A) people stating that ankur’s code is the best thing since sliced bread and that 30 lines is obviously superior to 1300. B) people stating that they need more information (e.g. profiles) and aren’t willing to assume that the optimsations are necessary
I’m in the B camp if you couldn’t tell.
mentioned by Jonathan Wight on November 22, 2007 6:26 pm | Permalink
You’re right of course. But I would say that last bit - that “you should’ve written your own you lazy dolt” - was more relevant. It’s like if I wrote a wrapper to NSWindow that flips it, rotates it, zooms it, etc. taking 2 mb and you use it in your apps because you need the
[window titlebarHeight]method. Can you say waste?Ya.
Ok.
I think camp B would do well to read the comment by diem.
But I still don’t understand the reason for this fuss.
determined by Harley on November 22, 2007 6:34 pm | Permalink
The “All optimisation is worth it, period. ” comment?
Optimisation comes with a cost: developer time.
Like everything else it has to be weighed.
And the reason it caused such a fuss… Who knows? The internet is full of strangeness.
reasonded by Jonathan Wight on November 22, 2007 6:41 pm | Permalink
Sean, as in Sean Collins?
http://www.seosoft.info/seolog/2007/11/22/an-introduction-to-sean-collins/
disclosed by Kenneth on November 22, 2007 7:24 pm | Permalink
Oh noes!! But wait! CGShading has axial and radial too!
Indeed. There’s really no reason to get so uptight about it. As I said before, fanboy and co. were initially demanding “numbers”, then saying this is a shared library (yeah, right), then the (only valid) argument about taking time. But if that’s the case, why the flamin’ flaming? Not to mention the nonsense on Mr. Tweetie.
Ankur: You should think about installing lemur CATTA. Keep out the idiots.
declared by Harley on November 22, 2007 7:26 pm | Permalink
Actually the main person getting uptight over this is Rick. Notice the stream of postings to his site? Seems to be indulging in some serious mud slinging and name calling.
Screw loose perhaps?
disclosed by Jonathan Wight on November 22, 2007 7:48 pm | Permalink
I’m sad to say I don’t disagree with him.
If his client list (and software) is anything to go by, he seems entitled to it. He may have just gained another customer (me).
revealed by Anonymous Coward on November 22, 2007 7:54 pm | Permalink
@anonymous: All Rick ever seems to do is complain about how stupid users are because they put up with developers not being as “perfect” as he likes to think he is. He fails to realise that 99% of users don’t care about most of the stuff he complains about. And to be completely honest I wouldn’t trust someone who steals icons from other 3rd party developers.
recorded by Martin Pilkington on November 22, 2007 9:55 pm | Permalink
Are people being obtuse? (deliberately or otherwise)
I have little programming experience beyond a HP47C calculator, but programming isn’t the point here.
It’s about waste; it’s about care and pride in one’s work; it’s about providing customers with the best that the vendor can provide. These ideas or ideals are part and parcel of all social intercourse.
“Couldn’t be bothered” lazy dickheads abound in every field of human activity. “Not worth the effort”? To whom?
Customers’ needs come first.
Not your disk space/memory being wasted? Then who cares, hey? Fuck ‘em!
divulged by pol on November 22, 2007 10:45 pm | Permalink
I appreciate the detail the article goes into on how to optimize code for a specific need.
I also appreciate “code aesthetic”, and even “.app aesthetic” to an extent, but I definitely rate the “real programmers ship” mantra over aesthetics.
I do not appreciate name calling and don’t care about name dropping. Real programmers ship, this is a meritocracy.
MarsEdit’s .app contains extraneous files? File a bug, he’ll fix it. You aren’t insinuating that it’s intentional, I’m sure.
What this article fails to point out is that programmer efficiency is more important than disk space or global optimization (and incidentally the binary output of 1300 of lines of code is guaranteed to be statistical noise).
“Time to market” is always a concern, you want working code as soon as you feel a concept works, whether you’re coding for yourself or for your boss.
Since programmer efficiency isn’t mentioned, there’s clearly no way measuring before acting would be mentioned, or in other words profiling before optimizing. This is dangerous and misleading, and should be rectified.
My gut feeling is that most of the time the gradient code wouldn’t have a significant impact on application performance, I might be wrong but there are no numbers that confirm or deny it.
Without the numbers backing the case, this article is just an academic exercise, with an unjustified negative connotation on the number of lines of code.
recorded by Duncan Wilcox on November 22, 2007 11:51 pm | Permalink
@Diem: “All optimisation is worth it, period.”
I’m sure you could write far more “optimized” code in x86/PPC assembly language than Objective-C. I would not consider that “worth it.”
declared by Brian Christensen on November 23, 2007 5:05 am | Permalink
The linearEvaluation function proposed by the author appears to be at least four or five times faster when considering computation. However, it is likely to perform even better than this due to the decreased footprint, lack of temporary variables, and the monochromatic output.
uttered by J. on November 23, 2007 7:40 am | Permalink
J: That might be so. However, is the slower performance making window resizing annoying, or even noticeably degraded, even on an old machine? If in your app this is true, then yes, optimization might be a good idea. If not, maybe my customers might prefer if I spent that time replying to a dozen support mails.
disclosed by Joachim Bengtsson on November 23, 2007 8:21 am | Permalink
Joachim,
Forget optimization. If you used the CG functions instead of using the CTGradient class, you would have the better performance and the time to answer your support emails.
Those who are viciously attacking this article are perhaps spending too much time in OO-land and not noticing the C.
declared by J. on November 23, 2007 8:30 am | Permalink
@POL
You’re lack of experience of production programming is evident in your attitude. I am not being condescending. In fact, that is the very same attitude that many early developers approached programming with. At some point however, one has to consider the trade off. For example, are we positive that Daniel Jalkut is only drawing monochromatic linear gradients (in a single direction, no less) with his code? Having a single class that generalizes behavior certainly adds memory and computation time if you analyze a single use. However, consider the situation where I have to draw 10 different gradients. Now I have 10 instances of very specialized code. And I have to keep them separate, have a different API for them, etc. Then in my other project where I also use that code, I have 10 different instances of the code. I have to maintain all of these different instances of what amounts to very similar, but specialized code. That time isn’t free. For an indie developer who is doing development, testing, support, and sales, that time isn’t just free, it’s lost revenue. On the flip side of that, what is the gain for the customer? The customer gets an extra couple kilobytes of memory, and maybe instead of taking 100 ms to draw the gradient, it takes 20-25ms. Is that relevant to the user? Even if you do it a billion times, will the user notice? Is it stopping the user from completing their task? Now, take that time that the customer has gained, by the optimization, and compare that to the time the customer would lose because the developer spent an extra 6 months optimizing (for a relatively small project) and specializing the code. Is it worth it for the developer to sacrifice the money and income to do that? Because he does it, does he get to charge more for the product. Do the users see any real value in that optimization, and will they pay more in order to support the indie developer for their time?
It’s a trade off. Specialized code is reduces bloat for a single use scenario, but ends up adding bloat when you use the code in multiple places.
@J.
But one might argue that by using CT, instead of CG, Joachim didn’t have to write any of the underlying gradient drawing code, thus saving much more time.
stated by Vinay Venkatesh on November 23, 2007 8:32 am | Permalink
@pol: If there is a huge difference in memory usage then by all means optimise your code. But you have to think about what is worthwhile. 10MB off 40MB of memory usage is a huge gain, but 1MB isn’t quite as big a gain, especially if you have to do a lot to get that gain. It’s all about weighing up whether the effort expended is worth the performance gain.
And editing 3rd party code isn’t a bad thing, if you know there is something seriously wrong with it that is causing problems in your application. If Ankur profiled the app and found CTGradient to be the problem then by all means optimise it, if the effort expended is worth the gain. I’ve been optimising some rather poor code I wrote a while ago to do syntax highlighting, but found an issue in AGRegex which I was using to parse the code. Changing 1 line managed to help me taken nearly a second of some large test cases I had.
Of course, this is all about processing time and memory usage. You also mentioned disk space. Now developers are also huge application users and often use many applications (so it is our disk space being “wasted”). Many of the disk space optimisations suggested saved 1-2MB per application (obviously a bit more for localised applications). For me that would be around 100-200MB saved in my apps folder. The thing is, 100-200MB is very little by todays standards. That is 0.12% of my HD space (I have a 160GB HD). Cleaning out your downloads folder can often save more space. Arguments like this would have been valid maybe 5, definitely 10 years ago, but the fact is that disk space is the one resource on a computer that can be seen as a commodity.
1 user in a 1000 may actually care about an application taking up an extra MB of space and even less will actually bother to complain about it. Why spend time sort out something for the minority when you can be adding new features, shipping applications, supporting other users, promoting your application or making your application faster in ways that actually affect your users?
voiced by Martin Pilkington on November 23, 2007 8:39 am | Permalink
One might refute that statement with a URL to the appropriate page in the documentation.
stated by J. on November 23, 2007 8:42 am | Permalink
happy thanksgiving everyone.
announced by Taybin on November 23, 2007 8:44 am | Permalink
Martin: I just had a look at the VT page for this “Trimmit” app. You should read the comments.
reported by J. on November 23, 2007 8:46 am | Permalink
Pages: « 1 2 [3] 4 » Show All
Leave a comment