我最近一直在关于连续或随机访问与C数组有关的NSArray的性能的一个小研究项目。大多数测试用例都是按照我的预期显示出来的,然而有些测试用例并不工作,我以为他们会这样做,我希望有人能够解释为什么。NSArray vs C Array性能比较
基本上,测试包括用50k对象填充一个C数组,迭代每个对象并调用一个方法(内部只增加对象中的一个浮点数),测试的第二部分涉及创建一个完成50k迭代,但访问数组中的随机对象。基本上它非常简单。
为了做比较,我用C数组初始化NSArray。然后每个测试都通过一个传入一个方法的块来运行,该方法跟踪执行该块所需的时间。我使用的代码包含在下面,但我想覆盖结果和我有第一个问题。
这些测试在iPhone 4上运行,并包装在dispatch_after中,以缓解由于启动应用程序而导致的任何剩余的线程或非原子操作。单次运行的结果如下,每次运行基本上是与微小变化是相同的:
===SEQUENCE===
NSARRAY FAST ENUMERATION: 12ms
NSARRAY FAST ENUMERATION WEAK: 186ms
NSARRAY BLOCK ENUMERATION: 31ms (258.3%)
C ARRAY DIRECT: 7ms (58.3%)
C ARRAY VARIABLE ASSIGN: 33ms (275.0%)
C ARRAY VARIABLE ASSIGN WEAK: 200ms (1666.7%)
===RANDOM===
NSARRAY RANDOM: 102ms (850.0%) *Relative to fast enumeration
C ARRAY DIRECT RANDOM: 39ms (38.2%) *Relative to NSArray Random
C ARRAY VARIABLE ASSIGN RANDOM: 82ms (80.4%)
最快方法似乎使用直接访问在C Array中项目“*(CARRAY + IDX)” ,最令人费解的是,将C数组的指针分配给一个目标c变量“id object = *(carry + idx)”导致了巨大的性能下降。
我初步认为它可能是做引用计数的东西,因为变量很强,所以在这一点上,我将其改变为弱预期性能增加“__weak id object = *(carry + idx)”。令我惊讶的是它实际上慢了很多。
随机存取结果非常好,我基于序列结果的预期,所以没有足够幸运的意外。
由于这一结果有一些问题:
- 为什么赋值给一个变量这么长的时间?
- 为什么分配给弱变量的时间更长? (也许有什么我不明白在这里)
- 考虑到上述如何苹果得到了标准的快速枚举执行如此之好?
为了完整起见,这里是代码。所以我创建数组如下:
__block id __strong *cArrayData = (id __strong *)malloc(sizeof(id) * ITEM_COUNT);
for (NSUInteger idx = 0; idx < ITEM_COUNT; idx ++) {
NSTestObject *object = [[NSTestObject alloc] init];
cArrayData[idx] = object;
}
__block NSArray *arrayData = [NSArray arrayWithObjects:cArrayData count:ITEM_COUNT];
而且NSTestObject的定义是这样的:
@interface NSTestObject : NSObject
- (void)doSomething;
@end
@implementation NSTestObject
{
float f;
}
- (void)doSomething
{
f++;
}
,并用于分析代码的方法:
int machTimeToMS(uint64_t machTime)
{
const int64_t kOneMillion = 1000 * 1000;
static mach_timebase_info_data_t s_timebase_info;
if (s_timebase_info.denom == 0) {
(void) mach_timebase_info(&s_timebase_info);
}
return (int)((machTime * s_timebase_info.numer)/(kOneMillion * s_timebase_info.denom));
}
- (int)profile:(dispatch_block_t)call name:(NSString *)name benchmark:(int)benchmark
{
uint64_t startTime, stopTime;
startTime = mach_absolute_time();
call();
stopTime = mach_absolute_time();
int duration = machTimeToMS(stopTime - startTime);
if (benchmark > 0) {
NSLog(@"%@: %i (%0.1f%%)", name, duration, ((float)duration/(float)benchmark) * 100.0f);
} else {
NSLog(@"%@: %i", name, duration);
}
return duration;
}
最后,这是我如何执行实际测试:
int benchmark = [self profile:^ {
for (NSTestObject *view in arrayData) {
[view doSomething];
}
} name:@"NSARRAY FAST ENUMERATION" benchmark:0];
[self profile:^ {
for (NSTestObject __weak *view in arrayData) {
[view doSomething];
}
} name:@"NSARRAY FAST ENUMERATION WEAK" benchmark:0];
[self profile:^ {
[arrayData enumerateObjectsUsingBlock:^(NSTestObject *view, NSUInteger idx, BOOL *stop) {
[view doSomething];
}];
} name:@"NSARRAY BLOCK ENUMERATION" benchmark:benchmark];
[self profile:^ {
for (NSUInteger idx = 0; idx < ITEM_COUNT; idx ++) {
[*(cArrayData + idx) doSomething];
}
} name:@"C ARRAY DIRECT" benchmark:benchmark];
[self profile:^ {
id object = nil;
NSUInteger idx = 0;
while (idx < ITEM_COUNT) {
object = (id)*(cArrayData + idx);
[object doSomething];
object = nil;
idx++;
}
} name:@"C ARRAY VARIABLE ASSIGN" benchmark:benchmark];
[self profile:^ {
__weak id object = nil;
NSUInteger idx = 0;
while (idx < ITEM_COUNT) {
object = (id)*(cArrayData + idx);
[object doSomething];
object = nil;
idx++;
}
} name:@"C ARRAY VARIABLE ASSIGN WEAK" benchmark:benchmark];
NSLog(@"\n===RANDOM===\n");
benchmark = [self profile:^ {
id object = nil;
for (NSUInteger idx = 0; idx < ITEM_COUNT; idx ++) {
object = arrayData[arc4random()%ITEM_COUNT];
[object doSomething];
}
} name:@"NSARRAY RANDOM" benchmark:benchmark];
[self profile:^ {
NSUInteger idx = 1;
while (idx < ITEM_COUNT) {
[*(cArrayData + arc4random()%ITEM_COUNT) doSomething];
idx++;
}
} name:@"C ARRAY DIRECT RANDOM" benchmark:benchmark];
[self profile:^ {
id object = nil;
NSUInteger idx = 0;
while (idx < ITEM_COUNT) {
object = (id)*(cArrayData + arc4random()%ITEM_COUNT);
[object doSomething];
idx++;
}
} name:@"C ARRAY VARIABLE ASSIGN RANDOM" benchmark:benchmark];
必需阅读:[Ridiculous Fish:Array](http://ridiculousfish.com/blog/posts/array.html) – Caleb