使用NSInMemoryStoreType时可以使用Core Data索引存储?

时间:2012-01-11 05:36:20

标签: objective-c ios cocoa core-data

我有几千个内存中的NSDictionary实例(包含字符串和数字)的NSArray,我需要在运行时执行任意查询。使用filteredArrayUsingPredicate会产生令人无法接受的性能。我可以在每个字段上手动构建索引并访问这些字典,但我认为仅使用索引属性构建动态内存中Core Data模型,将NSDictionary实例转换为NSManagedObjects,然后执行查询可能更简单NSFetchRequests。

不幸的是,NSInMemoryStoreType模型似乎不尊重NSAttributeDescription的“索引”属性:针对Core Data模型的查询比在字典数组上执行旧的filteredArrayUsingPredicate花了大约50%的时间。获取NSInMemoryStoreType模型以创建内存中索引是否有一些技巧,或者该属性是否被忽略?使用SQLite存储不是此应用程序的选项,因为属性的类型经常更改。

这是我用来比较两种不同搜索机制的性能的代码:

- (void)testInMemoryCoreDataEfficienctQuery {
    static const NSInteger InstanceCount = 5000; // the number of instances to test

    static NSString *EntityName = @"EntityPerformanceTest";
    static NSString *AttributeName = @"attrName";
    static NSString *PredicateVariable = @"predicateVariable";

    NSError *error = nil;
    NSManagedObjectContext *moc;
    NSEntityDescription *entity;
    {
        NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] init];
        {
            NSMutableArray *entities = [NSMutableArray array];
            entity = [[NSEntityDescription alloc] init];
            entity.name = EntityName;

            NSMutableArray *attrs = [NSMutableArray array];
            {
                NSAttributeDescription *attr = [[NSAttributeDescription alloc] init];
                attr.name = AttributeName;
                attr.attributeType = NSStringAttributeType;
                attr.indexed = YES; // ideally this would speed up searches on strings
                [attrs addObject:attr];
            }

            entity.properties = attrs;

            [entities addObject:entity];

            mom.entities = entities;
        }
        NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];

        NSPersistentStore *ps = [psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error];
        // NSPersistentStore *ps = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:[[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString randomUUID]] stringByAppendingPathExtension:@"sqlite"]] options:nil error:&error];

        STAssertNotNil(ps, nil);
        STAssertNil(error, @"%@", error);

        moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        moc.persistentStoreCoordinator = psc;
    }
    [moc processPendingChanges];
    [moc save:&error];
    [moc reset];
    STAssertNil(error, @"%@", error);

    // now test searching in a MOC vs. in a collection of dictionaries
    NSMutableArray *strings = [NSMutableArray array];
    NSMutableArray *dicts = [NSMutableArray arrayWithCapacity:InstanceCount];
    {
        for (int i = 0; i < InstanceCount; i++) {
            // create an arbitrary random string we will store and later query against
            CFUUIDRef randomUUID = CFUUIDCreate(NULL);
            NSString *uuidString = (NSString *)CFBridgingRelease(CFUUIDCreateString(NULL, randomUUID));
            CFRelease(randomUUID);

            [strings addObject:uuidString];

            // create the dictionary
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            [dict setValue:uuidString forKey:AttributeName];
            [dicts addObject:dict];

            // create the managed instance
            NSManagedObject *ob = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:moc];
            [ob setValue:uuidString forKey:AttributeName];
        }
    }
    [moc processPendingChanges];

    STAssertEquals([strings count], [[NSSet setWithArray:strings] count], @"strings were not unique");

    NSPredicate *query = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:AttributeName] rightExpression:[NSExpression expressionForVariable:PredicateVariable] modifier:(NSDirectPredicateModifier) type:(NSEqualToPredicateOperatorType) options:(0)];

    for (int iter = 0; iter < 2; iter++) {
        NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:EntityName];
        [fetch setFetchLimit:1];
        [fetch setFetchBatchSize:1];

        // time searching with Core Data
        CFAbsoluteTime mocStart = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < InstanceCount; i++) {
            fetch.predicate = [query predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObject:[strings objectAtIndex:arc4random() % strings.count] forKey:PredicateVariable]];

            NSArray *results = [moc executeFetchRequest:fetch error:&error];
            NSParameterAssert(!error);
            NSParameterAssert(results.count == 1);
        }
        CFAbsoluteTime mocEnd = CFAbsoluteTimeGetCurrent();

        // time searching with dictionaries
        CFAbsoluteTime dictStart = CFAbsoluteTimeGetCurrent();
        for (int i = 0; i < InstanceCount; i++) {
            NSArray *results = [dicts filteredArrayUsingPredicate:[query predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObject:[strings objectAtIndex:arc4random() % strings.count] forKey:PredicateVariable]]];
            NSParameterAssert(results.count == 1);
        }
        CFAbsoluteTime dictEnd = CFAbsoluteTimeGetCurrent();

        NSLog(@"assessed %d queries: moc=%.3f dict=%.3f", InstanceCount, mocEnd - mocStart, dictEnd - dictStart);

        /*
         Core Data seems to be slower, as per these results:

         2012-01-10 21:19:04.247 Glimpse[9151:15503] assessed 5000 queries: moc=19.085 dict=12.186
         2012-01-10 21:19:35.412 Glimpse[9151:15503] assessed 5000 queries: moc=19.001 dict=12.164
         */
    }
}

0 个答案:

没有答案