Best way to periodically drain the autorelease pool on a long-running background thread?
Asked Answered
A

4

6

In the developer documentation, it says:

If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should periodically drain and create autorelease pools (like the Application Kit does on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.

I was wondering what the best way to do this is. I have several methods I think would work, but don't know which is the "best". I currently have a method that start the thread and keeps it waiting for operations to perform:

- (void)startThread
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    accessoryRunLoop = [NSRunLoop currentRunLoop];

    //Add a dummy port to avoid exiting the thread due to no ports being found
    [accessoryRunLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

    while(accessoryThreadIsRunning)
    {
        //Keep the thread running until accessoryTheadIsRunning == NO
        [accessoryRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }

    [pool release];
}

My options I can think of are:

1) Add a counter in the while(accessoryThreadIsRunning) so that every 50 or 100 times it will drain the autorelease pool and create a new one.

2) Every time I perform a method in that thread (using performSelector: onThread:), I can create an autorelease pool and then release it at the end of the method.

3) Make a timer so that a pool is drained and then created periodically.

I think that option 1 is the best, but would like to know if there is a different way I should be doing this. Thanks!

Antoninaantonino answered 27/9, 2010 at 19:17 Comment(1)
i think there was talk in wwdc 10 about thatSquier
D
4

I'd start with dead simple and just create/drain the pool on every pass through the loop.

If it shows up during performance analysis as a bottle neck, fix it.

Keep it simple until analysis indicates complexity is required.


I just re-read your question and realized something entirely boneheaded in my answer. If you are running a run-loop it should be managing an autorelease pool automatically for you; it should create a pool at the top of the loop and drain it at the end of each pass through the loop.

You only need to cycle one yourself if you have other stuff going on outside of the runloop. Is that the case?

In any case, yes, the pattern is:

 while(...) {
    ... create pool ...
    ... do stuff ...
    ... drain pool ...
 }
Daphie answered 27/9, 2010 at 19:38 Comment(2)
+1 don't forget that an application's main runloop drains the pool after every event. If Apple don't think it's a performance issue, it probably isn't.Searby
Just to be clear, I should be alloc initing a new autorelease pool before the [accessoryRunLoop runMode:], and releasing it after? This is on iPhone, so a non-GC environment.Antoninaantonino
D
3

Drain it each time. As others have said draining an autorelease pool is cheap.

Moreover NOT draining it can be very costly. If you have enough stuff in your autorelease pool to cause paging you cause disk I/O, and disk I/O is literally thousands if not millions of times more costly then running a linked list calling release on stuff. (and on systems like iOS that don't have paging, lots of extra objects waiting to autorelease can cause memory low warnings, which might cause applications to be forced to exit, or the foreground application to go release a bunch of Nib views or something, then it then has to recreate later...or it might just force your application to exit).

Even if you don't use "enough" extra memory to cause low memory warnings or paging you will be running a larger staler list of items to drain. More memory accesses will be between your newest autorelease item and the oldest. There is a much greater chance that the oldest autorelease item is now farther away in the memory hierarchy, so your release may have cache misses vs. a L1 or L2 cache hit. So maybe 100 times more costly. Plus the memory you would have released (and might have been hot in the cache) might well have been reused by another object.

So doing the autorelease every 50 to 100 times might not even manage to be premature optimization.

Do one release per loop, and then if that shows as a bottleneck make it every X times, and make sure that makes it faster not slower.

Donothing answered 28/9, 2010 at 18:41 Comment(0)
C
0

The main thread's run loop drains its pool on each pass, so it makes sense to do it on other threads too. If you choose to drain the pool only occasionally, you risk having a lot of autoreleased objects waiting to be deallocated for a long time. In fact, it depends on how much memory you can release on each pass of the run loop and how often you trigger the run loop. I always prefer to drain it on each pass just because it's easy and helps me keep the memory footprint as low as possible.

Camouflage answered 27/9, 2010 at 20:11 Comment(0)
I
0

The conventional way is, yes to keep a counter and drain every 50 or so times, but as bbum said, just start out with draining the pool every loop, and go from there. OR you could -init the objects that you need, and not create any autoreleased objects. (just stick clear of factory methods) Remember to -release all your objects, though.

Indefensible answered 28/9, 2010 at 2:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.