当我尝试从服务器发送的数据构建初始数据库时,我遇到了基于CoreData的iOS应用程序的问题。基本上,服务器发送1MB的对象块(每块大约3,000个),iOS客户端对它们进行反序列化并将它们写入磁盘。
我所看到的是,对于前8个块(44个)中的一切都进展顺利,然后性能急剧下降,每个块开始花费的时间越来越长,如下图所示。几乎所有的时间都在[NSManagedObjectContext save]
消耗,正如您在仪器分析数据中看到的那样,但似乎应用程序由于某种原因不再以100%的CPU运行,就像在磁盘上等待I / O或者什么。
关于我是如何做到这一点的一些重要事实:
每个块都在其自己的NSManagedObjectContext
中使用自己的NSAutoreleasePool
进行处理,因此在处理块之间的非刷新上下文中没有对象生成。
在任何情境中都没有设置NSUndoManager
。
没有mergeChangesFromContextDidSaveNotification:
正在进行(即块上下文没有将其更改推送到“主”上下文中)
我在iOS 4.3上使用基于SQLite的数据存储。
正在编写的记录上都有索引。
整个同步作业在单个GCD后台线程上处理(即dispatch_queue_create()
和dispatch_async()
)。
我不知道为什么性能会突然降低,或者可以采取什么措施来解决它。我已经四处寻找并阅读了以下内容,但是我还没有跳出来:
使用此应用程序在数据库中扩展到100,000条记录的任何想法或指示将非常感激。
此仪器图表显示与上面相同的模拟(在iPad2上),但包括磁盘活动统计数据,您可以清楚地看到所有“未运行在100%CPU”的时间似乎都被写入磁盘。
我还在iOS模拟器上运行了相同的同步尝试。除了包含随时间略微增长的对象ID的字典外,每个块的总体内存使用量或多或少是恒定的(但这些不是CoreData对象或任何会影响保存的东西,它们只是NSNumbers)。与总堆相比,这个dict是一个少量的内存,所以问题就是内存不足。
此测试的有趣之处在于,CoreData Save仪器报告连续保存所花费的时间大致相同,这显然与第一组结果中的CPU分析信息冲突。似乎CoreData认为将更改推送到数据库需要花费相同的时间,但是数据库本身(即SQLite)突然需要更长时间才能将这些更改实际流式传输到磁盘。
答案 0 :(得分:7)
我知道这是一个老问题,所以这可能不再适用于您,但可能对其他人而言。
我已经看到通过iCloud播种核心数据数据库的性能问题,并发现如果您在数据模型上存在反向关系,那么您可能会在性能上非常糟糕地受到伤害。 iCloud事务日志记录的实现方式,实际上似乎是一个不可避免的问题。发送到iCloud的每个事务(在developer.icloud.com上查看它们 - 它们只是压缩了plist)记录了受更改影响的每个关系。与在Core Data中修改关系的一端并且负责反向结束时不同,核心数据事务日志结束时记录两个端点的更改,而不是将其解决。
因此,如果你有1对多的关系,并且你创建了另一条记录,最终会挂掉'很多'端 - 那么'1'端的记录也会更新,以反映一个新的附加事实记录现在挂了它。如果你的架构意味着你有一个“类型”对象,许多“数据”对象都会挂起,那么每次添加一个新的数据对象时,类型一个也会为它编写一个事务 - 但是这是踢球者,因为iCloud核心数据事务记录了已编辑实体的整体状态,而不仅仅是更改,已经记录的关系也被添加到日志中,而不仅仅是指示新下级记录的那个。随着实体之间关系数量的增加,随着写入数据量的增加,这可能会迅速失控 - 最终需要更长时间才能保存批次。
我在Apple开发论坛上here之前回答了一个类似这样的问题,这可能很有用,因为我似乎无法简洁地描述这一点。
如果这种情况影响你,那么提高播种性能的最简单方法是关闭反向关系,但这并不总是一种选择。
答案 1 :(得分:1)
有关您的实施的更多信息会有所帮助。例如,您是在主线程上运行它还是实现后台线程?但是,我以前见过这种行为。使用Core Data执行大量批处理操作时,如果没有正确管理内存,它可能会变慢。你检查过内存使用情况吗?你检查过泄漏了吗?另一件要尝试的是确保在需要时正确使用NSAutoreleasePool。通过定期排空池,这可能有助于提高性能。