NSSortDescriptor 的性能如何?

Posted

技术标签:

【中文标题】NSSortDescriptor 的性能如何?【英文标题】:The performance of NSSortDescriptor? 【发布时间】:2011-10-08 15:24:16 【问题描述】:

我有一个大约 4000 行的 SQLite 支持的数据库,连接到 Core Data。该模型是一个简单的概览 -> 详细模型。每个概述(带有标题和副标题)都与包含详细信息的详细信息有关系。为了查看这些数据,我实现了一个带有 NSFetchedRequestController 的 UITableView。

NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"registered_name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sort];
        
NSPredicate *filterPredicate = nil;
NSString *sectionName = nil;
NSString *cacheName = nil;
    
NSFetchedResultsController *aFetchedResultsController = nil;
aFetchedResultsController.delegate = self;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

[fetchRequest setFetchBatchSize:20];
[fetchRequest setSortDescriptors:sortDescriptors];
    
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Info" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
    
[fetchRequest setPredicate:filterPredicate];
aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:sectionName cacheName:cacheName];

NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) 
  NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
  abort();


[fetchRequest release];
[sort release];
return aFetchedResultsController;`

我已经实现了所有的东西,它可以工作,但是工作很慢。具体来说,我关心的是应用程序的启动。

这是降低启动时间的查询(XCode 中的 SQL 调试标志):

CoreData: sql: SELECT 0, t0.Z_PK FROM ZINFO t0 ORDER BY t0.ZREGISTERED_NAME
CoreData: annotation: sql connection fetch time: 0.8773s 
CoreData: annotation: total fetch execution time: 0.8976s for 4302 rows. 

这会在 performFetch 执行后立即发生。为什么 Core Data 觉得需要获取和排序我的数据库的所有 4302 行?如果我将 setLimit 设置为一个较小的值,比如 20,当然一切都运行得非常快,但我只显示了 20 个结果。另一方面,我现在有了 setBatchSize,效果很好,当我滚动表视图时,我可以在调试控制台中看到 SELECT 语句。但它仍然在启动时对 4302 行进行排序,我觉得这不必要地减慢了应用程序的启动时间。

根据 Apple 的 WWDC 2010 示例代码,我打算在 NSSortDescriptor 中实现规范化字符串排序和 caseInsensitiveNumericCompare 选择器,但在我看来,我在这里遗漏了一些简单的东西。

更新:

似乎我不能将自定义选择器 caseInsensitiveNumericCompare: 与 NSSortDescriptor 一起使用,因为我使用 SQLite 作为数据库。我真的不知道我还能做些什么来加快速度。也许 SQLite 会使用缩短和规范化的字符串更快地排序,这就是我接下来要尝试的。

更新 2:

使用标准化字符串(除 A-Z 和 0-9 外没有其他字母或符号),启动时间降至约 0.7 秒。减少幅度不大。我要尝试的最后一件事是对数据库进行预排序,然后将增量 ID 分配给行。然后在 NSSortDescriptor 中,我将按此数字 id 进行排序。根据我的 SQL 测试,它应该快大约 7 倍。

【问题讨论】:

【参考方案1】:

我解决了自己的问题。这只是对可能遇到类似问题的任何人的提醒。 我犯的错误是没有为数据库端的必填字段创建索引。我只是在 XCode 的模型中单击了“索引”选项。如果您自己提供了 .sqlite 文件,显然这不会创建任何索引。

在一个奇怪的启动案例之后,Core Data 决定为我创建数据库,我看到这些 SQL 语句被执行:

2011-10-08 19:49:40.572 App[1717:307] CoreData: sql: CREATE INDEX ZINFO_ZREGISTERED_NAME_INDEX ON ZINFO (ZREGISTERED_NAME)
2011-10-08 19:49:40.586 App[1717:307] CoreData: sql: CREATE INDEX ZINFO_ZFIRSTLETTER_INDEX ON ZINFO (ZFIRSTLETTER)
2011-10-08 19:49:40.598 App[1717:307] CoreData: sql: CREATE INDEX ZINFO_ZNAME_INDEX ON ZINFO (ZNAME)
2011-10-08 19:49:40.610 App[1717:307] CoreData: sql: CREATE INDEX ZINFO_ZN_SL_NAME_INDEX ON ZINFO (ZN_SL_NAME)
2011-10-08 19:49:40.622 App[1717:307] CoreData: sql: CREATE INDEX ZINFO_ZN_REGISTERED_NAME_INDEX ON ZINFO (ZN_REGISTERED_NAME)
2011-10-08 19:49:40.635 App[1717:307] CoreData: sql: CREATE INDEX ZINFO_ZDETAILS_INDEX ON ZINFO (ZDETAILS)

在我将这些索引复制到我自己的数据库上之后,事情已经大大加快了:

CoreData: sql: SELECT 0, t0.Z_PK FROM ZINFO t0 ORDER BY t0.ZREGISTERED_NAME
CoreData: annotation: sql connection fetch time: 0.1315s
CoreData: annotation: total fetch execution time: 0.1568s for 4161 rows.

我对这些数字非常满意。但是由于我现在已经对我的数据库进行了预排序,所以我可以为 NSSortDescriptor 使用一个数字。这让我想到:

CoreData: sql: SELECT 0, t0.Z_PK FROM ZINFO t0 ORDER BY t0.ZID
CoreData: annotation: sql connection fetch time: 0.0677s
CoreData: annotation: total fetch execution time: 0.0890s for 4161 rows.

所以略低于百分之九秒,低于十分之九秒。

【讨论】:

你能分享你的项目吗?我有点好奇看到它

以上是关于NSSortDescriptor 的性能如何?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用一个选择器创建一个按多个键排序的 NSSortDescriptor

具有任意排序的 NSSortDescriptor

创建 NSSortDescriptor

用于核心数据一对多关系的 NSSortDescriptor

使用 NSSortDescriptor 按时间倒序对 CoreData 进行排序

NSSortDescriptor 有两个同样重要的键