Which has faster performance indexesOfObjectsPassingTest or filteredArrayUsingPredicate? Which has faster performance indexesOfObjectsPassingTest or filteredArrayUsingPredicate? ios ios

Which has faster performance indexesOfObjectsPassingTest or filteredArrayUsingPredicate?


The following tests (compiled in Release mode, executed on a Mac Pro) indicate thatfilteredArrayUsingPredicate is slower than indexesOfObjectsPassingTest if you usea "textual" predicate, but faster if you use block-based predicate.The fasted method in my test was a simple (fast-enumeration) loop that adds all matchingobjects to a mutable array.

Results for filtering an array of 10,000,000 dictionaries, where about 50% match the predicate:

8.514334 (predicateWithFormat)4.422550 (predicateWithBlock)5.170086 (indexesOfObjectsPassingTest)3.154015 (fast-enumeration + mutable array)

Of course the results may be different for other predicates.

#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;}


I tested this problem with the brand new Xcode 6 performance tests (Objective-C) with the test cases below. I got the following results indicating that the enumerationBlock with the flag NSEnumerationConcurrent is the fastest filtering method for large arrays:

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

Here the tests:

#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

I changed the following in -testPerformanceEnumerationConcurrent:

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

And the results are still better for the concurrent version that in all other tests.

-[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