Memory considerations for BFS in Objective-C - objective-c

I've written a puzzle solver in Objective-C. It uses a breadth first search to explore the states reachable from the initial puzzle state. The search is terminated when the first winning state is encountered. The only optimization is a look-up table that helps prevent re-exploring from a state already seen.
The algorithm seems to work correctly. However profiling shows it's using a lot of memory and I'd like to understand why. I think my gap in understanding is related to the Objective-C run loop and the autorelease pool.
Does the following (simplified) code ever allow the run loop to complete an iteration and drain the autorelease pool?
- (void) search {
while (![myQueue empty]) {
State *state = [myQueue pop];
for (State *s in [state allReachableStates]) {
[myQueue push:s];
}
}
}
Profiling shows lots of memory used for NSArrays. This makes sense as allReachableStates does create a fair number of arrays. Since they're all autoreleased it seems possible that the above code is preventing the autorelease pool from draining.
Note all code is run on the main thread and I'm not using ARC.
Edit: So the fix was wrapping the for loop in an #autoreleasepool.

You're right that the autorelease pool associated with this turn of the runloop won't be drained within this method. It won't be drained until some time after this method returns.
You can wrap the while block in an #autoreleasepool yourself (one for each state)

It's better not to use a lot of autorelease in a for loop. You can check this Understanding Objective-C autorelease memory management

Related

Usage of autorelease pool in objectAtindex swizzling for NSMutableArray

- (nullable id)myObjectAtIndex:(NSUInteger)index{
#autoreleasepool {
id value = nil;
if (index < self.count)
{
value = [self myObjectAtIndex:index];
}
return value;
}
}
I have no idea about the purpose of using autoreleasepool here. Can someone give me a hand?
Unless I'm missing the obvious, which is always possible, we can only guess:
There is a stack of autorelease pools, the top of the stack being the one in use. When the #autoreleasepool { ... } construct is entered a new pool is created and pushed on the stack, on exit from the construct the pool is drained and popped off the stack.
The reason to create local pools is given in the NSAutoReleasePool docs (emphasis added):
The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event. If you use the Application Kit, you therefore typically don’t have to create your own pools. If your application creates a lot of temporary autoreleased objects within the event loop, however, it may be beneficial to create “local” autorelease pools to help to minimize the peak memory footprint.
So what is the purpose in the code you are looking at? Some guesses:
Either the original author knows/believes that the called methods count and objectAtIndex (post the swizzle) add a significant amount of objects to the autorelease pool and wishes to clean these up; or
The original author was/is planning to add future code to myObjectAtIndex which will add a significant amount of objects to the autorelease pool and wishes to clean these up; or
Wishes to be able to call objectAtIndex and ensure there is no impact on the memory used for live objects (e.g. maybe they were measuring memory use by something else); or
Who knows, accept the original author (hopefully!)
HTH
There is no scientific reason.
The whole code is an example of "this app crashes and I do not know why" panic code:
Obviously the author had a problem with guaranteeing correct indexes, what would be the correct approach. Therefore he wrote a special method to "repair" it. The naming ("my") shows, that he thought: I can do it better (instead of insuring correct indexes).
Moreover, adding an ARP to a piece of code, that obviously does not create an bigger amount of objects, is a sure sign for the fact, that he couldn't oversee his code anymore.
Move the whole code to /dev/null.

Dealing with infinite loops in Objective C

