views:

226

answers:

3

I have a tight loop that iterates about 500 times. In every iteration, it will create a few NSDecimalNumber objects to do some arithmetics.

Example - this code snippet is in the for loop. the -decimalNumberByAdding: method creates a new NSDecimalNumber instance and autoreleases it.

resultDecimalNumber = [resultDecimalNumber decimalNumberByAdding:anotherDecimalNumber];

So let me get that right: If the loop is huge, I collect thousands of NSDecimalNumber objects which wait for the whole loop to finish and the method to return, in order to get autoreleased after long time waiting.

How could I prevent a memory overflow? I've always tried to use non-autoreleased objects, but in this case it seems I have to live with them.

Imagine this was my loop:

for(i=0, i<500, i++) {
    resultDecimalNumber = [resultDecimalNumber decimalNumberByAdding:anotherDecimalNumber];
}

What would I have to add there? Must I create a autorelease pool inside the loop and drain it? Would that make sense?

+2  A: 

Yes. You should create your own autorelease pool inside the loop. This is exactly the situation that nested autorelease pools are for.

Rob Napier
Although Brad Larson provided a better solution for the problem itself, Rob Napier's answer was correct for the question itself, so I decided to check this one. To all: See Brad's post for a better solution regarding the usage of NSDecimalNumber.
HelloMoon
A: 

You can get the builtin floating-point equivalents for the NSDecimalNumbers you are using, then cast the result back into an NSDecimalNumber at the end of the loop:

double total(0);

for (i = 0; i < 500; ++i)
{
    total += [anotherDecimalNumber doubleValue];
}

resultDecimalNumber = // turn total back into an NSDecimalNumber

This technique will not only save you heaps of memory allocations inside the loop, but will also be significantly faster because you'll be using on-chip math operations instead of firing off functions.

fbrereto
This isn't desirable if you want to maintain precision and avoid floating point errors. You might as well not use NSDecimalNumber to begin with, and instead rely on a double value, if this is the approach you're going to take.
Brad Larson
Agreed. The only reason to break out NSDecimals in the first place is because double math isn't appropriate for your problem. If anything, one might go the other way in a few cases (do high-precision math with NSDecimals and return a double at the end), but I can't imagine using the above approach.
Rob Napier
+4  A: 

Use the C struct NSDecimal. From Apple's "Number and Value Programming Topics for Cocoa":

You might consider the C interface if you don’t need to treat decimal numbers as objects—that is, if you don’t need to store them in an object-oriented collection like an instance of NSArray or NSDictionary. You might also consider the C interface if you need maximum efficiency. The C interface is faster and uses less memory than the NSDecimalNumber class.

If you need mutability, you can combine the two interfaces. Use functions from the C interface and convert their results to instances of NSDecimalNumber.

I use NSDecimal, rather than NSDecimalNumber, on the iPhone for this very reason. You get all the precision of NSDecimalNumber, with a lot less overhead.

Benchmarking the two on my Mac (MacBook Air, 2nd gen), gives these results:

NSDecimal
Additions per second: 3355476.75
Subtractions per second: 3866671.27
Multiplications per second: 3458770.51
Divisions per second: 276242.32

NSDecimalNumber
Additions per second: 676901.32
Subtractions per second: 671474.6
Multiplications per second: 720310.63
Divisions per second: 190249.33

Even ignoring the memory usage, you get a near fivefold speedup in every operation but division if you use NSDecimal and the C APIs.

Brad Larson
Thanks mate. Gonna take a look on that.
HelloMoon
+1 Very nice benchmarks. I knew about the C interface but didn't know what kind of speed you'd see with it.
Rob Napier