Which has faster performance indexesOfObjectsPassingTest or filteredArrayUsingPredicate?

后端 未结 2 1443
孤城傲影
孤城傲影 2020-11-28 04:56

When needing to filter an NSArray to get a subset of the items in the array returned, which method is quicker more frequently and in edge cases?

2条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-11-28 05:11

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

    Here the tests:

    #import 
    
    @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":""}
    }
    
    - (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
    

提交回复
热议问题