简体   繁体   English

如何提高iPhone上的核心数据获取性能?

[英]How can I improve core data fetch performance on the iPhone?

Core data's performance on the iPhone is absolutely miserable. 核心数据在iPhone上的表现绝对是悲惨的。 Is indexing completely broken or is it just a poor implementation? 索引是完全破坏还是只是一个糟糕的实现?

I have about 21500 objects of a single type in my core data store (SQLite backing store). 我的核心数据存储(SQLite后备存储)中有大约21500个单一类型的对象。 The objects are indexed on a UUID which is an NSString (for example, one looks like this: "6b09e200-07b6-11df-a245-002500a30d78"). 对象在UUID上索引,UUID是NSString(例如,一个看起来像这样:“6b09e200-07b6-11df-a245-002500a30d78”)。

A single fetch where the object exists using executeFetchRequest in NSManagedObjectContext takes about 0.75 seconds! 使用NSManagedObjectContext中的executeFetchRequest对象存在的单个提取大约需要0.75秒! This is with the simplest possible predicate of "uuid == $UUID" where $UUID is a string like the example above. 这是最简单的谓词“uuid == $ UUID”,其中$ UUID是一个类似上面例子的字符串。

This is really surprising. 这真的很令人惊讶。 If I wanted to fetch every object in my store, one by one, it would take nearly 4.5 hours! 如果我想逐个获取商店中的每个对象,则需要将近4.5小时!

Is there anyway to improve this performance or should I just abandon core data all together? 反正有没有改善这种性能,还是我应该放弃核心数据?

Several points. 几点。 If it takes 5 seconds to fetch 21,500 rows, it sounds like you're running on an older device. 如果需要5秒钟才能获取21,500行,这听起来就像是在旧设备上运行。 Like a 3G or original iPhone. 像3G或原始iPhone。 The memory and I/O performance on those is just plain slow. 这些内存和I / O性能非常慢。 You'll need to handle your data with extreme care to avoid reading it all into memory and doing unnecessary I/O. 您需要非常谨慎地处理数据,以避免将其全部读入内存并进行不必要的I / O. You may find -setFetchBatchSize particularly useful. 您可能会发现-setFetchBatchSize特别有用。 If you're running on a 3GS, 10-20 thousand rows is manageable but will require care. 如果你在3GS上运行,可管理10-20万行,但需要小心。 If you're on an ipad or iphone4, this shouldn't be much of an issue. 如果您使用的是ipad或iphone4,这应该不是什么大问题。

You don't need to create your own UUID, except to interface with an external system, like a server. 除了与外部系统(如服务器)连接外,您不需要创建自己的UUID。 Each Managed Object has an objectID which is an OOP representation of its primary key. 每个受管对象都有一个objectID,它是其主键的OOP表示。 Just pass the objectID around, and do queries like @"self = %@" or @"self IN %@" to search for an object by its ID or an array of ids. 只需传递objectID,然后执行@“self =%@”或@“self IN%@”等查询,按ID或ID数组搜索对象。 You can also use -existingObjectWithID:error: to look up just 1 object by its objectID which will be faster than a generic fetch request with a generic predicate. 您还可以使用-existingObjectWithID:error:仅通过其objectID查找1个对象,该对象比具有通用谓词的通用提取请求更快。

The best way to verify the index is being used as you expect is to run the app in the simulator with the executable argument 正如您所期望的那样,正在使用验证索引的最佳方法是使用可执行参数在模拟器中运行应用程序

-com.apple.CoreData.SQLDebug 1 -com.apple.CoreData.SQLDebug 1

that will log to console the SQL being generated. 这将记录控制台生成的SQL。 You should see some stuff ending in something like t0.uuid == ? 你应该看到一些东西以t0.uuid ==结尾?

You can take that SQL select statement, and run it through SQLite's explain query facility. 您可以使用该SQL select语句,并通过SQLite的explain查询工具运行它。 Run /usr/bin/sqlite3 against the db file in the simulator. 对模拟器中的db文件运行/ usr / bin / sqlite3。 Do

.explain ON explain query plan copythatsqllinehere .explain ON解释查询计划copythatsqllinehere

