Re: Core Data faulting and bindings: recursive KVO notifications?
Re: Core Data faulting and bindings: recursive KVO notifications?
- Subject: Re: Core Data faulting and bindings: recursive KVO notifications?
- From: Dennis Lorson <email@hidden>
- Date: Sat, 29 Mar 2008 03:38:21 +0100
On 28 Mar 2008, at 20:24, Ben Trumbull wrote:
The problem I'm having arises when selecting all images at once
(Command+A), if they are fairly large in number (in my store: ~2000).
This takes an enormous time that is definitely > O(n).
Command-A should be instantaneous, even for selections 10x larger.
The typical performance problems I've seen with array controller
selections are (1) complex KVO observer actions cascade (2) tripping
faults unnecessarily (3) using expensive array controller options.
(1) Complex KVO observer actions need to be carefully constrained.
If they simply grow organically, then it's pretty easy to fall into
the trap of observers creating side effects that trigger other
observers that cascade to yet more observers. This (a) makes your
code impossible to understand, since no one ever intentionally
designed their app that way from the beginning, and (b) sucks for
performance.
This seems like an easy mistake indeed. However, I checked my code
for this when originally debugging the issue, and while it is
difficult to eliminate all cases, no custom KVO observer triggers
other observers as for my subclass implementation -- and if they do,
they relate to disjunct entity objects that should not trigger others.
To investigate this further, I created a very simple Core Data app. I
did the following:
- Create an Employee entity
- add a few (10) properties to it: plain string values.
- create a standard master-detail interface in IB: a tableview and
some textfields.
- add an arraycontroller, set the entity to Employee, bind the
managedObjectContext
- bind a tablecolumn value to arrangedObjects.someEmployeeProperty of
the controller
- bind the textfields to selection.otherEmployeeProperties of the
controller
When plain starting the app with a prepopulated store (10.000
Employees with all their properties set) and selecting all, the app
crashes after a few seconds due to stack overflow.
Again the same pattern as I described before can be observed.
I think I can come up with an explanation now:
- the selection.property1, selection.property2,... values change due
to the table selection change.
- the first observer (random textfield, lets assume the one bound to
selection.property1) gets notified
- through the controller, the property is asked from the model object
employee1
- Core Data fault fires, employee1 is fully fetched
- since employee1.property1 has a changed value (fault->real value),
it notifies all its observers.
- the same textfield receives this notification, and I can only assume
that IT NOW ASKS THE NEXT EMPLOYEE for the keyPath, as it tries to
assemble its multiple values set from the different employees.property1
- employee2.property1 is now asked for its value, faulted, sends
notification, textfield asks for employee 3.
etc...until stack overflow.
The test project can be found on http://users.telenet.be/dlo/CDPerformance.zip
The good news is that the issue is entirely avoidable by batch
faulting using the 10.5 [NSFetchRequest setReturnsObjectsAsFaults:NO]
and executing the fetch on all objects in the selection.
But still, I have two questions:
- Am I overlooking something obvious? If it is the correct approach,
someone else must have experienced this in a larger master-detail set,
right?
- While I understand the constraints the CD API imposes, shouldn't
this be labeled as a bug? I'm not sure here, if so, I'll file it with
bugreporter.
It's often easier to understand, and much faster, to use
NSNotificationCenter to defer and coalesce observations for a batch
operation like operating upon 2000 objects. One way to achieve this
with the array controller is to remove all the objects from the
controller, do your batch operation, and then add them back.
I agree, it's something I apply when importing large sets of data
(stacks of 1000 image objects at once along with 20+ metadata
properties per image). Not doing it causes the observers to go crazy
trying to refresh their data, although not recursively this time.
(2) Firing faults unnecessarily is the canonical performance issue
for Core Data developers. Our SQL logging (-
com.apple.CoreData.SyntaxColoredLogging 1 -
com.apple.CoreData.SQLDebug 1) and our Instruments template can help
you find this, and identify which entities you're faulting
excessively.
If you need that data, it's much (10-100x) better to batch fetch the
data instead. This is described in the Core Data Programming Guide
under Performance.
While I'm certain to perform some optimization here, the delay I was
having was almost fully due to the issue above.
It is indeed very useful, but in my case mostly to avoid the critical
stack overflow. I will not be having 100.000+ stores so the excessive
faulting has still little impact.
Regarding the call into _didChangeValuesForKeys:, the API contract
for KVO requires Core Data call -willChange/-didChange during
faulting. This is pretty undesirable.
You can say that ;-)
If it's actually recursing through the entire graph, then it's
probably a bug in your observer method. If it's looping, then it
could simply be toggling off a few array controller options will
address the issue.
You should file a bug with bugreport.apple.com, and include the
entire stack trace. Without the rest of it, it's hard for me to say
if this is the expected stack depth or not. It may be a problem
with one of your custom observation methods not coexisting happily
with faulting, but it's a bit hard to tell from this excerpt.
As you can verify from the test project, I don't use any custom KVO
logic. Still, the crash should occur almost every time, after a
variable amount of time (seconds range).
I'll file a bug if the issue is determined to be not on my part.
Thanks for your kind help with this,
Dennis
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden