I’m coming from a C++/STL world and I wanted to check how objective-c containers are in comparison to stl.
I wanted to compare an array of numbers but the only way to add a number to an NSArray is using NSNumber which is utterly slow and drank my ram empty so I guess I need to dealloc them manually. But I don’t want to test side effects so I just added [NSNull null] into the array.
The results of adding 10k things into array 1k times:
NSArray – 0.923411 seconds
vector<int> – 0.129984 seconds
I thought it might be allocations and deallocations so I set the number of arrays(imax in the code) to 1 and number of additions to 10000000(jmax) but it was even slower
NSArray – 2.19859 seconds
vector<int> – 0.223471 seconds
Edit:
As mentioned in the comments the constant increasing size of the array might be the problem so I made NSArray using arrayWithCapacity, but vector with reserve too and it was even slower than before(!) (imax = 1, jmax = 10000000).
NSArray – 2.55942
vector<int> – 0.19139
End edit
Why is this so slow?
My code for reference:
#import <Foundation/Foundation.h>
#include <vector>
#include <iostream>
#include <time.h>
using namespace std;
int main (int argc, const char * argv[])
{
int imax = 1000;
int jmax = 10000;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
cout << "Vector insertions" << endl;
clock_t start = clock();
for(int i = 0; i < imax; i++)
{
vector<int> *v = new vector<int>();
for(int j = 0; j < jmax; j++)
{
v->push_back(j);
}
delete v;
}
double interval = (clock() - start) / (double)CLOCKS_PER_SEC;
cout << interval << " seconds" << endl;
cout << "NSArray insertions" << endl;
start = clock();
for(int i = 0; i < imax; i++)
{
NSMutableArray *v = [[NSMutableArray alloc] init];
for(int j = 0; j < jmax; j++)
{
[v addObject:[NSNull null]];
}
[v dealloc];
}
interval = (clock() - start) / (double)CLOCKS_PER_SEC;
cout << interval << " seconds" << endl;
[pool drain];
return 0;
}
@JeremyP provides an excellent link and information. Always read the fish. Here’s some breakdown of what’s eating time, though, and what you might do about it.
First, there’s the many calls to
objc_msgSend()for dynamic dispatch. These can be avoided and you’ll save some of the time (though not as much as you’d think.objc_msgSend()is crazy optimized). But you’ll knock maybe 5% off by skipping it:A lot of time is eaten up with
retain/release. You can avoid that (and stick real numbers in rather thanNSNumber) by using a non-retainingCFMutableArray). This will get the append times to about 2x ofvector.The biggest cost of this one is the calls to
memmove()(or the collectable version of it on the Mac).Man,
NSMutableArraysure is slow. How could Apple be so stupid, right? I mean, really… wait… I wonder if there’s somethingNSMutableArraydoes better thanvector?Try swapping out these lines for their obvious counterparts:
(Yes, including creating and releasing the
NSNumber, not just usingNSNull.)Oh, and you might try this one too to see just how fast
NSMutableArrayandCFMutableArrayreally can be:In my tests I get: