私は最近、C配列に関連したNSArrayへの順次アクセスまたはランダムアクセスのパフォーマンスに関する少しの研究プロジェクトを行っています。テストケースのほとんどは私が期待していたように見えますが、いくつかは私が彼らがどのように思っていたか、私は誰かがなぜそれを説明できるのかと期待しています。NSArrayとC配列のパフォーマンス比較
基本的にテストは、50k個のオブジェクトを持つC配列を作成し、各オブジェクトを反復処理し、メソッドを呼び出します(内部的にはオブジェクト内の浮動小数点数をインクリメントします)。テストの2番目の部分では、配列内のランダムなオブジェクトにアクセスします。基本的にはかなり単純です。
私はNSArrayをC配列で初期化しています。各テストは、ブロックを実行するのにかかる時間を追跡するメソッドに渡されるブロックを介して実行されます。私が使用しているコードは以下に含まれていますが、最初に得た結果とクエリをカバーしたいと思います。
これらのテストは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%)
最速のアプローチは、直接「*(CARRAY + IDX)」を使用して、Cアレイ内のアイテムにアクセスしているように見えますCの配列から目的のC変数 "id object = *(carry + idx)"にポインタを代入すると大量のパフォーマンスが低下します。
私は当初、変数が強かったので参照カウントで何かをしていたかもしれないと考えました。この時点で、パフォーマンスが「__weak id object = *(carry + idx)」を増やすことを期待して弱に変更しました。私の驚いたことに、それは実際にはかなり遅かった。
ランダムアクセスの結果は、シーケンス結果に基づいてどのように期待されていたのでしょうか。驚くことはありません。
- なぜ変数に代入ない限り取る:
は、この結果として質問の数があるのですか?
- 弱い変数への割り当てにさらに時間がかかるのはなぜですか? (たぶん、ここでは理解できないことがあるかもしれません)
- 上記を考慮して、Appleはどのようにして標準的な高速列挙を実現しましたか?
ここでコードは完全です。だから私は、次のように配列を作成しています:
__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];
必要な読書:[ばかげた魚:アレイ](http://ridiculousfish.com/blog/posts/array.html) – Caleb