I've recently joined an iPad project. In looking through the code base I've come across some unusual things, one of which is this (I've meta-coded this):
while (something.isAlwaysTrue) {
// Wait for something to happen. i.e. Get data from an internet connection.
// Respond to something.
}
I didn't notice any problems with this until I started memory profiling the app and found that these loops are memory leaking massively. The reason being that because they never end, any autorelease instances created inside them are never freed because the method never ends and the autorelease pools never get a chance to free up.
After talking it through with the developers who wrote the code I came up with the following technique:
-(void) queueTask {
// Using GCD or perform with delay, call the process method.
}
-(void) process {
// Wait and/or do stuff.
[self queueTask];
}
The basic idea is that by using a method to queuing through GCD or the runloop, it gives the autorelease pool a chance to execute and clean up autorelease instances. This appears to work just fine.
My question is - is this the best way to go about dealing with these loops? or is there a better way to do it?
Two points;
Minimizing Heap Growth
anyway, here's how minimize memory growth:
while (something.isAlwaysTrue) {
NSAutoreleasePool * pool = [NSAutoreleasePool new];
// Wait for something to happen. i.e. Get data from an internet connection.
// Respond to something.
[pool release], pool = 0;
}
or if you prefer the bleating (sic) edge:
while (something.isAlwaysTrue) {
#autoreleasepool{
// Wait for something to happen. i.e. Get data from an internet connection.
// Respond to something.
}
}
autorelease pools just operate like thread local stacks. when you push a pool, autoreleased objects are added to the top pool in the current thread. when you pop the pool, the pool sends a release message for each autorelease.
using GCD as a substitute for an autorelease pool is odd; similar to using an NSArray of single character NSStrings where you should simply use a single NSString.
Mutithreaded Program Flow
the infinite loop is a very suspicious program. it suggest you may be trying to reinvent run loops. the main run loop is of course common. a secondary thread 1) with a run loop 2) that never ends is unusual.
you should reconsider how the program flows. typically, you act on events, rather than holding your breath, polling until they complete. you may be trying to break from that situation in the program you proposed -- but i don't have enough detail.

Under ARC, is it still advisable to create an #autoreleasepool for loops?

Let's say that I have a loop that returns a bunch of autoreleased NSData objects...
NSData* bigData = ...
while(some condition) {
NSData* smallData = [bigData subdataWithRange:...];
//process smallData
}
Under ARC, should I still wrap an #autoreleasepool around the while condition?
NSData* bigData = ...
#autoreleasepool {
while(some condition) {
NSData* smallData = [bigData subdataWithRange:...];
//process smallData
}
}
The reason why I'm asking is I see the living allocation count in instruments going through the roof for my NSData objects that invoke a dataWith... method as opposed to an initWith... method. When I use initWith..., the living allocation count is much, much less.
Is it better to prefer the initWith... methods whenever possible?
Yes you should still use autorelease pools when using convenience methods in a tight loop. All the old memory management rules still apply under ARC, the compiler is merely injecting RRs for you. Checkout the great post by the awesome Mike Ash!
Link
I think your issue is that the autorelease pool is supposed to go inside the loop. With the loop inside the autorelease block rather than vice-versa, the accumulated objects won't be released until after the loop finishes.
Under ARC, should I still wrap an #autoreleasepool around the while condition?
Yes. Autorelease Pools are still in place, and grow and pop as before. The compiler just adds and coalesces the necessary retains and releases operations when ARC is enabled (echoing Logan), based on the methods that are visible to the TU and default naming conventions.
Execution in ARC is nearly identical the manual reference counting: Autorelease pool stacks still exist. One difference is that the compiler may order the reference counting operations slightly different from the way you wrote it (not in an incorrect way), and may omit unnecessary retain cycles.
Is it better to prefer the initWith... methods whenever possible?
WRT minimizing heap growth compared to the autoreleased counterparts: Yes. That's always been the case. It's especially important on iOS devices, where memory is quite limited.
The exception to this is when the object may avoid an allocation. Example:
NSString * copy = [NSString stringWithString:arg];
in this case, copy may be [[arg retain] autorelease]. Note that in this case, copy is still autoreleased, but you should not usually go to great lengths to test the presence of such optimizations. Note: It's also better to use copy = [arg copy]...[arg release] here.
The other bonus is that your ref count imbalances are often caught earlier when the object is never autoreleased, and closer to the call site (rather than when the Autorelease Pool is finally popped).
Performance with large autorelease pools is actually much worse than most people would suppose. If you can avoid depending on them heavily (e.g. using alloc+init...+release), you can make your program noticeably faster. Explicitly creating autorelease pools is cheap, and can help minimize this problem. When allocations are large and/or numerous, avoid using autorelease on them where possible, and do wrap these sections in explicit autorelease pools.

Why is this Objective-C code allocating GBs of RAM, releasing it later, and not reporting any leaks?

