我有几千个内存中的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
*/
}
}