Gần đây tôi đã chạy một dự án nghiên cứu nhỏ về hiệu suất truy cập tuần tự hoặc ngẫu nhiên tới một NSArray liên quan đến một mảng C. Hầu hết các trường hợp kiểm tra được hiển thị như tôi mong đợi tuy nhiên một số không làm việc như thế nào tôi nghĩ rằng họ sẽ và tôi hy vọng ai đó có thể giải thích tại sao.So sánh hiệu suất của NSArray so với C Array
Về cơ bản thử nghiệm bao gồm điền một mảng C với các đối tượng 50k, lặp qua từng đối tượng và gọi phương thức (trong đó chỉ tăng một phao trong đối tượng), phần thứ hai của thử nghiệm liên quan đến việc tạo vòng lặp hoàn thành 50k lặp lại nhưng truy cập một đối tượng ngẫu nhiên trong mảng. Về cơ bản nó khá đơn giản.
Để thực hiện so sánh, tôi đang khởi tạo NSArray với mảng C. Mỗi bài kiểm tra sau đó được chạy qua một khối được truyền vào một phương thức theo dõi thời gian cần thiết để thực thi khối. Mã tôi đang sử dụng được chứa dưới đây nhưng tôi muốn bao gồm các kết quả và truy vấn tôi có đầu tiên.
Các thử nghiệm này được chạy trên iPhone 4 và được bao bọc trong dispatch_after để giảm thiểu bất kỳ hoạt động luồng hoặc hoạt động không nguyên tử nào còn lại do khởi động ứng dụng. Kết quả của một hoạt động đơn lẻ như sau, mỗi lần chạy là về cơ bản giống với các biến thể nhỏ:
===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ách tiếp cận nhanh nhất dường như được tiếp cận trực tiếp các mục trong thư mục C Mảng sử dụng "* (carray + idx)" , điều khó hiểu nhất là việc gán con trỏ từ mảng C tới biến c mục tiêu "id object = * (carry + idx)" gây ra một hit hiệu năng lớn. Tôi thấy ban đầu nó có thể là vòng cung làm một cái gì đó với tính tham chiếu như biến mạnh vì vậy tại thời điểm này tôi đã thay đổi nó thành yếu mong đợi hiệu suất tăng "__weak id object = * (carry + idx)". Ngạc nhiên thay, nó thực sự chậm hơn rất nhiều.
Kết quả truy cập ngẫu nhiên diễn ra khá tốt như thế nào tôi mong đợi dựa trên kết quả chuỗi, do đó, không có bất ngờ có may mắn đủ.
Như một kết quả của việc này có một số câu hỏi:
- Tại sao gán cho một biến mất quá lâu?
- Tại sao việc gán cho một biến yếu thậm chí còn lâu hơn? (Có thể có điều gì đó mà tôi không hiểu đang diễn ra ở đây)
- Xem xét ở trên làm sao Apple có được liệt kê nhanh tiêu chuẩn để thực hiện tốt như vậy?
Và để có đầy đủ, đây là mã. Vì vậy, tôi đang tạo ra các mảng thể như sau:
__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];
Và NSTestObject được định nghĩa như thế này:
@interface NSTestObject : NSObject
- (void)doSomething;
@end
@implementation NSTestObject
{
float f;
}
- (void)doSomething
{
f++;
}
Và phương pháp sử dụng để cấu hình các mã:
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;
}
Cuối cùng đây là cách tôi đang thực hiện các thử nghiệm thực tế:
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];
Bắt buộc đọc: [Vô lý cá: Mảng] (http://ridiculousfish.com/blog/posts/array.html) – Caleb