On 17-Apr-10, at 7:01 AM, Ken Thomases wrote:

On Apr 16, 2010, at 4:05 PM, Ben Haller wrote:

So I'm keeping an "unused pool" of these objects, and when I'm done with an object I throw it into the pool, and when I need a new one I grab one from the pool. I do these operations with inline functions, so I can get a new object instance very quickly indeed. This works great, and I've been doing it for a while. Of course - init only gets called when the object is truly allocated, the first time around.

Actually, I would think you'd be better off treating an unused object as truly deallocated and then you'd want to re-init it when it is allocated again.

In other words, I'd think you would want a memory pool, not an object pool. You should be able to implement that by overriding +allocWithZone: and -dealloc to use a custom allocator instead of falling through to either super's implementation or NSAllocateObject/ NSDeallocateObject.

Your +allocWithZone: would reuse memory from your pool in preference to allocating it anew. Then, it would set the 'isa' ivar and bzero the rest. It would -retain the object before returning it. Your - dealloc would just return the object's memory to the pool. All done.

An interesting idea, but would be much slower than my scheme. Four method calls (release, dealloc, alloc, retain) per recycled object, instead of a few lines of inline code.

That said, if you really want to go with the object pool rather than the memory pool, there's no particular reason to be especially careful about the use of -init. An object all of whose ivars other than 'isa' are zeroed is indistinguishable from one which hasn't been init-ed. So, there's no particular reason to avoid calling - init on it each time it is reused. The -init method isn't magical nor mysterious. It's just a normal method like any other, but with some conventions surrounding its use.

Not a big deal. Just thought I'd point it out, since you made the point about only calling -init once.

Well, this makes assumptions about what NSObject is doing in its ivars, no? If I bzero NSObject's ivars, I might be stomping something that I'm not allowed to stomp. If I don't bzero NSObject's ivars, but do call NSObject's -init twice for the same object, that could also violate the design of NSObject; in fact I am fairly certain that I have read that you are not allowed to do that, but I can't seem to find it in the docs now. I think this is a flaw in Paul Sanders' scheme, too, now that I think about it; assigning from an inited object blows away whatever was in NSObject's ivars, which really doesn't seem OK. In the current implementation it may be just the isa pointer, IIRC, but there is no reason to assume that will always be the case. If I avoided calling NSObject's -init, then yes, there is nothing "magical nor mysterious" about -init. :-> But that would mean having two sets of -init methods, one for the "original" case, and one for the "reuse" case.

The question is how to zero out the ivars correctly. I have the Class of the object I'm reusing, and I can do the math ahead of time once. But the class is not fixed at compile time; it depends upon choices the user makes at runtime. So I have to use the Objective-C runtime to find my ivar block and zero it out. The first ivar in the class is known, because it is defined by the common superclass of all of these objects; it's called "pedigree". So what I'm thinking of doing is:

1. individualIvarsOffset = ivar_getOffset(class_getInstanceVariable(individualClass, "pedigree"));

2. individualIvarsSize = class_getInstanceSize(individualClass) - individualIvarsOffset;

3. bzero(individual + individualIvarsOffset, individualIvarsSize);

Step one gets the offset of the known first instance variable. This seems safe to me as long as the ivar layout is guaranteed not be shuffled around arbitrarily; i.e. as long as variables occur in memory in the order in which they are declared in the header file. Is that a guarantee that Obj-C gives, or not?

Not quite, but I don't think you need it. (The non-fragile instance variable implementation, as well as truly synthesize declared properties, require flexibility in the order of instance variables.)

First, your classes are direct subclasses of NSObject. NSObject is documented to only have the 'isa' instance variable. So, you don't need to find your first instance variable, you only need to avoid NSObject's isa ivar. You can zero out everything else.

Oh, if it is guaranteed to only have "isa" then that does simplify things; never mind what I say above, then. Where is that documented?

The guarantee you do get is that the superclass's instance variables are all before the subclass's. This would be most people's expectation; it's also implicit in the design of runtime API. For example, if you get an 'Ivar' reference from class_getInstanceVariable(), then its offset, as given by ivar_getOffset(), can only be dependent on the class passed into the former. There's no way for the runtime to provide a different offset for subclasses.

Step two calculates the size of the ivars block that I own (i.e. not NSObject's ivars, which I certainly don't want to touch; just the ones for my subclasses) as the total instance size minus the offset of the known first instance variable. This seems safe given the same caveat as step one, plus the added caveat that no extra stuff can be added to the object's memory block at the end; everything from my first ivar to the end of the malloced block must belong to me. Again, I'm not sure if this is a guarantee Obj-C gives...?

Well, there's the weird, rarely-if-ever used extraBytes parameter to NSAllocateObject()/class_createInstance(). It's accessible with object_getIndexedIvars(). But, still, you're safe because it's not included in class_getInstanceSize(). How could it be since it's a per-instance thing while class_getInstanceSize() only takes the class?

And, as I say, since you're guaranteed that the instance variables of the superclass precede those of the subclass, your calculation can be class_getInstanceSize(individualClass) - class_getInstanceSize([individualClass superclass]). (Alternatively, you can specify [NSObject class] in that last call, given that you're deriving directly from NSObject.)

Furthermore, you can use class_getInstanceSize([individualClass superclass]) as the value of individualIvarsOffset, too. As I say, you don't really care about specific ivars, you just care about blocks of ivars belonging (or not) to a given class.

  Ah, I like this better than my code.  Very good, thanks.

So, thoughts? Am I insane? Is the above scheme safe? Is there a better way? Thanks for any feedback!

Not insane, although, as Graham suggested, hopefully you've measured and have good reason to believe your scheme is really faster than the built-in allocator.

  I have.

Also, how do you know when your objects are "free" to be reused? With the built-in allocator or the memory pool approach, you know you're not reusing objects while they're still in use somewhere.

The objects are owned by a single object in my app. When that object is done with them, it knows, and it throws them back into the unused pool. If someone else kept a reference to it, then yes, there would be a problem; but that does not happen, and it would be a violation of the architecture.

Lastly, if your objects are so simple (direct subclasses or NSObject, no pointer ivars, etc.), have you considered using plain C structs instead of objects? Or C++ objects and collections? I'm not saying you necessarily should use those, but you should consider them if performance is so critical and you're bending over backward to work against the normal Cocoa way.

Yeah, I thought about that a lot, actually. It's a weird situation, but I really do want and use inheritance and dynamic message dispatch and so forth. It only comes into play a few times in the entire lifetime of each individual object, but at those few crucial junctures, it would be a huge PITA to be doing things the struct way. C++ hadn't occurred to me, but I'm not fond of the language and don't really want to open that can of worms; to me, that would be uglier than what I'm doing now, in fact. Is there a reason that I'm missing, why that would be a good solution?

Ben Haller
McGill University

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to