哪个具有更快的性能索引OfObjectsPassingTest或filteredArrayUsingPredicate



当需要过滤 NSArray 以获取返回的数组中项目的子集时,哪种方法更快、更频繁、更在极端情况下?

以下测试(在发布模式下编译,在 Mac Pro 上执行)表明 如果使用,filteredArrayUsingPredicateindexesOfObjectsPassingTest慢"文本"谓词,但如果使用基于块的谓词,则速度更快。我的测试中的 fasted 方法是一个简单的(快速枚举)循环,它添加了所有匹配项对象到可变数组。

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

8.514334(谓词与格式)4.422550(谓词带块)5.170086 (索引对象通过测试)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;
}

我用全新的 Xcode 6 性能测试 (Objective-C) 测试了这个问题,测试用例如下。我得到了以下结果,表明带有标志 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

以下是测试:

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

更新

我在-testPerformanceEnumerationConcurrent中更改了以下内容:

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

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

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

最新更新