Monday, September 18, 2006

[Question] Is NSEnumerator really faster than using a for loop?

Now this is a classic question. One of the strange things you see just learning Cocoa/Objective-C is this thing called the Enumerator loop which looks something like

NSArray *anArray = // ... ; NSEnumerator *enumerator = [anArray objectEnumerator]; id object; while ((object = [enumerator nextObject])) { // do something with object... }
and naturally people ask if using NSEnumerator is really faster than using a for loop like this example
NSMutableArray *my array = //... int i; for( i=0; i < LIMIT ; i++) { NSManagedObject *obj = [myArray objectAtIndex:i]; //do something with object }
The answer to this question is yes just a little bit though. I've been told by some smart people (though I haven't verified) that the kernel XNU on Mac OS X is optimized for Objective-C messages, even if you don't believe that we all know Apple optimizes so much of their own software to take advantage of Altivec(PPC), hardware accelleration, etc. So naturally you should assume Apple has given you some reason to use this loop. To test what is really faster I went to CocoaDev and grabbed code off a page where a discussion on this very topic took place (take a good look at the source code.) I compiled this code myself and then ran it once, cleared out the test and ran the test 3 times, all tests using 10,000 objects and 10,000 iterations then punched in the results in NeoOffice. Here are the results (all times are in seconds): (pay attention to the NSEnumerator and for loop tests (first 2 on the left in the graph) for now) as you can see yes the NSEmumerator loop is faster than the for loop but only by a few seconds and that was with 10,000 objects and 10,000 iterations. When I did the same test with 1,000 objects and 1,000 iterations the results all land roughly within 1/10th of 1 second +/- a few milesconds. The conclusion Really just to stay in Cocoa standards you should use NSEnumerator, but at the same time it's not the end of the world if you don't. There are some examples where you might really need to know what the index of the object your dealing with is, in which case you should use a for loop. Now as for those last 3 tests on there aren't that important, I included them because they do show you can gain some speed by really optimizing the heck out of your code if you really want to do so. I encourage you to go to the CocoaDev page linked above and look at the code for yourself to see what you really want to use for yourself, but as you'll see the code is vastly more complicated than you'll want unless you really need to squeeze every last ounce of peformance out of your application.

6 comments:

Anonymous said...

You should note to readers that Apple documentation advises against using NSEnumerators on mutable collections if the collection is going to be modified:

From the documentation:
Note: It is not safe to modify a mutable collection while enumerating through it. Some enumerators may currently allow enumeration of a collection that is modified, but this behavior is not guaranteed to be supported in the future.

Anonymous said...

You've answered this question only for arrays with 10,000 objects. If you rerun these tests with different numbers of objects, you'll find that the NSEnumerator and for-loop timing differs between small and large arrays.

The numbers I've seen -- for years -- suggest that for-loops are far superior for most arrays (which are 'small').

Also, if you were at the WWDC session where they discussed ObjC 2.0 in-depth, you'd have wanted to pay special attention to the section about collection iteration.

Massive said...

You've answered this question only for arrays with 10,000 objects. If you rerun these tests with different numbers of objects, you'll find that the NSEnumerator and for-loop timing differs between small and large arrays.

The numbers I've seen -- for years -- suggest that for-loops are far superior for most arrays (which are 'small').

Also, if you were at the WWDC session where they discussed ObjC 2.0 in-depth, you'd have wanted to pay special attention to the section about collection iteration.

Massive said...

Sorry for the dupe, I meant to sign in. :)

faceleg said...

Thanks for posting this test, I was wondering if I could/should use a for loop - I'm just so darn accustomed to them.

Anonymous said...

Stepping through arrays in the classic way array[0] etc allows you to go forward, backward and to use random access.
NSEnumerator only allows forwards.
Seems like the only thing you get is a marginal improvement in speed.

 
...