I have inherited some code, and it looks like this:
- (bool)makeOneLevel:(int)nummines objects:(int)numobjects
{
[field release];
state = gameWait;
field = [[MineField alloc] createLevel:nummines objects:numobjects];
if([field rating] == -1)
{
return false;
}
...
There is always one MineField allocated. Whenever you make a new field, the first thing the function does is release the old one. If the function succeeds in making a MineField, then it returns true.
I also have this:
while(numsaved < self.makeNumber)
{
while(![mineView makeOneLevel:self.makeNumMines objects:self.makeNumObjects])
{
}
{
//saving code here
}
numsaved++;
}
Which calls the function until it creates a valid MineField. This all works. But it allocates GBs of RAM while doing it. But the Leaks tool finds no leaks, and when the outer while finishes and the OS gets control back, all that RAM is deallocated just fine.
Am I doing something wrong with the MineField allocation, or should I be looking elsewhere in the creation process?
Without knowing the internals it's impossible to say for sure, but the behavior you're describing sounds like -[MineView makeOneLevel:objects:] is internally allocating and autoreleasing objects. Since the AppKit default event loop creates and cleans up an autorelease pool for each event it processes, everything does end up going away eventually, but not until the event is finished processing (e.g, after your method exits).
The easiest solution will be to wrap your own autorelease pool around the while() loop, and drain it either every time around the loop or periodically. If you aren't too scared of the internals of the method you're calling in the loop, though, you may be better off just finding where it's autoreleasing objects and fix it (by making it explicitly release objects when appropriate).
If you do not get any better answers, try using the heap profiler from Google perftools to track down where the huge allocations are happening.

Reducing the memory footprint of a function with a lot of autoreleased variables?

I'm still wrapping my head around some of the nuances of memory management in objective-C, and came up with the following case I'm unsure about:
+ (NSDecimalNumber*)factorial: (NSDecimalNumber *)l {
NSDecimalNumber *index = l;
NSDecimalNumber *running = [NSDecimalNumber one];
for (; [index intValue] > 1; index = [index decimalNumberBySubtracting:[NSDecimalNumber one]]) {
running = [running decimalNumberByMultiplyingBy: index];
}
return running;
}
Here decimalNumberByMultiplyingBy and decimalNumberBySubtracting will be creating a lot of NSDecimalNumbers, which will get autoreleased eventually as I understand it, but I worry until that time the containing program will be hanging unto an awful lot of memory.
Should I be introducing an autorelease pool somewhere? (If so where?) Is that going to have a noticeable effect on performance (when compared to the side effect of so much memory in use)?
Is autoreleasing the right mechanism to use here? Should I look at breaking the loop apart and manually releasing memory as I'm done with it?
It's likely a n00b question, but I'm trying to get a flavour for what the best practice(s) is/are in this situation.
It's good practice to avoid creating a bunch of large autoreleased objects within a single pass of the run loop. You already seem aware of the solutions. You can use non-autoreleased objects and release them when you're done with them. You can also create an autorelease pool for sections of your code that create a large number of autoreleased objects. When an object is autoreleased, it gets released when the enclosing autorelease pool is released/drained. Using an autorelease pool would look like this:
NSAutoReleasePool *subPool = [[NSAutoreleasePool alloc] init];
// Code that generates a bunch of autoreleased objects.
[subPool release];
But, before you do any optimization, run some benchmarks and see if you actually need to optimize. My guess is that the method you've shown won't cause any problems. However, let's say you're trying to apply your method to a set of a million random integers within a single loop. In that case, you might benefit from using an autorelease pool.
Check out Apple's Memory Management Programming Guide for Cocoa for more details.
You could set up an autorelease pool within the loop, but why bother?
You are not going to be able to accumulate that many objects in this loop, because you're computing factorials, and the largest exponent an NSDecimalNumber can have is 127.
You'll get an overflow error before you even get to 100 iterations through the loop.
Note that the main autorelease pool gets emptied every time the application makes a trip through the main run loop, so the autoreleased values are not going to hang around very long.
Best way to determine the answer is to write it a few different ways and test. I don't think this is going to be a problem, though, NSDecimalNumbers are going to max out around ~100!, and 100 NSDecimalNumber objects probably won't make a bit of difference.
Answer to your other questions: In situations where it will matter you can manually release your objects. You can also create an autorelease pool inside that loop. Autorelease is super fast.
while(/*condition*/) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// code goes here
[pool drain];
}