哪个有更快的性能索引?ObjectsPassingTest或filteredArrayUsingPredicate?

当需要筛选NSArray以获取返回的数组中的项目的子集时,哪种方法在边缘情况下更快更频繁?

以下testing(在发布模式下编译,在Mac Pro上执行) 表明 ,如果使用“textual”谓词, filteredArrayUsingPredicateindexesOfObjectsPassingTest要慢,但如果使用基于块的谓词,则速度会更快。 我testing中的禁食方法是一个简单的(快速枚举)循环,将所有匹配的对象添加到可变数组中。

筛选包含10,000,000个词典的数组的结果,其中约50%与谓词匹配:

 8.514334(predicateWithFormat)
 4.422550(predicateWithBlock)
 5.170086(indexesOfObjectsPassingTest)
 3.154015(快速枚举+可变数组)

当然,其他谓词的结果可能不同。

 #import <Foundation/Foundation.h> NSUInteger filter1(NSArray *a) { NSPredicate *pred = [NSPredicate predicateWithFormat:@"num > 1000 AND foo == 'bar'"]; NSArray *filtered = [a filteredArrayUsingPredicate:pred]; return [filtered count]; } NSUInteger filter2(NSArray *a) { NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *obj, NSDictionary *bindings) { return ([obj[@"num"] intValue] > 1000 && [obj[@"foo"] isEqualToString:@"bar"]); }]; NSArray *filtered = [a filteredArrayUsingPredicate:pred]; return [filtered count]; } NSUInteger filter3(NSArray *a) { NSIndexSet *matching = [a indexesOfObjectsPassingTest:^BOOL(NSDictionary *obj, NSUInteger idx, BOOL *stop) { return ([obj[@"num"] intValue] > 1000 && [obj[@"foo"] isEqualToString:@"bar"]); }]; NSArray *filtered = [a objectsAtIndexes:matching]; return [filtered count]; } NSUInteger filter4(NSArray *a) { NSMutableArray *filtered = [NSMutableArray array]; for (NSDictionary *obj in a) { if ([obj[@"num"] intValue] > 1000 && [obj[@"foo"] isEqualToString:@"bar"]) { [filtered addObject:obj]; } } return [filtered count]; } void testmethod(NSArray *a, NSUInteger(*method)(NSArray *a)) { @autoreleasepool { NSDate *t1 = [NSDate date]; NSUInteger count = method(a); NSDate *t2 = [NSDate date]; NSLog(@"%f", [t2 timeIntervalSinceDate:t1]); } } int main(int argc, const char * argv[]) { @autoreleasepool { NSMutableArray *a = [NSMutableArray array]; for (int i = 0; i < 10000000; i++) { [a addObject:@{@"num": @(arc4random_uniform(2000)), @"foo":@"bar"}]; } testmethod(a, filter1); testmethod(a, filter2); testmethod(a, filter3); testmethod(a, filter4); } return 0; } 

我用下面的testing用例对全新的Xcode 6性能testing(Objective-C)进行了testing。 我得到了以下结果,表明带有NSEnumerationConcurrent标志的NSEnumerationConcurrent是大型数组最快的过滤方法:

 testPerformancePredicateWithFormat - measured [Time, seconds] average: 0.189 testPerformancePredicateWithBlock - measured [Time, seconds] average: 0.093 testPerformanceEnumerationBlock - measured [Time, seconds] average: 0.092 testPerformanceIndexesOfObjectsPassingTest - measured [Time, seconds] average: 0.082 testPerformanceFastEnumeration - measured [Time, seconds] average: 0.068 testPerformanceEnumerationConcurrent - measured [Time, seconds] average: 0.036 

这里的testing:

 #import <XCTest/XCTest.h> @interface TestPMTests : XCTestCase @property(nonatomic, copy)NSArray *largeListOfDictionaries; @end @implementation TestPMTests - (void)setUp { [super setUp]; self.largeListOfDictionaries = [NSMutableArray array]; // Initialize a large array with ~ 300.000 entries as Dictionaries of at least one key value pair {"id":"<any id>"} } - (void)testPerformancePredicateWithFormat { NSString *ID = @"204440e5-4069-48e8-a405-88882a5ba27e"; NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF.id == %@", ID]; [self measureBlock:^{ NSArray *filtered = [self.largeListOfDictionaries filteredArrayUsingPredicate:pred]; NSLog(@"Count: %d", filtered.count); }]; } - (void)testPerformancePredicateWithBlock { NSString *ID = @"204440e5-4069-48e8-a405-88882a5ba27e"; NSString *kID = @"id"; NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *d, NSDictionary *bindings) { return [d[kID] isEqualToString:ID]; }]; [self measureBlock:^{ NSArray *filtered = [self.largeListOfDictionaries filteredArrayUsingPredicate:pred]; NSLog(@"Count: %d", filtered.count); }]; } - (void)testPerformanceIndexesOfObjectsPassingTest { NSString *ID = @"204440e5-4069-48e8-a405-88882a5ba27e"; NSString *kID = @"id"; [self measureBlock:^{ NSIndexSet *matchingIndexes = [self.largeListOfDictionaries indexesOfObjectsPassingTest:^BOOL(NSDictionary *d, NSUInteger idx, BOOL *stop) { return [d[kID] isEqualToString:ID]; }]; NSArray *filtered = [self.largeListOfDictionaries objectsAtIndexes:matchingIndexes]; NSLog(@"Count: %d", filtered.count); }]; } - (void)testPerformanceFastEnumeration { NSString *ID = @"204440e5-4069-48e8-a405-88882a5ba27e"; NSString *kID = @"id"; [self measureBlock:^{ NSMutableArray *filtered = [NSMutableArray array]; for (NSDictionary *d in self.largeListOfDictionaries) { if ([d[kID] isEqualToString:ID]) { [filtered addObject:d]; } } NSLog(@"Count: %d", filtered.count); }]; } - (void)testPerformanceEnumerationBlock { NSString *ID = @"204440e5-4069-48e8-a405-88882a5ba27e"; NSString *kID = @"id"; [self measureBlock:^{ NSMutableArray *filtered = [NSMutableArray array]; [self.largeListOfDictionaries enumerateObjectsUsingBlock:^(NSDictionary *d, NSUInteger idx, BOOL *stop) { if ([d[kID] isEqualToString:ID]) { [filtered addObject:d]; } }]; NSLog(@"Count: %d", filtered.count); }]; } - (void)testPerformanceEnumerationConcurrent { NSString *ID = @"204440e5-4069-48e8-a405-88882a5ba27e"; NSString *kID = @"id"; [self measureBlock:^{ NSMutableArray *filtered = [NSMutableArray array]; [self.largeListOfDictionaries enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSDictionary *d, NSUInteger idx, BOOL *stop) { if ([d[kID] isEqualToString:ID]) { [filtered addObject:d]; } }]; NSLog(@"Count: %d", filtered.count); }]; } 

UPDATE

我在-testPerformanceEnumerationConcurrent更改了以下-testPerformanceEnumerationConcurrent

 dispatch_sync(queue, ^{ [filtered addObject:d]; }); 

对于所有其他testing中的并发版本,结果仍然更好。

 -[TestPMTests testPerformancePredicateWithFormat average: 0.134 -[TestPMTests testPerformancePredicateWithBlock] average: 0.079 -[TestPMTests testPerformanceEnumerationBlock] average: 0.079 -[TestPMTests testPerformanceIndexesOfObjectsPassingTest] average: 0.068 -[TestPMTests testPerformanceFastEnumeration] average: 0.054 -[TestPMTests testPerformanceEnumerationConcurrent] average: 0.029