Send Cocoa-dev mailing list submissions to
email@hidden
To subscribe or unsubscribe via the World Wide Web, visit
http://lists.apple.com/mailman/listinfo/cocoa-dev
or, via email, send a message with subject or body 'help' to
email@hidden
You can reach the person managing the list at
email@hidden
When replying, please edit your Subject line so it is more specific
than "Re: Contents of Cocoa-dev digest..."
Today's Topics:
1. Releasing ivars in -didTurnIntoFault. Should set to nil?
(Jerry Krinock)
2. Re: Adding more and more key/value observers is much too slow
- workaround needed. (Chris Kane)
3. Re: Adding more and more key/value observers is much too slow
- workaround needed. (Chris Kane)
4. Creating new managed object on 10.4 problem (Colin Cornaby)
----------------------------------------------------------------------
Message: 1
Date: Wed, 12 Aug 2009 11:41:05 -0700
From: Jerry Krinock <email@hidden>
Subject: Releasing ivars in -didTurnIntoFault. Should set to nil?
To: Cocoa Developers <email@hidden>
Message-ID: <email@hidden>
Content-Type: text/plain; charset=US-ASCII; format=flowed; delsp=yes
I release a "regular" instance variable in my managed object's -
didTurnIntoFault, and discovered that this was causing a crash
sometime after an undo. In searching code on the internet I see that
others have reported such crashes. I solved the problem by setting
the instance variable to nil:
- (void)didTurnIntoFault {
[childrenTemp release] ; childrenTemp = nil ;
[super didTurnIntoFault] ;
}
Now I understand that if nilling an instance variable after releasing
it is done in -dealloc, it is papering over other memory management
problems and is therefore bad programming practice. But I believe
that this practice is OK in -didTurnIntoFault because, particularly
when Undo is involved, -didTurnIntoFault may be invoked more than once
on an object. Therefore nilling after releasing in -didTurnIntoFault
is recommended.
Does anyone disagree?
Sincerely,
Jerry Krinock
------------------------------
Message: 2
Date: Wed, 12 Aug 2009 11:44:52 -0700
From: Chris Kane <email@hidden>
Subject: Re: Adding more and more key/value observers is much too slow
- workaround needed.
To: Andy Lee <email@hidden>
Cc: "Cocoa-Dev \(Apple\)" <email@hidden>
Message-ID: <email@hidden>
Content-Type: text/plain; charset=iso-8859-1; format=flowed; delsp=yes
On Aug 12, 2009, at 11:15 AM, Andy Lee wrote:
On Aug 12, 2009, at 11:37 AM, Christopher Kane wrote:
Don't pass the observer as the context: argument; pretty much
anything else is better.
Out of curiosity -- why would this affect performance and why is
this a bad idea (other than performance)? Isn't the context:
treated as an opaque pointer by KVO?
--Andy
Well, as anyone can see from sampling the test program, a lot of time
is being spent in hash table management, and as I can tell through
experience, there are a lot of probe collisions happening. You are
adding many observers into the KVO subsystem, and perhaps KVO is
keeping hash tables of these registrations.
[modelObject addObserver:observer
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld
context:observer];
Now here is where we enter the realm of the hypothetical. Suppose
that the hashing is based on some combination of the observer, key
path, options, and context, which would be reasonable. Suppose (and I
checked and this isn't the case, but this is easy to understand), that
the hash code computation goes something like this:
NSUInteger computeHash(...args...) {
return (NSUInteger)((uintptr_t)observer - (uintptr_t)context +
[keyPath hash] - (uintptr_t)options);
}
Now, the keyPath and the options are the same for every registration,
so they aren't contributing anything [in the test program] to make the
hash codes for each registration different. If the observer and
context are the same, obviously in my contrived example, they are
cancelling each other out. Thus my -hash would always return the same
value for all the registrations, which is terrible for hash table
performance.
Now I can see that what is going on is not actually full degenerate
linear probing, so the interaction between observer and context must
be more subtle in the computation of any hash code. How did I know
that context being the observer was the issue? Try it! Just changing
it to NULL makes an enormous difference. So there must be some sort
of interaction there. In this contrived example, the observers and
modelObjects are likely to be fairly linearly allocated from out of
the heap, and probably all pretty close together, which might be one
effect, and so the bit pattern of the "contexts", which can only be
used as a raw pointer/integer value in any hash code computation,
would be relatively similar.
However, using the address of static data would be better than NULL,
to avoid the likelihood of collisions with other possible KVO
registrants on that object.
I don't know any reason for not passing observer as the context: other
than it seems to avoid this performance corner-case. In the context
(no pun intended) of the observeValueForKeyPath:... method, of course,
you don't *need* to pass observer as the context because the observer
is already "self".
Chris Kane
Cocoa Frameworks, Apple
Passing NULL (just, for example) flattens the curve quite a bit.
If you have nothing better to pass in, put this in your code and
pass the address of it:
static char _xyzzy_ = 0;
Chris Kane
Cocoa Frameworks, Apple
On Aug 3, 2009, at 6:51 AM, Andreas Känner wrote:
Hi,
If you add more and more key/value observers with
addObserver:forKeyPath:options:context: this call takes longer and
longer in a non-linear way.
I posted a bug report today (ID: 7112953) but maybe someone
already has a workaround. Here is my sample code:
#include <Foundation/Foundation.h>
@interface ModelObject : NSObject
{
NSString* title;
}
@property (readwrite, copy) NSString* title;
@end
@implementation ModelObject
@synthesize title;
@end
@interface Observer : NSObject
{}
@end
@implementation Observer
@end
void addObservers(NSUInteger count)
{
// Create model objects and observers:
NSMutableArray* modelObjects = [NSMutableArray
arrayWithCapacity:count];
NSMutableArray* observers = [NSMutableArray
arrayWithCapacity:count];
for(NSUInteger i=0; i<count; i++)
{
[modelObjects addObject:[[ModelObject alloc] init]];
[observers addObject:[[Observer alloc] init]];
}
// Register observers:
NSDate* startDate = [NSDate date];
for(NSUInteger i=0; i<count; i++)
{
ModelObject* modelObject = [modelObjects objectAtIndex:i];
Observer* observer = [observers objectAtIndex:i];
[modelObject addObserver:observer
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld
context:observer];
}
NSLog(@"time to add %d observers: %f s", count, -[startDate
timeIntervalSinceNow]);
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool* pool = [NSAutoreleasePool new];
addObservers(10);
addObservers(100);
addObservers(1000);
addObservers(10000);
[pool release];
}
In my test case an observer does always observe only one object
and each observed object is only observed by one observer. I think
this is a common use case.
I have results for Snow Leopard too, but I think I'm not allowed
to post them on this mailing list. Here are the results for
Leopard:
I tested this with different build settings (with/without GC and
32/64bit)
Machine: iMac 2.4 GHz Intel Core 2 Duo
Leopard (10.5.7/9J61):
32Bit:
With GC:
time to add 10 observers: 0.001008 s
time to add 100 observers: 0.001796 s
time to add 1000 observers: 0.174864 s
time to add 10000 observers: 10.746330 s
Without GC:
time to add 10 observers: 0.000920 s
time to add 100 observers: 0.000956 s
time to add 1000 observers: 0.056894 s
time to add 10000 observers: 4.615175 s
64Bit:
With GC:
time to add 10 observers: 0.001340 s
time to add 100 observers: 0.001533 s
time to add 1000 observers: 0.125700 s
time to add 10000 observers: 7.545702 s
Without GC:
time to add 10 observers: 0.001058 s
time to add 100 observers: 0.000831 s
time to add 1000 observers: 0.053189 s
time to add 10000 observers: 4.006754 s
Notes:
If you know a workaround for this bottleneck please drop me an
email.
_______________________________________________
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
_______________________________________________
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
------------------------------
Message: 3
Date: Wed, 12 Aug 2009 11:44:52 -0700
From: Chris Kane <email@hidden>
Subject: Re: Adding more and more key/value observers is much too slow
- workaround needed.
To: Andy Lee <email@hidden>
Cc: "Cocoa-Dev \(Apple\)" <email@hidden>
Message-ID: <email@hidden>
Content-Type: text/plain; charset=iso-8859-1; format=flowed; delsp=yes
On Aug 12, 2009, at 11:15 AM, Andy Lee wrote:
On Aug 12, 2009, at 11:37 AM, Christopher Kane wrote:
Don't pass the observer as the context: argument; pretty much
anything else is better.
Out of curiosity -- why would this affect performance and why is
this a bad idea (other than performance)? Isn't the context:
treated as an opaque pointer by KVO?
--Andy
Well, as anyone can see from sampling the test program, a lot of time
is being spent in hash table management, and as I can tell through
experience, there are a lot of probe collisions happening. You are
adding many observers into the KVO subsystem, and perhaps KVO is
keeping hash tables of these registrations.
[modelObject addObserver:observer
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld
context:observer];
Now here is where we enter the realm of the hypothetical. Suppose
that the hashing is based on some combination of the observer, key
path, options, and context, which would be reasonable. Suppose (and I
checked and this isn't the case, but this is easy to understand), that
the hash code computation goes something like this:
NSUInteger computeHash(...args...) {
return (NSUInteger)((uintptr_t)observer - (uintptr_t)context +
[keyPath hash] - (uintptr_t)options);
}
Now, the keyPath and the options are the same for every registration,
so they aren't contributing anything [in the test program] to make the
hash codes for each registration different. If the observer and
context are the same, obviously in my contrived example, they are
cancelling each other out. Thus my -hash would always return the same
value for all the registrations, which is terrible for hash table
performance.
Now I can see that what is going on is not actually full degenerate
linear probing, so the interaction between observer and context must
be more subtle in the computation of any hash code. How did I know
that context being the observer was the issue? Try it! Just changing
it to NULL makes an enormous difference. So there must be some sort
of interaction there. In this contrived example, the observers and
modelObjects are likely to be fairly linearly allocated from out of
the heap, and probably all pretty close together, which might be one
effect, and so the bit pattern of the "contexts", which can only be
used as a raw pointer/integer value in any hash code computation,
would be relatively similar.
However, using the address of static data would be better than NULL,
to avoid the likelihood of collisions with other possible KVO
registrants on that object.
I don't know any reason for not passing observer as the context: other
than it seems to avoid this performance corner-case. In the context
(no pun intended) of the observeValueForKeyPath:... method, of course,
you don't *need* to pass observer as the context because the observer
is already "self".
Chris Kane
Cocoa Frameworks, Apple
Passing NULL (just, for example) flattens the curve quite a bit.
If you have nothing better to pass in, put this in your code and
pass the address of it:
static char _xyzzy_ = 0;
Chris Kane
Cocoa Frameworks, Apple
On Aug 3, 2009, at 6:51 AM, Andreas Känner wrote:
Hi,
If you add more and more key/value observers with
addObserver:forKeyPath:options:context: this call takes longer and
longer in a non-linear way.
I posted a bug report today (ID: 7112953) but maybe someone
already has a workaround. Here is my sample code:
#include <Foundation/Foundation.h>
@interface ModelObject : NSObject
{
NSString* title;
}
@property (readwrite, copy) NSString* title;
@end
@implementation ModelObject
@synthesize title;
@end
@interface Observer : NSObject
{}
@end
@implementation Observer
@end
void addObservers(NSUInteger count)
{
// Create model objects and observers:
NSMutableArray* modelObjects = [NSMutableArray
arrayWithCapacity:count];
NSMutableArray* observers = [NSMutableArray
arrayWithCapacity:count];
for(NSUInteger i=0; i<count; i++)
{
[modelObjects addObject:[[ModelObject alloc] init]];
[observers addObject:[[Observer alloc] init]];
}
// Register observers:
NSDate* startDate = [NSDate date];
for(NSUInteger i=0; i<count; i++)
{
ModelObject* modelObject = [modelObjects objectAtIndex:i];
Observer* observer = [observers objectAtIndex:i];
[modelObject addObserver:observer
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld
context:observer];
}
NSLog(@"time to add %d observers: %f s", count, -[startDate
timeIntervalSinceNow]);
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool* pool = [NSAutoreleasePool new];
addObservers(10);
addObservers(100);
addObservers(1000);
addObservers(10000);
[pool release];
}
In my test case an observer does always observe only one object
and each observed object is only observed by one observer. I think
this is a common use case.
I have results for Snow Leopard too, but I think I'm not allowed
to post them on this mailing list. Here are the results for
Leopard:
I tested this with different build settings (with/without GC and
32/64bit)
Machine: iMac 2.4 GHz Intel Core 2 Duo
Leopard (10.5.7/9J61):
32Bit:
With GC:
time to add 10 observers: 0.001008 s
time to add 100 observers: 0.001796 s
time to add 1000 observers: 0.174864 s
time to add 10000 observers: 10.746330 s
Without GC:
time to add 10 observers: 0.000920 s
time to add 100 observers: 0.000956 s
time to add 1000 observers: 0.056894 s
time to add 10000 observers: 4.615175 s
64Bit:
With GC:
time to add 10 observers: 0.001340 s
time to add 100 observers: 0.001533 s
time to add 1000 observers: 0.125700 s
time to add 10000 observers: 7.545702 s
Without GC:
time to add 10 observers: 0.001058 s
time to add 100 observers: 0.000831 s
time to add 1000 observers: 0.053189 s
time to add 10000 observers: 4.006754 s
Notes:
If you know a workaround for this bottleneck please drop me an
email.
_______________________________________________
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
_______________________________________________
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
------------------------------
Message: 4
Date: Wed, 12 Aug 2009 11:56:58 -0700
From: Colin Cornaby <email@hidden>
Subject: Creating new managed object on 10.4 problem
To: Cocoa-Dev List <email@hidden>
Message-ID: <email@hidden>
Content-Type: text/plain; charset=US-ASCII; format=flowed; delsp=yes
Hey,
I have a CoreData project with a schema that I layout by hand in a
class (because the schema has to be used in a command line tool.) On
10.5 my code is working perfectly, but on 10.4 whenever my code
attempts to create a new object, I get an exception. According to the
stack trace it looks like CoreData is trying to fulfill the fault on
object creation.
My object creation line looks like this:
rootObject = [NSEntityDescription
insertNewObjectForEntityForName:@"group"
inManagedObjectContext:context];
....and the exception thrown is:
*** Uncaught exception: <NSUnknownKeyException> [<RGProductGroup
0x324060> setValue:forUndefinedKey:]: this class is not key value
coding-compliant for the key files.
...where files is a key defined in the superentity for group.
As I said, on 10.5 this code works perfectly. Does anyone know of any
issues that could cause this?
Thanks,
Colin
------------------------------
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins (at) lists.apple.com
http://lists.apple.com/mailman/listinfo/cocoa-dev
End of Cocoa-dev Digest, Vol 6, Issue 1146
******************************************