在使用NSPredicate predicateWithBlock和在for循环中使用块之间是否存在显着的性能差异?

时间:2015-05-13 12:01:51

标签: objective-c objective-c-blocks nspredicate

这样的过滤之间的性能(在任何一个方向上)都存在很大差异:

NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id,NSDictionary*)];
NSArray *filtered = [sourceArray filteredArrayUsingPredicate:predicate];

与此类过滤:

BOOL (^filterPredicate)(id,NSDictionary*) predicate = ^BOOL(id sourceObject, NSDictionary *bindings) {};
foreach (id sourceObject in sourceArray) {
    if (filterPredicate(sourceObject)) {
          filtered addObject:sourceObject;
    }
}

我的直觉是,如果有任何差异,第二种方式会更快,因为第一种方式会有NSPredicate级别的额外包袱。

如果存在差异,它是什么,它有多大,它的来源是什么?

1 个答案:

答案 0 :(得分:3)

Per @ wain的建议我测试了一下。

我需要扩展测试以获得更准确的阅读,但这是我的方法和结果:

  • 创建SomeObject
  • 数组
  • 为每个对象设置SomeObject属性
  • 过滤NSString rangeOfString
  • 比较执行时间

<强> SomeObject.h

@interface SomeObject : NSObject

@property (nonatomic, strong) NSNumber *numericProperty;
@property (nonatomic, strong) NSString *stringProperty;
@property (nonatomic, strong) NSArray *arrayProperty;
@property (nonatomic, strong) NSDate *dateProperty;

@end

<强> ViewController.m

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSArray *objectsToSort = [self generateArrayOfRandomSomeObject:1000];

    // Sorting with predicate

    BOOL (^predicateBlock)(id, NSDictionary *) = ^BOOL(id sourceObject, NSDictionary *bindings) {
        SomeObject *destinationTypeObject = (SomeObject *)sourceObject;
        if (sourceObject) {
            NSRange r = [destinationTypeObject.stringProperty rangeOfString:@"ab" options:NSCaseInsensitiveSearch];
            if (r.location != NSNotFound) {
                return true;
            } else {
                return false;
            }
        }

        return NO;
    };

    NSPredicate *predicate = [NSPredicate predicateWithBlock:predicateBlock];

    NSDate *predicateMethodStart = [NSDate date];

    NSArray *filteredWithPredicate = [objectsToSort filteredArrayUsingPredicate:predicate];

    NSDate *predicateMethodFinish = [NSDate date];

    NSTimeInterval predicateTime = [predicateMethodFinish timeIntervalSinceDate:predicateMethodStart];

    NSLog(@"predicateTime = %f", predicateTime);

    NSData *blockStart = [NSDate date];

    NSMutableArray *filteredWithBlock = [NSMutableArray array];

    for (id sourceObject in objectsToSort) {
        if (predicateBlock(sourceObject, nil)) {
            [filteredWithBlock addObject:sourceObject];
        }
    }

    NSDate *blockFinish = [NSDate date];

    NSTimeInterval blockTime = [blockFinish timeIntervalSinceDate:blockStart];

    NSLog(@"blockTime = %f", blockTime);

}

- (NSArray *)generateArrayOfRandomSomeObject:(NSUInteger)numberOfObjects
{
    NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:numberOfObjects];

    for (int count = 0; count < numberOfObjects; count++) {

        SomeObject *object = [[SomeObject alloc] init];

        int length = arc4random_uniform(10) + 4;
        unichar buf[length];

        for (int idx = 0; idx < length; idx++) {
            buf[idx] = (unichar)('a' + arc4random_uniform(26));
        }

        object.stringProperty = [NSString stringWithCharacters:buf length:length];

        u_int32_t max = ((u_int32_t)((u_int32_t)numberOfObjects * 10));
        u_int32_t randomNumber = arc4random_uniform(max);

        object.numericProperty = [NSNumber numberWithUnsignedInteger:randomNumber];

        [objects addObject:object];

    }

    return objects;
}

@end

获胜者是...... NSPredicate。为什么?我不知道。

2015-05-13 15:17:20.708 TestingPerformance[60942:5787881] predicateTime = 0.000768
2015-05-13 15:17:20.710 TestingPerformance[60942:5787881] blockTime = 0.000923

编辑0:原始代码出错并通过初始化适当大小的数组进行测试

不管怎样,尽管我在原始代码中输入了NSData *blockStart,但它仍然有用!我修好了。

我初始化容量为1000的可变数组。

新结果:

2015-05-13 15:26:51.950 TestingPerformance[61157:5792057] predicateTime = 0.000771
2015-05-13 15:26:51.952 TestingPerformance[61157:5792057] blockTime = 0.000864

所以predicateTime是一致的,blockTime通过这种小调整得到改善。

编辑1:使用传统的for循环而不使用迭代器

for (int i = 0; i < objectsToSort.count; i++) {
    if (predicateBlock(objectsToSort[i], nil)) {
        [filteredWithBlock addObject:objectsToSort[i]];
    }
}

2015-05-13 15:30:25.464 TestingPerformance[61282:5793520] predicateTime = 0.000775
2015-05-13 15:30:25.467 TestingPerformance[61282:5793520] blockTime = 0.001079

编辑2:增加N,添加随机数组访问

- (void)viewDidLoad {
    [super viewDidLoad];

    NSUInteger N = 100000;

    NSArray *objectsToSort = [self generateArrayOfRandomSomeObject:N];

    // Sorting with predicate

    BOOL (^predicateBlock)(id, NSDictionary *) = ^BOOL(id sourceObject, NSDictionary *bindings) {
        SomeObject *destinationTypeObject = (SomeObject *)sourceObject;
        if (sourceObject) {
            NSRange r = [destinationTypeObject.stringProperty rangeOfString:@"ab" options:NSCaseInsensitiveSearch];
            if (r.location != NSNotFound) {
                return true;
            } else {
                return false;
            }
        }

        return NO;
    };

    NSPredicate *predicate = [NSPredicate predicateWithBlock:predicateBlock];

    NSDate *predicateMethodStart = [NSDate date];

    NSArray *filteredWithPredicate = [objectsToSort filteredArrayUsingPredicate:predicate];

    NSDate *predicateMethodFinish = [NSDate date];

    [self doStuffWithArray:filteredWithPredicate];

    NSDate *predicateStuffFinish = [NSDate date];

    NSDate *blockStart = [NSDate date];

    NSMutableArray *filteredWithBlock = [NSMutableArray arrayWithCapacity:N];

    for (id sourceObject in objectsToSort) {
        if (predicateBlock(sourceObject, nil)) {
            [filteredWithBlock addObject:sourceObject];
        }
    }

    NSDate *blockFinish = [NSDate date];
    [self doStuffWithArray:filteredWithBlock];
    NSDate *blockDoStuffFinish = [NSDate date];

    NSLog(@"predicateTime = %f", [predicateMethodFinish timeIntervalSinceDate:predicateMethodStart]);
    NSLog(@"predicate time to do stuff = %f", [predicateStuffFinish timeIntervalSinceDate:predicateMethodFinish]);

    NSLog(@"blockTime = %f", [blockFinish timeIntervalSinceDate:blockStart]);
    NSLog(@"block time to do stuff = %f", [blockDoStuffFinish timeIntervalSinceDate:blockFinish]);
}

这是做事情

- (void)doStuffWithArray:(NSArray *)arrayOfSomeObjects {

    NSLog(@"Filtered array has %lu objects.", (unsigned long)arrayOfSomeObjects.count);

    NSUInteger numberOfSpotsToCheck = ((NSUInteger)(arrayOfSomeObjects.count / 4));
    for (int i = 0; i < numberOfSpotsToCheck; i++) {
        u_int32_t randomIndex = arc4random_uniform((u_int32_t)arrayOfSomeObjects.count);
        SomeObject *o = (SomeObject *)[arrayOfSomeObjects objectAtIndex:randomIndex];
        NSLog(@"Object at index: %d \n  description is %@ \n stringProprerty is %@ \n numericProperty is %i",
              i,
              o,
              o.stringProperty,
              [o.numericProperty unsignedIntValue]);
    }
}

在访问和过滤方面,阻止时间仍然有点慢。

2015-05-13 16:10:26.750 TestingPerformance[62201:5810268] predicateTime = 0.086866
2015-05-13 16:10:26.750 TestingPerformance[62201:5810268] predicate time to do stuff = 0.531776
2015-05-13 16:10:26.751 TestingPerformance[62201:5810268] blockTime = 0.107424
2015-05-13 16:10:26.751 TestingPerformance[62201:5810268] block time to do stuff = 0.684613