There are 2 choices (possibly more). Using NSSet as an example:
NSMutableSet * mutableSet = [ NSMutableSet setWithSet:nonMutableSet ] ;
or
NSMutableSet * mutableSet = [ [ nonMutableSet mutableCopy ] autorelease ] ;
Is there any difference between these two implementations? Is one "more efficient" at all? (Other examples would be NSArray/NSMutableArray and NSDictionary/NSMutableDictionary
Benchmark fun! :)
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{ @autoreleasepool {
NSMutableSet *masterSet = [NSMutableSet set];
for (NSInteger i = 0; i < 100000; i++) {
[masterSet addObject:[NSNumber numberWithInteger:i]];
}
clock_t start = clock();
for (NSInteger i = 0; i < 100; i++) {
@autoreleasepool {
[NSMutableSet setWithSet:masterSet];
}
}
NSLog(@"a: --- %lu", clock() - start);
sleep(1);
start = clock();
for (NSInteger i = 0; i < 100; i++) {
@autoreleasepool {
[[masterSet mutableCopy] autorelease];
}
}
NSLog(@"b: --- %lu", clock() - start);
return 0;
} }
On my machine (10.7), setWithSet: is ~3x slower than -mutableCopy (Does somebody want to try on iOS 5? :) )
Now, the question is: why?
-mutableCopy is spending most of its time in CFBasicHashCreateCopy() (see CFBasicHash.m). This appears to be copying the hash buckets directly, with no rehashing.
Running Time Self Symbol Name
256.0ms 61.5% 0.0 -[NSObject mutableCopy]
256.0ms 61.5% 0.0 -[__NSCFSet mutableCopyWithZone:]
256.0ms 61.5% 0.0 CFSetCreateMutableCopy
255.0ms 61.2% 156.0 CFBasicHashCreateCopy
97.0ms 23.3% 44.0 __CFSetStandardRetainValue
-setWithSet is enumerating through each value of the set, and then adding it to the new set. From the implementation of CFBasicHashAddValue (again in CFBasicHash.m), it looks like it is rehashing each value in the set.
Running Time Self Symbol Name
1605.0ms 86.0% 0.0 +[NSSet setWithSet:]
1605.0ms 86.0% 2.0 -[NSSet initWithSet:copyItems:]
1232.0ms 66.0% 68.0 -[__NSPlaceholderSet initWithObjects:count:]
1080.0ms 57.8% 299.0 CFBasicHashAddValue
324.0ms 17.3% 28.0 -[NSSet getObjects:count:]
272.0ms 14.5% 75.0 __CFBasicHashFastEnumeration
This rehash makes sense at the CFSet level. CFSets take a CFSetHashCallBack in the callBacks parameter; thus, two CFSets of CFNumbers could have a different hashing routine specified. Foundation's NSSet uses CFSet under-the-hood, and has a CFSetHashCallBack function which invokes -[NSObject hash]
. (Although I guess that Apple could optimize this case and avoid the rehash when two sets have the same hash callback).
Note that this benchmark is for NSSet (of NSNumbers) only, other collection classes may have different performance characteristics.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With