Ben Ford

benford.me

Optimizing CoreData Queries

Most simple apps will never deal with slow Core Data data reads, but with a complex data model, or where the data largely defines the UI, getting super fast queries can be a rewarding challenge.

Multi-threading CoreData will certainly give the appearance of speed, as it won’t block the main thread and this won’t speed up a slow query. There is a subtle trick which will massively speed up queries. This technique isn’t obvious, and is probably the biggest secret about core data performance!

Many Requests == Lots of Overhead

From the documentation:

Each round trip to the persistent store (each fetch) incurs an overhead, both in accessing the store and in merging the returned objects into the persistence stack. You should avoid executing multiple requests if you can instead combine them into a single request that will return all the objects you require.

Filter those arrays

This tiny blurb doesn’t do the concept justice. The “overhead” is very large; especially if you’re making many requests with the intent to query the datastore with different NSPredicates. It can be 2x or 3x faster to make one fetch request and filter the resulting array using filteredArrayUsingPredicate:.

An Example

For example, say you have two entities in your sqlite-backed data store: “Meeting” and “Type”. Your goal is to display “meetings” grouped by “Type”.

The most obvious procedure would be:

  • Fetch all “Type” objects into an array.
  • Loop through the array.
  • Fetch each set of “Meeting” objects using NSFetchRequest coupled with a NSPredicate.
  • Hand off the results.

This seems correct, as it offloads the predicate query onto the sqlite datastore, but this is actually the slowest way to fetch data.

Fast method (the difference is subtle):

  • Fetch all “Type” objects into an array.
  • Fetch all the “Meeting” objects into an array.
  • Loop through the array.
  • Filter the “Meeting” objects using the NSPredicate and filteredArrayUsingPredicate:
  • Hand off the results.

The difference is your making one NSFetchRequest(the slow part), and many filteredArrayUsingPredicate: calls on the array. You’re essentially querying “in memory”.

Conclusion

It’s counterintuitive, but “filtering arrays” is much much faster than “querying the datastore”. The overhead of making multiple fetch requests far outweighs any benefit the database can give you.

Other Useful Links

Pulse has a good overview of performance problems with Core Data.

Also, Why Brent Simmons switched away from Core Data, but relax, he still encourages it’s use.