Welcome to the MacNN Forums.

If this is your first visit, be sure to check out the FAQ by clicking the link above. You may have to register before you can post: click the register link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below.

You are here: MacNN Forums > Software - Troubleshooting and Discussion > Developer Center > Simple array speed question

Simple array speed question
Thread Tools
Mac Enthusiast
Join Date: Sep 2000
Location: Vermont, USA
Status: Offline
Reply With Quote
May 17, 2004, 12:37 AM
 
Which would be faster:

1) [aMutArray addObjectsFromArray:otherArray];

or

2)

for(i=0;i<[otherArray count];i++) {

[aMutArray addObject:[otherArray objectAtIndex:i]];
}

or 3)

NSEnumerator *enu = [otherArray objectEnumerator];
id anObject = nil;
while(anObject = [enu nextObject])
[aMutArray addObject:anObject];

Any explanations of why one would be faster over another would be greatly appreciated.

Thanks.
     
Mac Elite
Join Date: Feb 2001
Location: Vancouver, WA
Status: Offline
Reply With Quote
May 17, 2004, 04:12 AM
 
As for finding out which is faster, it'd be pretty easy to test for yourself -- write a little tool that builds a big bogus array and runs through each routine several times, with some logging timeIntervalSinceNow in between.

Actually, I'm bored, so I may as well...
Code:
#import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; #define ARRAY_SIZE 1000 #define NUM_TESTS 100000 // setup: build bogus array, make immutable NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:ARRAY_SIZE]; int i; for (i = 0; i < ARRAY_SIZE; i++) { [array1 addObject:[NSString stringWithFormat:@"%d", i]]; } NSArray *otherArray = [NSArray arrayWithArray:array1]; // test 1 NSAutoreleasePool *testPool1 = [[NSAutoreleasePool alloc] init]; NSDate *start1 = [NSDate date]; for (i = 0; i < NUM_TESTS; i++) { NSMutableArray *testArray1 = [NSMutableArray arrayWithCapacity:ARRAY_SIZE]; [testArray1 addObjectsFromArray:otherArray]; } NSLog(@"addObjectsFromArray: %f", -[start1 timeIntervalSinceNow]); [testPool1 release]; // test 2 NSAutoreleasePool *testPool2 = [[NSAutoreleasePool alloc] init]; NSDate *start2 = [NSDate date]; for (i = 0; i < NUM_TESTS; i++) { NSMutableArray *testArray2 = [NSMutableArray arrayWithCapacity:ARRAY_SIZE]; int j; for (j = 0; j < [otherArray count]; j++) { [testArray2 addObject:[otherArray objectAtIndex:j]]; } } NSLog(@"addObject * for(): %f", -[start2 timeIntervalSinceNow]); [testPool2 release]; // test 3 NSAutoreleasePool *testPool3 = [[NSAutoreleasePool alloc] init]; NSDate *start3 = [NSDate date]; for (i = 0; i < NUM_TESTS; i++) { NSMutableArray *testArray3 = [NSMutableArray arrayWithCapacity:ARRAY_SIZE]; NSEnumerator *enu = [otherArray objectEnumerator]; id anObject = nil; while(anObject = [enu nextObject]) { [testArray3 addObject:anObject]; } } NSLog(@"addObject * enumerator: %f", -[start3 timeIntervalSinceNow]); [testPool3 release]; [pool release]; return 0; }
Not the most scientific test -- since we're measuring "wall clock" time, we'd have to make sure no other process is grabbing CPU time while we run. But the repeated tests help to make up for that. Anyhow, here's my output:
Code:
2004-05-17 00:58:34.503 arraytest[19233] addObjectsFromArray: 46.095778 2004-05-17 00:59:51.187 arraytest[19233] addObject * for(): 66.782827 2004-05-17 01:00:54.199 arraytest[19233] addObject * enumerator: 54.454364
The first method seems to be fastest... why? This is sort of a general design issue with the Cocoa frameworks: you usually get the best performance by telling the kit as much about your specific problem as possible. Specific-purpose APIs can be optimized further than general-purpose APIs -- Apple (or NeXT) has been able to spend years working out the fastest L33T HAX0R way to combine two arrays. But -addObject: and -objectAtIndex: have to perform decently in a wider variety of situations, so the same optimization tricks can't always apply.

Actually, if you mess around with ARRAY_SIZE and NUM_TESTS a bit, you'll likely find that there are cases in which direct access is a little bit faster than -addObjectsFromArray:. However, I'd still generally recommend using it, based on the advice above -- the time saved is small, and a future OS X release could improve the optimization for that case. Using addObjectsFromArray: keeps your code easily readable and debuggable. It's said that premature optimization is the root of all evil -- worry about making your program run first, then worry about making it run well... and handle the high-level (or design-level) optimizations before you get to worrying about message-passing overhead and the like.

It's easy to guess at how Apple might have optimized -addObjectsFromArray: (for one thing, they have full access to the internal data structures, and don't need to waste time calling -objectAtIndex: over and over again), but it's a little harder to guess at why enumerators often seem to be faster than iterating through an array with a for loop. My theory: NSEnumerator is probably a cover for a data structure like a linked list, where getting the next pointer doesn't require any arithmetic. (Based on that theory, though, it's possible that it could be faster or slower on different CPU architectures.) But stuff that low-level isn't exactly my area of expertise, so this just a guess.

More generally, enumerators represent the more OO-friendly way of iterating through a collection. Not only is it considered more readable and debuggable than a for loop (by many, but not all programmers), it's also collection-class agnostic: you can write a method that iterates across any collection (a set, array, or dictionary) using an enumerator, whereas using -objectAtIndex: requires you to be dealing with an array. And generally, the speed difference between enumerators and objectAtIndex-loops is small enough that it's not worth caring about... so use whichever you prefer.
Rick Roe
icons.cx | weblog
     
Mac Enthusiast
Join Date: Sep 2000
Location: Vermont, USA
Status: Offline
Reply With Quote
May 17, 2004, 10:55 AM
 
Hi Rick,

Thanks for the great reply. I never thought of building a quick test like that! I will keep this in mind for the future.

My code is working great right now, so I am just trying to optimize a few things.

Thanks again,
Ian
     
   
Thread Tools
Forum Links
Forum Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On
Top
Privacy Policy
All times are GMT -5. The time now is 06:31 PM.
All contents of these forums © 1995-2011 MacNN. All rights reserved.
Branding + Design: www.gesamtbild.com
vBulletin v.3.8.7 © 2000-2011, Jelsoft Enterprises Ltd., Content Relevant URLs by vBSEO 3.3.2