Reputation: 60120
I recently found myself writing a piece of code that executed a Core Data fetch, then allocated two mutable arrays with the initial capacity being equal to the number of results returned from the fetch:
// Have some existing context, request, and error objects
NSArray *results = [context executeFetchRequest:request error:&error];
NSMutableArray *firstArray = [[[NSMutableArray alloc]
initWithCapacity:[results count]] autorelease];
NSMutableArray *secondArray = [[[NSMutableArray alloc]
initWithCapacity:[results count]] autorelease];
I looked this over again once I'd written it, and something struck me as odd: I was calling [results count]
twice. The resultset is potentially pretty large (hundreds, maybe a thousand, objects).
My first instict was to break out [results count]
into a separate NSUInteger, then use that integer for the capacity of each of the arrays. My question: is this kind of by-hand optimization necessary? Will the compiler recognize it's running [results count]
twice and just hold the value without me having to explicitly specify that behavior? Or will it run the method twice - a potentially costly operation?
In a similar vein, what other optimizations should programmers (especially iPhone programmers, where there's a limited amount of memory/processing power available) do by hand, as opposed to trusting the compiler?
Upvotes: 1
Views: 817
Reputation: 62769
The compiler will not pick up the fact that you already ran it.
Fixing this is not an optimization, it's simply programming correctly. There are a few things that you just have to be aware of that go beyond optimization.. most of them involve unnecessary looping of one sort or another.
Would it be really hard to just pull it out into a separate variable?
NSArray *results = [context executeFetchRequest:request error:&error];
NSUInteger count=[results count];
NSMutableArray *firstArray = [[[NSMutableArray alloc] initWithCapacity:count] autorelease];
NSMutableArray *secondArray = [[[NSMutableArray alloc] initWithCapacity:count] autorelease];
Which is absolutely no less clear (possibly more clear).
Anyway, executing unbounded unnecessary loops need to be avoided, it's not optimization, it's just wrong.
And please don't think I don't understand the cons of premature optimization, I've ranted about it repeatedly in replies to other questions...
As for your other question, never "optimize" without a test or spec showing that your code is running too slow, but DON'T code poorly. Always be aware of inner loops and things that might cause inner loops--these are the most critical.
An insertion sort into an array list causes an inner loop, an insertion sort into a linked list does not...
iterating over a linked list causes an inner loop (assuming you are using the loop index to retrieve the "Next" value instead of holding onto the node and referencing .next which would be fine), iterating over an array list does not.
Comparing two lists often requires an inner loop and it's often worth looking over your data to try to figure out a way that an inner loop is not required (for instance, if they are both sorted you can code such that you won't get an inner loop.
Upvotes: 1
Reputation: 14558
To answer the actual question: no, an Objective-C compiler cannot optimize away method sends, ever. (Well, actually, there’s one possible case: if it knows for certain that the receiver is nil.)
There is no way for a class to provide guarantees about method behaviour (in particular, you can’t use gcc’s __attribute__((const))
and such with a method), and there is no way for the compiler to tell what method implementation will be called because of Objective-C’s dynamic nature. For example, results
could actually be a proxy object which forwards the count
method to a random object each time it’s called. There’s no particular reason for Core Data to do that, but the compiler doesn’t know that.
All that aside, the cost of calling -[NSArray count]
is trivial, and there’s absolutely no way that particular method will be a bottleneck in anything but extremely contrived code. The habit of avoiding double calls can reasonably be argued to be worthwhile, but actually worrying about the cost or going back to “correct” it for performance reasons would be a waste of time, quite likely more time than your program will spend calling -[NSArray count]
throughout its useful lifetime.
Upvotes: 5
Reputation: 14824
The short answer: none. At least not right now.
I don't think the compiler will recognize that you're running [results count]
twice, BUT that shouldn't matter. The usual quote on this subject is that "premature optimization is the root of all evil". Write your code however you want to; think about readability first. Then, after you've finished, and you've actually noticed that your code is slow, then go back and find optimizations if necessary.
So the rule is: write sensible, readable code, and optimize afterwards if problems are discovered.
Upvotes: 4
Reputation: 2398
First rule of optimization: never optimize without benchmarks verifying what needs optimizing. Otherwise you're suffering from 'premature optimization', and a lot of (potentially) wasted effort.
Now, if you know that something takes 2s to run,and you're running in 4 times when you could be running it once - that would be justifiable. but it sounds like you're not sure in this case, so the answer is "benchmark it"
Upvotes: 4