it should print out something like 0|0|TABLE ZFOO AS t0 WITH INDEX something 它应该打印出类似0 | 0 |表ZFOO AS t0 WITH INDEX的东西

if it's missing "with index" then you have some issue with either the way you created the Core Data store (are you sure the model is marked to index uuid ?) or there's something else with your fetch request. 如果它缺少“带索引”那么你在创建核心数据存储的方式上有一些问题(你确定模型被标记为索引uuid吗?)或者你的获取请求还有其他的东西。

This is really surprising. 这真的很令人惊讶。 If I wanted to fetch every object in my store, one by one, it >would take nearly 4.5 hours! 如果我想逐个获取商店中的每个对象,它将需要将近4.5个小时!

I suppose you could do it that way, as one of the most painful ways possible. 我想你可以这样做,作为最痛苦的方式之一。 Or you could use -setFetchBatchSize: and iterate over batches of the objects very quickly. 或者您可以使用-setFetchBatchSize:并快速迭代批量对象。

Also, keep in mind that each fetch does I/O with the database to remain in sync with what any other threads have saved. 另外,请记住,每次提取都会对数据库执行I / O操作,以保持与其他任何线程保存的内容同步。 Fetching is not some magical dictionary look up. 提取不是一些神奇的字典查找。 There's a lower bound on the time it takes to execute the smallest unit of I/O. 执行最小的I / O单元所需的时间有一个下限。 You're going to want to amortize the number of individual I/O requests to get the best performance. 您将要分摊单个I / O请求的数量以获得最佳性能。 You'll have to balance that against reading too much into memory at once. 你必须平衡这一点,而不是一次读入太多内存。

If you continue to have trouble, please file a bug with bugreport.apple.com 如果您仍然遇到问题,请向bugreport.apple.com提交错误

  • Ben

I think the problem is comparing strings is much slower then comparing numbers for most if not all database. 我认为问题是比较字符串要慢得多,然后比较大多数(如果不是全部)数据库的数字。

You can try to add a new property (column), aNumber, for your NSManagedObject, which is a number and value is generated from it's UUID. 您可以尝试为NSManagedObject添加新属性(列),aNumber,这是一个数字,并从其UUID生成值。

Then, build your query like "aNumber == XXX AND uuid == UUID" 然后,构建您的查询,如“aNumber == XXX AND uuid == UUID”

This can make the database to compare a number first and it only has to compare a string if the number is same. 这可以使数据库首先比较一个数字,如果数字相同,它只需要比较一个字符串。

OR, you can try to index the UUID. 或者,您可以尝试索引UUID。

This isn't going to answer your question but might give you something to think about. 这不会回答你的问题,但可能会给你一些思考。 Using just SQLite on the iPhone I was mightily disappointed with performance. 在iPhone上使用SQLite我对性能感到非常失望。 I was dealing with about 8000 entries which would take about a two minutes to insert / sort if returning all and so on. 我正在处理大约8000个条目,如果全部返回则需要大约两分钟来插入/排序等等。

Playing around with it I found the time needed to filter / sort in memory was 100 fold better than letting it be done SQLite and I think its mainly due to the performance of the flash memory. 玩弄它我发现在内存中过滤/排序所需的时间比让SQLite完成要好100倍,我认为这主要是由于闪存的性能。

In short the less Core Data has to use the flash memory the better performance you will get and I don't think there would be many ways to make it much better. 简而言之,Core Data必须使用更少的闪存才能获得更好的性能,而且我认为没有太多方法可以让它变得更好。

the trick of using core data is that only data which are actually needed are fetched from store and held in memory. 使用核心数据的技巧是,只有实际需要的数据才能从存储中获取并保存在内存中。 I can't imagine how would I edit/reorder/whatever 21500 rows on a device like an iPhone. 我无法想象如何在像iPhone这样的设备上编辑/重新排序/任何21500行。 There are several ways how to improve CoreData performance: - setFetchBatchSize - using primitive methods - loading only properties which are needed 有几种方法可以提高CoreData的性能: - setFetchBatchSize - 使用原始方法 - 只加载需要的属性

I remember a WWDC video comparing SQLite & CoreData performance and CD was a clear winner. 我记得比较SQLite和CoreData性能的WWDC视频和CD是一个明显的赢家。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM